graphiti-core 0.10.3__py3-none-any.whl → 0.10.4__py3-none-any.whl

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.

graphiti_core/graphiti.py CHANGED
@@ -797,7 +797,7 @@ class Graphiti:
797
797
  # We should only delete edges created by the episode
798
798
  edges_to_delete: list[EntityEdge] = []
799
799
  for edge in edges:
800
- if edge.episodes[0] == episode.uuid:
800
+ if edge.episodes and edge.episodes[0] == episode.uuid:
801
801
  edges_to_delete.append(edge)
802
802
 
803
803
  # Find nodes mentioned by the episode
graphiti_core/nodes.py CHANGED
@@ -38,6 +38,20 @@ from graphiti_core.utils.datetime_utils import utc_now
38
38
 
39
39
  logger = logging.getLogger(__name__)
40
40
 
41
+ ENTITY_NODE_RETURN: LiteralString = """
42
+ OPTIONAL MATCH (e:Episodic)-[r:MENTIONS]->(n)
43
+ WITH n, collect(e.uuid) AS episodes
44
+ RETURN
45
+ n.uuid As uuid,
46
+ n.name AS name,
47
+ n.name_embedding AS name_embedding,
48
+ n.group_id AS group_id,
49
+ n.created_at AS created_at,
50
+ n.summary AS summary,
51
+ labels(n) AS labels,
52
+ properties(n) AS attributes,
53
+ episodes"""
54
+
41
55
 
42
56
  class EpisodeType(Enum):
43
57
  """
@@ -280,6 +294,9 @@ class EpisodicNode(Node):
280
294
  class EntityNode(Node):
281
295
  name_embedding: list[float] | None = Field(default=None, description='embedding of the name')
282
296
  summary: str = Field(description='regional summary of surrounding edges', default_factory=str)
297
+ episodes: list[str] | None = Field(
298
+ default=None, description='List of episode uuids that mention this node.'
299
+ )
283
300
  attributes: dict[str, Any] = Field(
284
301
  default={}, description='Additional attributes of the node. Dependent on node labels'
285
302
  )
@@ -318,19 +335,14 @@ class EntityNode(Node):
318
335
 
319
336
  @classmethod
320
337
  async def get_by_uuid(cls, driver: AsyncDriver, uuid: str):
321
- records, _, _ = await driver.execute_query(
338
+ query = (
322
339
  """
