graphiti-core 0.7.6__py3-none-any.whl → 0.7.8__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
@@ -29,7 +29,7 @@ from graphiti_core.edges import EntityEdge, EpisodicEdge
29
29
  from graphiti_core.embedder import EmbedderClient, OpenAIEmbedder
30
30
  from graphiti_core.helpers import DEFAULT_DATABASE, semaphore_gather
31
31
  from graphiti_core.llm_client import LLMClient, OpenAIClient
32
- from graphiti_core.nodes import CommunityNode, EntityNode, EpisodeType, EpisodicNode
32
+ from graphiti_core.nodes import CommunityNode, EntityNode, EntityType, EpisodeType, EpisodicNode
33
33
  from graphiti_core.search.search import SearchConfig, search
34
34
  from graphiti_core.search.search_config import DEFAULT_SEARCH_LIMIT, SearchResults
35
35
  from graphiti_core.search.search_config_recipes import (
@@ -262,7 +262,7 @@ class Graphiti:
262
262
  group_id: str = '',
263
263
  uuid: str | None = None,
264
264
  update_communities: bool = False,
265
- entity_types: dict[str, BaseModel] | None = None,
265
+ entity_types: dict[str, EntityType] | None = None,
266
266
  ) -> AddEpisodeResults:
267
267
  """
268
268
  Process an episode and update the graph.
graphiti_core/nodes.py CHANGED
@@ -19,7 +19,7 @@ from abc import ABC, abstractmethod
19
19
  from datetime import datetime
20
20
  from enum import Enum
21
21
  from time import time
22
- from typing import Any
22
+ from typing import Any, ClassVar
23
23
  from uuid import uuid4
24
24
 
25
25
  from neo4j import AsyncDriver
@@ -39,6 +39,12 @@ from graphiti_core.utils.datetime_utils import utc_now
39
39
  logger = logging.getLogger(__name__)
40
40
 
41
41
 
42
+ class EntityType(BaseModel):
43
+ type_description: ClassVar[str] = Field(
44
+ default='', description='Description of what the entity type represents'
45
+ )
46
+
47
+
42
48
  class EpisodeType(Enum):
43
49
  """
44
50
  Enumeration of different types of episodes that can be processed.
@@ -64,7 +64,7 @@ def node(context: dict[str, Any]) -> list[Message]:
64
64
  {json.dumps(context['existing_nodes'], indent=2)}
65
65
  </EXISTING NODES>
66
66
 
67
- Given the above EXISTING NODES, MESSAGE, and PREVIOUS MESSAGES. Determine if the NEW NODE extracted from the conversation
67
+ Given the above EXISTING NODES and their attributes, MESSAGE, and PREVIOUS MESSAGES. Determine if the NEW NODE extracted from the conversation
68
68
  is a duplicate entity of one of the EXISTING NODES.
69
69
 
70
70
  <NEW NODE>
@@ -30,14 +30,17 @@ class MissedEntities(BaseModel):
30
30
  missed_entities: list[str] = Field(..., description="Names of entities that weren't extracted")
31
31
 
32
32
 
