graphiti-core 0.7.6__tar.gz → 0.7.8__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.
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/PKG-INFO +1 -1
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/graphiti.py +2 -2
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/nodes.py +7 -1
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/dedupe_nodes.py +1 -1
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/extract_nodes.py +11 -8
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/summarize_nodes.py +2 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/search/search_filters.py +1 -1
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/node_operations.py +32 -11
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/pyproject.toml +1 -1
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/LICENSE +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/README.md +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/cross_encoder/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/cross_encoder/bge_reranker_client.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/cross_encoder/client.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/cross_encoder/openai_reranker_client.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/edges.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/embedder/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/embedder/client.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/embedder/openai.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/embedder/voyage.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/errors.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/helpers.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/anthropic_client.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/client.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/config.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/errors.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/groq_client.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/openai_client.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/openai_generic_client.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/utils.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/models/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/models/edges/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/models/edges/edge_db_queries.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/models/nodes/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/models/nodes/node_db_queries.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/dedupe_edges.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/eval.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/extract_edge_dates.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/extract_edges.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/invalidate_edges.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/lib.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/models.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/prompts/prompt_helpers.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/py.typed +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/search/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/search/search.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/search/search_config.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/search/search_config_recipes.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/search/search_utils.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/bulk_utils.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/datetime_utils.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/__init__.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/community_operations.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/edge_operations.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/graph_data_operations.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/temporal_operations.py +0 -0
- {graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/utils.py +0 -0
|
@@ -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,
|
|
265
|
+
entity_types: dict[str, EntityType] | None = None,
|
|
266
266
|
) -> AddEpisodeResults:
|
|
267
267
|
"""
|
|
268
268
|
Process an episode and update the graph.
|
|
@@ -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
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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 = '
|
|
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
|
|
{graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/node_operations.py
RENAMED
|
@@ -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
|
|
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,
|
|
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':
|
|
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(
|
|
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,
|
|
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,
|
|
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 = [
|
|
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[
|
|
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()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/cross_encoder/bge_reranker_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/cross_encoder/openai_reranker_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/llm_client/openai_generic_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/community_operations.py
RENAMED
|
File without changes
|
{graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/edge_operations.py
RENAMED
|
File without changes
|
{graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/graph_data_operations.py
RENAMED
|
File without changes
|
{graphiti_core-0.7.6 → graphiti_core-0.7.8}/graphiti_core/utils/maintenance/temporal_operations.py
RENAMED
|
File without changes
|
|
File without changes
|