323
- MATCH (n:Entity {uuid: $uuid})
324
- RETURN
325
- n.uuid As uuid,
326
- n.name AS name,
327
- n.name_embedding AS name_embedding,
328
- n.group_id AS group_id,
329
- n.created_at AS created_at,
330
- n.summary AS summary,
331
- labels(n) AS labels,
332
- properties(n) AS attributes
333
- """,
340
+ MATCH (n:Entity {uuid: $uuid})
341
+ """
342
+ + ENTITY_NODE_RETURN
343
+ )
344
+ records, _, _ = await driver.execute_query(
345
+ query,
334
346
  uuid=uuid,
335
347
  database_=DEFAULT_DATABASE,
336
348
  routing_='r',
@@ -348,16 +360,8 @@ class EntityNode(Node):
348
360
  records, _, _ = await driver.execute_query(
349
361
  """
350
362
  MATCH (n:Entity) WHERE n.uuid IN $uuids
351
- RETURN
352
- n.uuid As uuid,
353
- n.name AS name,
354
- n.name_embedding AS name_embedding,
355
- n.group_id AS group_id,
356
- n.created_at AS created_at,
357
- n.summary AS summary,
358
- labels(n) AS labels,
359
- properties(n) AS attributes
360
- """,
363
+ """
364
+ + ENTITY_NODE_RETURN,
361
365
  uuids=uuids,
362
366
  database_=DEFAULT_DATABASE,
363
367
  routing_='r',
@@ -383,16 +387,8 @@ class EntityNode(Node):
383
387
  MATCH (n:Entity) WHERE n.group_id IN $group_ids
384
388
  """
385
389
  + cursor_query
390
+ + ENTITY_NODE_RETURN
386
391
  + """
387
- RETURN
388
- n.uuid As uuid,
389
- n.name AS name,
390
- n.name_embedding AS name_embedding,
391
- n.group_id AS group_id,
392
- n.created_at AS created_at,
393
- n.summary AS summary,
394
- labels(n) AS labels,
395
- properties(n) AS attributes
396
392
  ORDER BY n.uuid DESC
397
393
  """
398
394
  + limit_query,
@@ -548,6 +544,7 @@ def get_entity_node_from_record(record: Any) -> EntityNode:
548
544
  created_at=record['created_at'].to_native(),
549
545
  summary=record['summary'],
550
546
  attributes=record['attributes'],
547
+ episodes=record['episodes'],
551
548
  )
552
549
 
553
550
  entity_node.attributes.pop('uuid', None)
@@ -82,6 +82,7 @@ def v1(context: dict[str, Any]) -> list[Message]:
82
82
  7. If only a date is mentioned without a specific time, use 00:00:00 (midnight) for that date.
83
83
  8. If only year is mentioned, use January 1st of that year at 00:00:00.
84
84
  9. Always include the time zone offset (use Z for UTC if no specific time zone is mentioned).
85
+ 10. A fact discussing that something is no longer true should have a valid_at according to when the negated fact became true.
85
86
  """,
86
87
  ),
87
88
  ]
@@ -32,6 +32,7 @@ from graphiti_core.helpers import (
32
32
  semaphore_gather,
33
33
  )
34
34
  from graphiti_core.nodes import (
35
+ ENTITY_NODE_RETURN,
35
36
  CommunityNode,
36
37
  EntityNode,
37
38
  EpisodicNode,
@@ -53,6 +54,20 @@ DEFAULT_MMR_LAMBDA = 0.5
53
54
  MAX_SEARCH_DEPTH = 3
54
55
  MAX_QUERY_LENGTH = 32
55
56
 
57
+ SEARCH_ENTITY_NODE_RETURN: LiteralString = """
58
+ OPTIONAL MATCH (e:Episodic)-[r:MENTIONS]->(n)
59
+ WITH n, score, collect(e.uuid) AS episodes
60
+ RETURN
61
+ n.uuid As uuid,
62
+ n.name AS name,
63
+ n.name_embedding AS name_embedding,
64
+ n.group_id AS group_id,
65
+ n.created_at AS created_at,
66
+ n.summary AS summary,
67
+ labels(n) AS labels,
68
+ properties(n) AS attributes,
69
+ episodes"""
70
+
56
71
 
57
72
  def fulltext_query(query: str, group_ids: list[str] | None = None):
58
73
  group_ids_filter_list = (
@@ -230,8 +245,8 @@ async def edge_similarity_search(
230
245
 
231
246
  query: LiteralString = (
232
247
  """
233
- MATCH (n:Entity)-[r:RELATES_TO]->(m:Entity)
234
- """
248
+ MATCH (n:Entity)-[r:RELATES_TO]->(m:Entity)
249
+ """
235
250
  + group_filter_query
236
251
  + filter_query
237
252
  + """\nWITH DISTINCT r, vector.similarity.cosine(r.fact_embedding, $search_vector) AS score
@@ -341,27 +356,21 @@ async def node_fulltext_search(
341
356
 
342
357
  filter_query, filter_params = node_search_filter_query_constructor(search_filter)
343
358
 
344
- records, _, _ = await driver.execute_query(
345
- """
346
- CALL db.index.fulltext.queryNodes("node_name_and_summary", $query, {limit: $limit})
347
- YIELD node AS node, score
348
- MATCH (n:Entity)
349
- WHERE n.uuid = node.uuid
359
+ query = (
350
360
  """
361
+ CALL db.index.fulltext.queryNodes("node_name_and_summary", $query, {limit: $limit})
362
+ YIELD node AS n, score
363
+ WHERE n:Entity
364
+ """
351
365
  + filter_query
366
+ + SEARCH_ENTITY_NODE_RETURN
352
367
  + """
353
- RETURN
354
- n.uuid AS uuid,
355
- n.group_id AS group_id,
356
- n.name AS name,
357
- n.name_embedding AS name_embedding,
358
- n.created_at AS created_at,
359
- n.summary AS summary,
360
- labels(n) AS labels,
361
- properties(n) AS attributes
362
368
  ORDER BY score DESC
363
- LIMIT $limit
364
- """,
369
+ """
370
+ )
371
+
372
+ records, _, _ = await driver.execute_query(
373
+ query,
365
374
  filter_params,
366
375
  query=fuzzy_query,
367
376
  group_ids=group_ids,
@@ -406,19 +415,12 @@ async def node_similarity_search(
406
415
  + filter_query
407
416
  + """
408
417
  WITH n, vector.similarity.cosine(n.name_embedding, $search_vector) AS score
409
- WHERE score > $min_score
410
- RETURN
411
- n.uuid As uuid,
412
- n.group_id AS group_id,
413
- n.name AS name,
414
- n.name_embedding AS name_embedding,
415
- n.created_at AS created_at,
416
- n.summary AS summary,
417
- labels(n) AS labels,
418
- properties(n) AS attributes
419
- ORDER BY score DESC
420
- LIMIT $limit
421
- """,
418
+ WHERE score > $min_score"""
419
+ + SEARCH_ENTITY_NODE_RETURN
420
+ + """
421
+ ORDER BY score DESC
422
+ LIMIT $limit
423
+ """,
422
424
  query_params,
423
425
  search_vector=search_vector,
424
426
  group_ids=group_ids,
@@ -452,16 +454,8 @@ async def node_bfs_search(
452
454
  WHERE n.group_id = origin.group_id
453
455
  """
454
456
  + filter_query
457
+ + ENTITY_NODE_RETURN
455
458
  + """
456
- RETURN DISTINCT
457
- n.uuid As uuid,
458
- n.group_id AS group_id,
459
- n.name AS name,
460
- n.name_embedding AS name_embedding,
461
- n.created_at AS created_at,
462
- n.summary AS summary,
463
- labels(n) AS labels,
464
- properties(n) AS attributes
465
459
  LIMIT $limit
466
460
  """,
467
461
  filter_params,
@@ -26,7 +26,7 @@ from pydantic import BaseModel
26
26
  from typing_extensions import Any
27
27
 
28
28
  from graphiti_core.edges import Edge, EntityEdge, EpisodicEdge
29
- from graphiti_core.helpers import semaphore_gather
29
+ from graphiti_core.helpers import DEFAULT_DATABASE, semaphore_gather
30
30
  from graphiti_core.llm_client import LLMClient
31
31
  from graphiti_core.models.edges.edge_db_queries import (
32
32
  ENTITY_EDGE_SAVE_BULK,
@@ -95,7 +95,7 @@ async def add_nodes_and_edges_bulk(
95
95
  entity_nodes: list[EntityNode],
96
96
  entity_edges: list[EntityEdge],
97
97
  ):
98
- async with driver.session() as session:
98
+ async with driver.session(database=DEFAULT_DATABASE) as session:
99
99
  await session.execute_write(
100
100
  add_nodes_and_edges_bulk_tx, episodic_nodes, episodic_edges, entity_nodes, entity_edges
101
101
  )
@@ -101,9 +101,11 @@ def label_propagation(projection: dict[str, list[Neighbor]]) -> list[list[str]]:
101
101
  ]
102
102
 
103
103
  community_lst.sort(reverse=True)
104
- community_candidate = community_lst[0][1] if len(community_lst) > 0 else -1
105
-
106
- new_community = max(community_candidate, curr_community)
104
+ candidate_rank, community_candidate = community_lst[0] if community_lst else (0, -1)
105
+ if community_candidate != -1 and candidate_rank > 1:
106
+ new_community = community_candidate
107
+ else:
108
+ new_community = max(community_candidate, curr_community)
107
109
 
108
110
  new_community_map[uuid] = new_community
109
111
 
@@ -42,7 +42,7 @@ async def build_indices_and_constraints(driver: AsyncDriver, delete_existing: bo
42
42
  driver.execute_query(
43
43
  """DROP INDEX $name""",
44
44
  name=name,
45
- _database=DEFAULT_DATABASE,
45
+ database_=DEFAULT_DATABASE,
46
46
  )
47
47
  for name in index_names
48
48
  ]
@@ -87,7 +87,7 @@ async def build_indices_and_constraints(driver: AsyncDriver, delete_existing: bo
87
87
  *[
88
88
  driver.execute_query(
89
89
  query,
90
- _database=DEFAULT_DATABASE,
90
+ database_=DEFAULT_DATABASE,
91
91
  )
92
92
  for query in index_queries
93
93
  ]
@@ -95,7 +95,7 @@ async def build_indices_and_constraints(driver: AsyncDriver, delete_existing: bo
95
95
 
96
96
 
97
97
  async def clear_data(driver: AsyncDriver, group_ids: list[str] | None = None):
98
- async with driver.session() as session:
98
+ async with driver.session(database=DEFAULT_DATABASE) as session:
99
99
 
100
100
  async def delete_all(tx):
101
101
  await tx.run('MATCH (n) DETACH DELETE n')
@@ -150,7 +150,7 @@ async def retrieve_episodes(
150
150
  reference_time=reference_time,
151
151
  num_episodes=last_n,
152
152
  group_ids=group_ids,
153
- _database=DEFAULT_DATABASE,
153
+ database_=DEFAULT_DATABASE,
154
154
  )
155
155
  episodes = [
156
156
  EpisodicNode(
@@ -306,7 +306,7 @@ async def resolve_extracted_node(
306
306
 
307
307
  # Prepare context for LLM
308
308
  existing_nodes_context = [
309
- {'uuid': node.uuid, 'name': node.name, 'attributes': node.attributes}
309
+ {**{'uuid': node.uuid, 'name': node.name, 'summary': node.summary}, **node.attributes}
310
310
  for node in existing_nodes
311
311
  ]
312
312
 
@@ -408,6 +408,7 @@ async def resolve_extracted_node(
408
408
  if new_attributes.get(attribute_name) is None:
409
409
  new_attributes[attribute_name] = attribute_value
410
410
  node.attributes = new_attributes
411
+ node.labels = list(set(existing_node.labels + extracted_node.labels))
411
412
 
412
413
  uuid_map[extracted_node.uuid] = existing_node.uuid
413
414
 
@@ -432,7 +433,8 @@ async def dedupe_node_list(
432
433
 
433
434
  # Prepare context for LLM
434
435
  nodes_context = [
435
- {'uuid': node.uuid, 'name': node.name, 'summary': node.summary} for node in nodes
436
+ {'uuid': node.uuid, 'name': node.name, 'summary': node.summary}.update(node.attributes)
437
+ for node in nodes
436
438
  ]
437
439
 
438
440
  context = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: graphiti-core
3
- Version: 0.10.3
3
+ Version: 0.10.4
4
4
  Summary: A temporal graph building library
5
5
  License: Apache-2.0
6
6
  Author: Paul Paliychuk
@@ -10,7 +10,7 @@ graphiti_core/embedder/gemini.py,sha256=nE0XH8wYVGcPSO7DaNQ7kdsQLFSoH4FQOu2HMQUy
10
10
  graphiti_core/embedder/openai.py,sha256=fcU63koSRI-OjDuEcBfUKgXu8XV_-8EF6HpVrYa1_8I,1880
11
11
  graphiti_core/embedder/voyage.py,sha256=DZsH1nSTfP1vqCinNIIwSyEzv7jsyur2tKxlBv-ZZ_E,1902
12
12
  graphiti_core/errors.py,sha256=Nib1uQx2cO_VOizupmRjpFfmuRg-hFAVqTtZAuBehR8,2405
13
- graphiti_core/graphiti.py,sha256=mdIkciD4o3fjD5PapshKvNv0F6eE76rXZCAB00vcDG8,30807
13
+ graphiti_core/graphiti.py,sha256=6YcBSpUSpcQ_DJ4mWaFtmaTuB988U029vhTUz6axliU,30825
14
14
  graphiti_core/helpers.py,sha256=7BQzUBFmoBDA2OIDdFtoN4W-vXOhPRIsF0uDb7PsNi0,2913
15
15
  graphiti_core/llm_client/__init__.py,sha256=PA80TSMeX-sUXITXEAxMDEt3gtfZgcJrGJUcyds1mSo,207
16
16
  graphiti_core/llm_client/anthropic_client.py,sha256=qVLtRdlYksdl221lIBv7saOmuJtgG5p3af0HUsVxseM,12926
@@ -27,12 +27,12 @@ graphiti_core/models/edges/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
27
27
  graphiti_core/models/edges/edge_db_queries.py,sha256=2UoLkmazO-FJYqjc3g0LuL-pyjekzQxxed_XHVv_HZE,2671
28
28
  graphiti_core/models/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  graphiti_core/models/nodes/node_db_queries.py,sha256=AQgRGVO-GgFWfLq1G6k8s86WItwpXruy3Mj4DBli-vM,2145
30
- graphiti_core/nodes.py,sha256=E8f-Ch-atWS8OF97Ozc3XCPwe0ug7mkoGwBL_zNoXT0,17830
30
+ graphiti_core/nodes.py,sha256=gwrxygjBaC29KfEHkeEMDXWUclc1nPnZxnGYTHfCRTs,17712
31
31
  graphiti_core/prompts/__init__.py,sha256=EA-x9xUki9l8wnu2l8ek_oNf75-do5tq5hVq7Zbv8Kw,101
32
32
  graphiti_core/prompts/dedupe_edges.py,sha256=GrLKEHPrEsjK31wQf7AnMJDXaVCrCuJWaNlVAxEd4ks,3543
33
33
  graphiti_core/prompts/dedupe_nodes.py,sha256=VXSb3chBo4l7qzzWxT-sNHJhjY3jNtUBAndnALUcqws,4632
34
34
  graphiti_core/prompts/eval.py,sha256=gnBQTmwsCl3Qvwpcm7aieVszzo6y1sMCUT8jQiKTvvE,5317
35
- graphiti_core/prompts/extract_edge_dates.py,sha256=wBM80uGIpUuDQs-5nwSieu3JcEg8PzKVrAFx08qUH80,4080
35
+ graphiti_core/prompts/extract_edge_dates.py,sha256=3Drs3CmvP0gJN5BidWSxrNvLet3HPoTybU3BUIAoc0Y,4218
36
36
  graphiti_core/prompts/extract_edges.py,sha256=6-MkMkQp4QSpbdZ-gB3PkMcs7q3E07dAWYM5oY1FZdQ,3467
37
37
  graphiti_core/prompts/extract_nodes.py,sha256=I8CO8D7rMaKQv2biBxToYrAMD1RNCe1RMG9tGNv1Tm4,7048
38
38
  graphiti_core/prompts/invalidate_edges.py,sha256=DV2mEyIhhjc0hdKEMFLQMeG0FiUCkv_X0ctCliYjQ2c,3577
@@ -47,19 +47,19 @@ graphiti_core/search/search_config.py,sha256=VvKg6AB_RPhoe56DBBXHRBXHThAVJ_OLFCy
47
47
  graphiti_core/search/search_config_recipes.py,sha256=4GquRphHhJlpXQhAZOySYnCzBWYoTwxlJj44eTOavZQ,7443
48
48
  graphiti_core/search/search_filters.py,sha256=JkP7NbM4Dor27dne5vAuxbJic12dIJDtWJxNqmVuRec,5884
49
49
  graphiti_core/search/search_helpers.py,sha256=G5Ceaq5Pfgx0Weelqgeylp_pUHwiBnINaUYsDbURJbE,2636
50
- graphiti_core/search/search_utils.py,sha256=rravGcYaWzAYMyeNw8jUztpND4jr7k9M_1H06KJiig4,27370
50
+ graphiti_core/search/search_utils.py,sha256=MkuY9Pn1Xl_PJXUMiiBe2eEuchIqJTn6K4auJh-bj9Q,27072
51
51
  graphiti_core/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
- graphiti_core/utils/bulk_utils.py,sha256=P4LKO46Yle4tBdNcQ3hDHcSQFaR8UBLfoL-z1M2Wua0,14690
52
+ graphiti_core/utils/bulk_utils.py,sha256=mX7ftT-vbt4ac_BODPyWaSwDCSSCiF0RNu4YRLqF6N0,14733
53
53
  graphiti_core/utils/datetime_utils.py,sha256=Ti-2tnrDFRzBsbfblzsHybsM3jaDLP4-VT2t0VhpIzU,1357
54
54
  graphiti_core/utils/maintenance/__init__.py,sha256=vW4H1KyapTl-OOz578uZABYcpND4wPx3Vt6aAPaXh78,301
55
- graphiti_core/utils/maintenance/community_operations.py,sha256=pQUv0pfInC1Pho7C4BN8gC3_bks7wRAZpJn2bmw6gT8,10008
55
+ graphiti_core/utils/maintenance/community_operations.py,sha256=aLMfQ5l8PkNJY3ugqV6UOMG74aI6L9Zz2zFiLBsnKvM,10155
56
56
  graphiti_core/utils/maintenance/edge_operations.py,sha256=9i0PBgaW3dLPTLmx-9j1W86Rb4sPc1bG4Y3TjPn07Gg,12794
57
- graphiti_core/utils/maintenance/graph_data_operations.py,sha256=F3dkXg63nJU7OFLzaiT67a2_kFiWSU9Vr59iIHgsXQs,7030
58
- graphiti_core/utils/maintenance/node_operations.py,sha256=7TLBBJ5m-3_UK53_UNm0hQJh3iiJP0tIVG7Sd6oOioE,15801
57
+ graphiti_core/utils/maintenance/graph_data_operations.py,sha256=3JmVIvX19ZdHvOy-gPUBy1A8vBHTv9cH0DESgnPhr50,7055
58
+ graphiti_core/utils/maintenance/node_operations.py,sha256=_MZ0ZcwZsU6id7SUalMgmrytpdn3aUQwOM12dP9OqmE,15932
59
59
  graphiti_core/utils/maintenance/temporal_operations.py,sha256=RdNtubCyYhOVrvcOIq2WppHls1Q-BEjtsN8r38l-Rtc,3691
60
60
  graphiti_core/utils/maintenance/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
61
  graphiti_core/utils/ontology_utils/entity_types_utils.py,sha256=QJX5cG0GSSNF_Mm_yrldr69wjVAbN_MxLhOSznz85Hk,1279
62
- graphiti_core-0.10.3.dist-info/LICENSE,sha256=KCUwCyDXuVEgmDWkozHyniRyWjnWUWjkuDHfU6o3JlA,11325
63
- graphiti_core-0.10.3.dist-info/METADATA,sha256=3tU3LJE6tj6tNi8WvIe32iKCA3QjFjzH7BPSELnu70Y,14659
64
- graphiti_core-0.10.3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
65
- graphiti_core-0.10.3.dist-info/RECORD,,
62
+ graphiti_core-0.10.4.dist-info/LICENSE,sha256=KCUwCyDXuVEgmDWkozHyniRyWjnWUWjkuDHfU6o3JlA,11325
63
+ graphiti_core-0.10.4.dist-info/METADATA,sha256=Da7oA1ru5YgGpYutZY0XQZcICgc_DIMT6bLZzJp9ylk,14659
64
+ graphiti_core-0.10.4.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
65
+ graphiti_core-0.10.4.dist-info/RECORD,,