33
- class EntityClassification(BaseModel):
34
- entities: list[str] = Field(
35
- ...,
36
- description='List of entities',
33
+ class EntityClassificationTriple(BaseModel):
34
+ uuid: str = Field(description='UUID of the entity')
35
+ name: str = Field(description='Name of the entity')
36
+ entity_type: str | None = Field(
37
+ default=None, description='Type of the entity. Must be one of the provided types or None'
37
38
  )
38
- entity_classifications: list[str | None] = Field(
39
- ...,
40
- description='List of entities classifications. The index of the classification should match the index of the entity it corresponds to.',
39
+
40
+
41
+ class EntityClassification(BaseModel):
42
+ entity_classifications: list[EntityClassificationTriple] = Field(
43
+ ..., description='List of entities classification triples.'
41
44
  )
42
45
 
43
46
 
@@ -180,7 +183,7 @@ def classify_nodes(context: dict[str, Any]) -> list[Message]:
180
183
  {context['entity_types']}
181
184
  </ENTITY TYPES>
182
185
 
183
- Given the above conversation, extracted entities, and provided entity types, classify the extracted entities.
186
+ Given the above conversation, extracted entities, and provided entity types and their descriptions, classify the extracted entities.
184
187
 
185
188
  Guidelines:
186
189
  1. Each entity must have exactly one type
@@ -85,6 +85,8 @@ def summarize_context(context: dict[str, Any]) -> list[Message]:
85
85
  provided ENTITY. Summaries must be under 500 words.
86
86
 
87
87
  In addition, extract any values for the provided entity properties based on their descriptions.
88
+ If the value of the entity property cannot be found in the current context, set the value of the property to None.
89
+ Do not hallucinate entity property values if they cannot be found in the current context.
88
90
 
89
91
  <ENTITY>
90
92
  {context['node_name']}
@@ -55,7 +55,7 @@ def node_search_filter_query_constructor(
55
55
  filter_params: dict[str, Any] = {}
56
56
 
57
57
  if filters.node_labels is not None:
58
- node_labels = ':'.join(filters.node_labels)
58
+ node_labels = '|'.join(filters.node_labels)
59
59
  node_label_filter = ' AND n:' + node_labels
60
60
  filter_query += node_label_filter
61
61
 
@@ -18,14 +18,17 @@ import logging
18
18
  from time import time
19
19
 
20
20
  import pydantic
21
- from pydantic import BaseModel
22
21
 
23
22
  from graphiti_core.helpers import MAX_REFLEXION_ITERATIONS, semaphore_gather
24
23
  from graphiti_core.llm_client import LLMClient
25
- from graphiti_core.nodes import EntityNode, EpisodeType, EpisodicNode
24
+ from graphiti_core.nodes import EntityNode, EntityType, EpisodeType, EpisodicNode
26
25
  from graphiti_core.prompts import prompt_library
27
26
  from graphiti_core.prompts.dedupe_nodes import NodeDuplicate
28
- from graphiti_core.prompts.extract_nodes import EntityClassification, ExtractedNodes, MissedEntities
27
+ from graphiti_core.prompts.extract_nodes import (
28
+ EntityClassification,
29
+ ExtractedNodes,
30
+ MissedEntities,
31
+ )
29
32
  from graphiti_core.prompts.summarize_nodes import Summary
30
33
  from graphiti_core.utils.datetime_utils import utc_now
31
34
 
@@ -117,7 +120,7 @@ async def extract_nodes(
117
120
  llm_client: LLMClient,
118
121
  episode: EpisodicNode,
119
122
  previous_episodes: list[EpisodicNode],
120
- entity_types: dict[str, BaseModel] | None = None,
123
+ entity_types: dict[str, EntityType] | None = None,
121
124
  ) -> list[EntityNode]:
122
125
  start = time()
123
126
  extracted_node_names: list[str] = []
@@ -152,7 +155,11 @@ async def extract_nodes(
152
155
  'episode_content': episode.content,
153
156
  'previous_episodes': [ep.content for ep in previous_episodes],
154
157
  'extracted_entities': extracted_node_names,
155
- 'entity_types': entity_types.keys() if entity_types is not None else [],
158
+ 'entity_types': {
159
+ type_name: values.type_description for type_name, values in entity_types.items()
160
+ }
161
+ if entity_types is not None
162
+ else {},
156
163
  }
157
164
 
158
165
  node_classifications: dict[str, str | None] = {}
@@ -163,9 +170,13 @@ async def extract_nodes(
163
170
  prompt_library.extract_nodes.classify_nodes(node_classification_context),
164
171
  response_model=EntityClassification,
165
172
  )
166
- entities = llm_response.get('entities', [])
167
173
  entity_classifications = llm_response.get('entity_classifications', [])
168
- node_classifications.update(dict(zip(entities, entity_classifications)))
174
+ node_classifications.update(
175
+ {
176
+ entity_classification.get('name'): entity_classification.get('entity_type')
177
+ for entity_classification in entity_classifications
178
+ }
179
+ )
169
180
  # catch classification errors and continue if we can't classify
170
181
  except Exception as e:
171
182
  logger.exception(e)
@@ -251,7 +262,7 @@ async def resolve_extracted_nodes(
251
262
  existing_nodes_lists: list[list[EntityNode]],
252
263
  episode: EpisodicNode | None = None,
253
264
  previous_episodes: list[EpisodicNode] | None = None,
254
- entity_types: dict[str, BaseModel] | None = None,
265
+ entity_types: dict[str, EntityType] | None = None,
255
266
  ) -> tuple[list[EntityNode], dict[str, str]]:
256
267
  uuid_map: dict[str, str] = {}
257
268
  resolved_nodes: list[EntityNode] = []
@@ -284,12 +295,15 @@ async def resolve_extracted_node(
284
295
  existing_nodes: list[EntityNode],
285
296
  episode: EpisodicNode | None = None,
286
297
  previous_episodes: list[EpisodicNode] | None = None,
287
- entity_types: dict[str, BaseModel] | None = None,
298
+ entity_types: dict[str, EntityType] | None = None,
288
299
  ) -> tuple[EntityNode, dict[str, str]]:
289
300
  start = time()
290
301
 
291
302
  # Prepare context for LLM
292
- existing_nodes_context = [{'uuid': node.uuid, 'name': node.name} for node in existing_nodes]
303
+ existing_nodes_context = [
304
+ {'uuid': node.uuid, 'name': node.name, 'attributes': node.attributes}
305
+ for node in existing_nodes
306
+ ]
293
307
 
294
308
  extracted_node_context = {
295
309
  'uuid': extracted_node.uuid,
@@ -316,7 +330,7 @@ async def resolve_extracted_node(
316
330
  'attributes': [],
317
331
  }
318
332
 
319
- entity_type_classes: tuple[BaseModel, ...] = tuple()
333
+ entity_type_classes: tuple[EntityType, ...] = tuple()
320
334
  if entity_types is not None: # type: ignore
321
335
  entity_type_classes = entity_type_classes + tuple(
322
336
  filter(
@@ -366,6 +380,13 @@ async def resolve_extracted_node(
366
380
  node = existing_node
367
381
  node.name = name
368
382
  node.summary = summary_response.get('summary', '')
383
+
384
+ new_attributes = existing_node.attributes
385
+ existing_attributes = existing_node.attributes
386
+ for attribute_name, attribute_value in existing_attributes.items():
387
+ if new_attributes.get(attribute_name) is None:
388
+ new_attributes[attribute_name] = attribute_value
389
+
369
390
  uuid_map[extracted_node.uuid] = existing_node.uuid
370
391
 
371
392
  end = time()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: graphiti-core
3
- Version: 0.7.6
3
+ Version: 0.7.8
4
4
  Summary: A temporal graph building library
5
5
  License: Apache-2.0
6
6
  Author: Paul Paliychuk
@@ -9,7 +9,7 @@ graphiti_core/embedder/client.py,sha256=HKIlpPLnzFT81jurPkry6z8F8nxfZVfejdcfxHVU
9
9
  graphiti_core/embedder/openai.py,sha256=FzEM9rtSDK1wTb4iYKjNjjdFf8BEBTDxG2vM_E-5W-8,1621
10
10
  graphiti_core/embedder/voyage.py,sha256=7kqrLG75J3Q6cdA2Nlx1JSYtpk2141ckdl3OtDDw0vU,1882
11
11
  graphiti_core/errors.py,sha256=ddHrHGQxhwkVAtSph4AV84UoOlgwZufMczXPwB7uqPo,1795
12
- graphiti_core/graphiti.py,sha256=BfsR_JF89_bX0D9PJ2Q2IHQrnph9hd4I7-ayGvvZxpU,29231
12
+ graphiti_core/graphiti.py,sha256=HOpwLotVxfMCyPpem6ijrXeA2KlWBRMd70-dPxwjCCY,29244
13
13
  graphiti_core/helpers.py,sha256=z7ApOgrm_J7hk5FN_XPAwkKyopEY943BgHjDJbSXr2s,2869
14
14
  graphiti_core/llm_client/__init__.py,sha256=PA80TSMeX-sUXITXEAxMDEt3gtfZgcJrGJUcyds1mSo,207
15
15
  graphiti_core/llm_client/anthropic_client.py,sha256=RlD6e49XvMJsTKU0krpq46gPSFm6-hfLkkq4Sfx27BE,2574
@@ -25,25 +25,25 @@ graphiti_core/models/edges/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
25
25
  graphiti_core/models/edges/edge_db_queries.py,sha256=2UoLkmazO-FJYqjc3g0LuL-pyjekzQxxed_XHVv_HZE,2671
26
26
  graphiti_core/models/nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  graphiti_core/models/nodes/node_db_queries.py,sha256=f4_UT6XL8UDt4_CO9YIHeI8pvpw_vrutA9SYrgi6QCU,2121
28
- graphiti_core/nodes.py,sha256=0lH8SOpnzTtNIvG4ScnJ3SeQudviCsZwsnM867kY1aI,16998
28
+ graphiti_core/nodes.py,sha256=AEMk4YD8D0G-05nwzU8l4bMHz9oHcZROdsgSljtD9jM,17171
29
29
  graphiti_core/prompts/__init__.py,sha256=EA-x9xUki9l8wnu2l8ek_oNf75-do5tq5hVq7Zbv8Kw,101
30
30
  graphiti_core/prompts/dedupe_edges.py,sha256=EuX8ngeItBzrlMBOgeHrpExzxIFHD2aoDyaX1ZniF6I,3556
31
- graphiti_core/prompts/dedupe_nodes.py,sha256=mqvNATL-4Vo33vaxUEZfOq6hXXOiL-ftY0zcx2G-82I,4624
31
+ graphiti_core/prompts/dedupe_nodes.py,sha256=1MHYuqi-eXPnTQd_jEcVKfljSC_F_r5SdVRm_dpdOiI,4645
32
32
  graphiti_core/prompts/eval.py,sha256=csW494kKBMvWSm2SYLIRuGgNghhwNR3YwGn3veo3g_Y,3691
33
33
  graphiti_core/prompts/extract_edge_dates.py,sha256=td2yx2wnX-nLioMa0mtla3WcRyO71_wSjemT79YZGQ0,4096
34
34
  graphiti_core/prompts/extract_edges.py,sha256=vyEdW7JAPOT_eLWUi6nRmxbvucyVoyoYX2SxXfknRUg,3467
35
- graphiti_core/prompts/extract_nodes.py,sha256=6WwcdmB0FcRYASqNIcSBoqIgTgj_lhU-Vgy65r7oLao,6891
35
+ graphiti_core/prompts/extract_nodes.py,sha256=fSi07hGnlo7L5t9c0Q5zcBxDSki7QQKWhex9DFsVWW8,7048
36
36
  graphiti_core/prompts/invalidate_edges.py,sha256=DV2mEyIhhjc0hdKEMFLQMeG0FiUCkv_X0ctCliYjQ2c,3577
37
37
  graphiti_core/prompts/lib.py,sha256=oxhlpGEgV15VOLEZiwirxmIJBIdfzfiyL58iyzFDskE,4254
38
38
  graphiti_core/prompts/models.py,sha256=cvx_Bv5RMFUD_5IUawYrbpOKLPHogai7_bm7YXrSz84,867
39
39
  graphiti_core/prompts/prompt_helpers.py,sha256=-9TABwIcIQUVHcNANx6wIZd-FT2DgYKyGTfx4IGYq2I,64
40
- graphiti_core/prompts/summarize_nodes.py,sha256=ONDZdkvC7-RPaKx2geWSVjNaJAsHxRisV8tiU2ukw4k,3781
40
+ graphiti_core/prompts/summarize_nodes.py,sha256=GxEuA1luQMCdKYYc5zHug2y2aGZhvFn4Q51kkKGM4x4,4002
41
41
  graphiti_core/py.typed,sha256=vlmmzQOt7bmeQl9L3XJP4W6Ry0iiELepnOrinKz5KQg,79
42
42
  graphiti_core/search/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  graphiti_core/search/search.py,sha256=DX-tcIa0SiKI2HX-b_WdjGE74A8RLWQor4p90dJluUA,12643
44
44
  graphiti_core/search/search_config.py,sha256=UZN8jFA4pBlw2O5N1cuhVRBdTwMLR9N3Oyo6sQ4MDVw,3117
45
45
  graphiti_core/search/search_config_recipes.py,sha256=yUqiLnn9vFg39M8eVwjVKfBCL_ptGrfDMQ47m_Blb0g,6885
46
- graphiti_core/search/search_filters.py,sha256=4MJmCXD-blMc71xB4F9K4a72qidDCigADQ_ztdG15kc,5884
46
+ graphiti_core/search/search_filters.py,sha256=JkP7NbM4Dor27dne5vAuxbJic12dIJDtWJxNqmVuRec,5884
47
47
  graphiti_core/search/search_utils.py,sha256=Bywp7trqP_gEwqH8I-JHuj3Mljw9P2K6_XooXI75jHI,25739
48
48
  graphiti_core/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  graphiti_core/utils/bulk_utils.py,sha256=P4LKO46Yle4tBdNcQ3hDHcSQFaR8UBLfoL-z1M2Wua0,14690
@@ -52,10 +52,10 @@ graphiti_core/utils/maintenance/__init__.py,sha256=TRY3wWWu5kn3Oahk_KKhltrWnh0NA
52
52
  graphiti_core/utils/maintenance/community_operations.py,sha256=gIw1M5HGgc2c3TXag5ygPPpAv5WsG-yoC8Lhmfr6FMs,10011
53
53
  graphiti_core/utils/maintenance/edge_operations.py,sha256=tNw56vN586JYZMgie6RLRTiHZ680-kWzDIxW8ucL6SU,12780
54
54
  graphiti_core/utils/maintenance/graph_data_operations.py,sha256=qds9ALk9PhpQs1CNZTZGpi70mqJ93Y2KhIh9X2r8MUI,6533
55
- graphiti_core/utils/maintenance/node_operations.py,sha256=ZorGKps8sijOVS1V0Dv1oe3yWzq9s4u8stbe-4IY0vQ,14468
55
+ graphiti_core/utils/maintenance/node_operations.py,sha256=6FnA1zKWS1gU9RkmDAzr3witS8WF2H52AjyR3OpyxKw,15069
56
56
  graphiti_core/utils/maintenance/temporal_operations.py,sha256=RdNtubCyYhOVrvcOIq2WppHls1Q-BEjtsN8r38l-Rtc,3691
57
57
  graphiti_core/utils/maintenance/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
- graphiti_core-0.7.6.dist-info/LICENSE,sha256=KCUwCyDXuVEgmDWkozHyniRyWjnWUWjkuDHfU6o3JlA,11325
59
- graphiti_core-0.7.6.dist-info/METADATA,sha256=BnBzvu8YGhYy8aqvVvPH288bebp-kjUQf5YdoxbNSEY,10541
60
- graphiti_core-0.7.6.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
61
- graphiti_core-0.7.6.dist-info/RECORD,,
58
+ graphiti_core-0.7.8.dist-info/LICENSE,sha256=KCUwCyDXuVEgmDWkozHyniRyWjnWUWjkuDHfU6o3JlA,11325
59
+ graphiti_core-0.7.8.dist-info/METADATA,sha256=mSD97eHHyxTuOgqeqAfkZ0ymN0HGJ-dwWTzIRbksuGQ,10541
60
+ graphiti_core-0.7.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
61
+ graphiti_core-0.7.8.dist-info/RECORD,,