graphiti-core 0.18.9__py3-none-any.whl → 0.19.0__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.

Files changed (29) hide show
  1. graphiti_core/driver/driver.py +4 -0
  2. graphiti_core/driver/falkordb_driver.py +3 -14
  3. graphiti_core/driver/kuzu_driver.py +175 -0
  4. graphiti_core/driver/neptune_driver.py +301 -0
  5. graphiti_core/edges.py +155 -62
  6. graphiti_core/graph_queries.py +31 -2
  7. graphiti_core/graphiti.py +6 -1
  8. graphiti_core/helpers.py +8 -8
  9. graphiti_core/llm_client/config.py +1 -1
  10. graphiti_core/llm_client/openai_base_client.py +12 -2
  11. graphiti_core/llm_client/openai_client.py +10 -2
  12. graphiti_core/migrations/__init__.py +0 -0
  13. graphiti_core/migrations/neo4j_node_group_labels.py +114 -0
  14. graphiti_core/models/edges/edge_db_queries.py +205 -76
  15. graphiti_core/models/nodes/node_db_queries.py +253 -74
  16. graphiti_core/nodes.py +271 -98
  17. graphiti_core/search/search.py +42 -12
  18. graphiti_core/search/search_config.py +4 -0
  19. graphiti_core/search/search_filters.py +35 -22
  20. graphiti_core/search/search_utils.py +1329 -392
  21. graphiti_core/utils/bulk_utils.py +50 -15
  22. graphiti_core/utils/datetime_utils.py +13 -0
  23. graphiti_core/utils/maintenance/community_operations.py +39 -32
  24. graphiti_core/utils/maintenance/edge_operations.py +47 -13
  25. graphiti_core/utils/maintenance/graph_data_operations.py +100 -15
  26. {graphiti_core-0.18.9.dist-info → graphiti_core-0.19.0.dist-info}/METADATA +87 -13
  27. {graphiti_core-0.18.9.dist-info → graphiti_core-0.19.0.dist-info}/RECORD +29 -25
  28. {graphiti_core-0.18.9.dist-info → graphiti_core-0.19.0.dist-info}/WHEEL +0 -0
  29. {graphiti_core-0.18.9.dist-info → graphiti_core-0.19.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,114 @@
1
+ import asyncio
2
+ import csv
3
+ import os
4
+
5
+ from graphiti_core.driver.driver import GraphDriver
6
+ from graphiti_core.driver.neo4j_driver import Neo4jDriver
7
+ from graphiti_core.helpers import validate_group_id
8
+ from graphiti_core.utils.maintenance.graph_data_operations import build_dynamic_indexes
9
+
10
+
11
+ async def neo4j_node_group_labels(driver: GraphDriver, group_id: str, batch_size: int = 100):
12
+ validate_group_id(group_id)
13
+ await build_dynamic_indexes(driver, group_id)
14
+
15
+ episode_query = """
16
+ MATCH (n:Episodic {group_id: $group_id})
17
+ CALL {
18
+ WITH n
19
+ SET n:$($group_label)
20
+ } IN TRANSACTIONS OF $batch_size ROWS"""
21
+
22
+ entity_query = """
23
+ MATCH (n:Entity {group_id: $group_id})
24
+ CALL {
25
+ WITH n
26
+ SET n:$($group_label)
27
+ } IN TRANSACTIONS OF $batch_size ROWS"""
28
+
29
+ community_query = """
30
+ MATCH (n:Community {group_id: $group_id})
31
+ CALL {
32
+ WITH n
33
+ SET n:$($group_label)
34
+ } IN TRANSACTIONS OF $batch_size ROWS"""
35
+
36
+ async with driver.session() as session:
37
+ await session.run(
38
+ episode_query,
39
+ group_id=group_id,
40
+ group_label='Episodic_' + group_id.replace('-', ''),
41
+ batch_size=batch_size,
42
+ )
43
+
44
+ async with driver.session() as session:
45
+ await session.run(
46
+ entity_query,
47
+ group_id=group_id,
48
+ group_label='Entity_' + group_id.replace('-', ''),
49
+ batch_size=batch_size,
50
+ )
51
+
52
+ async with driver.session() as session:
53
+ await session.run(
54
+ community_query,
55
+ group_id=group_id,
56
+ group_label='Community_' + group_id.replace('-', ''),
57
+ batch_size=batch_size,
58
+ )
59
+
60
+
61
+ def pop_last_n_group_ids(csv_file: str = 'group_ids.csv', count: int = 10):
62
+ with open(csv_file) as file:
63
+ reader = csv.reader(file)
64
+ group_ids = [row[0] for row in reader]
65
+
66
+ total_count = len(group_ids)
67
+ popped = group_ids[-count:]
68
+ remaining = group_ids[:-count]
69
+
70
+ with open(csv_file, 'w', newline='') as file:
71
+ writer = csv.writer(file)
72
+ for gid in remaining:
73
+ writer.writerow([gid])
74
+
75
+ return popped, total_count
76
+
77
+
78
+ async def get_group_ids(driver: GraphDriver):
79
+ query = """MATCH (n:Episodic)
80
+ RETURN DISTINCT n.group_id AS group_id"""
81
+
82
+ results, _, _ = await driver.execute_query(query)
83
+ group_ids = [result['group_id'] for result in results]
84
+
85
+ with open('group_ids.csv', 'w', newline='') as file:
86
+ writer = csv.writer(file)
87
+ for gid in group_ids:
88
+ writer.writerow([gid])
89
+
90
+
91
+ async def neo4j_node_label_migration(driver: GraphDriver, batch_size: int = 10):
92
+ group_ids, total = pop_last_n_group_ids(csv_file='group_ids.csv', count=batch_size)
93
+ while len(group_ids) > 0:
94
+ await asyncio.gather(*[neo4j_node_group_labels(driver, group_id) for group_id in group_ids])
95
+ group_ids, _ = pop_last_n_group_ids(csv_file='group_ids.csv', count=batch_size)
96
+
97
+
98
+ async def main():
99
+ neo4j_uri = os.environ.get('NEO4J_URI') or 'bolt://localhost:7687'
100
+ neo4j_user = os.environ.get('NEO4J_USER') or 'neo4j'
101
+ neo4j_password = os.environ.get('NEO4J_PASSWORD') or 'password'
102
+
103
+ driver = Neo4jDriver(
104
+ uri=neo4j_uri,
105
+ user=neo4j_user,
106
+ password=neo4j_password,
107
+ )
108
+ await get_group_ids(driver)
109
+ await neo4j_node_label_migration(driver)
110
+ await driver.close()
111
+
112
+
113
+ if __name__ == '__main__':
114
+ asyncio.run(main())
@@ -20,18 +20,36 @@ EPISODIC_EDGE_SAVE = """
20
20
  MATCH (episode:Episodic {uuid: $episode_uuid})
21
21
  MATCH (node:Entity {uuid: $entity_uuid})
22
22
  MERGE (episode)-[e:MENTIONS {uuid: $uuid}]->(node)
23
- SET e = {uuid: $uuid, group_id: $group_id, created_at: $created_at}
23
+ SET
24
+ e.group_id = $group_id,
25
+ e.created_at = $created_at
24
26
  RETURN e.uuid AS uuid
25
27
  """
26
28
 
27
- EPISODIC_EDGE_SAVE_BULK = """
28
- UNWIND $episodic_edges AS edge
29
- MATCH (episode:Episodic {uuid: edge.source_node_uuid})
30
- MATCH (node:Entity {uuid: edge.target_node_uuid})
31
- MERGE (episode)-[e:MENTIONS {uuid: edge.uuid}]->(node)
32
- SET e = {uuid: edge.uuid, group_id: edge.group_id, created_at: edge.created_at}
33
- RETURN e.uuid AS uuid
34
- """
29
+
30
+ def get_episodic_edge_save_bulk_query(provider: GraphProvider) -> str:
31
+ if provider == GraphProvider.KUZU:
32
+ return """
33
+ MATCH (episode:Episodic {uuid: $source_node_uuid})
34
+ MATCH (node:Entity {uuid: $target_node_uuid})
35
+ MERGE (episode)-[e:MENTIONS {uuid: $uuid}]->(node)
36
+ SET
37
+ e.group_id = $group_id,
38
+ e.created_at = $created_at
39
+ RETURN e.uuid AS uuid
40
+ """
41
+
42
+ return """
43
+ UNWIND $episodic_edges AS edge
44
+ MATCH (episode:Episodic {uuid: edge.source_node_uuid})
45
+ MATCH (node:Entity {uuid: edge.target_node_uuid})
46
+ MERGE (episode)-[e:MENTIONS {uuid: edge.uuid}]->(node)
47
+ SET
48
+ e.group_id = edge.group_id,
49
+ e.created_at = edge.created_at
50
+ RETURN e.uuid AS uuid
51
+ """
52
+
35
53
 
36
54
  EPISODIC_EDGE_RETURN = """
37
55
  e.uuid AS uuid,
@@ -43,82 +61,193 @@ EPISODIC_EDGE_RETURN = """
43
61
 
44
62
 
45
63
  def get_entity_edge_save_query(provider: GraphProvider) -> str:
46
- if provider == GraphProvider.FALKORDB:
47
- return """
48
- MATCH (source:Entity {uuid: $edge_data.source_uuid})
49
- MATCH (target:Entity {uuid: $edge_data.target_uuid})
50
- MERGE (source)-[e:RELATES_TO {uuid: $edge_data.uuid}]->(target)
51
- SET e = $edge_data
52
- RETURN e.uuid AS uuid
53
- """
54
-
55
- return """
56
- MATCH (source:Entity {uuid: $edge_data.source_uuid})
57
- MATCH (target:Entity {uuid: $edge_data.target_uuid})
58
- MERGE (source)-[e:RELATES_TO {uuid: $edge_data.uuid}]->(target)
59
- SET e = $edge_data
60
- WITH e CALL db.create.setRelationshipVectorProperty(e, "fact_embedding", $edge_data.fact_embedding)
61
- RETURN e.uuid AS uuid
62
- """
64
+ match provider:
65
+ case GraphProvider.FALKORDB:
66
+ return """
67
+ MATCH (source:Entity {uuid: $edge_data.source_uuid})
68
+ MATCH (target:Entity {uuid: $edge_data.target_uuid})
69
+ MERGE (source)-[e:RELATES_TO {uuid: $edge_data.uuid}]->(target)
70
+ SET e = $edge_data
71
+ RETURN e.uuid AS uuid
72
+ """
73
+ case GraphProvider.NEPTUNE:
74
+ return """
75
+ MATCH (source:Entity {uuid: $edge_data.source_uuid})
76
+ MATCH (target:Entity {uuid: $edge_data.target_uuid})
77
+ MERGE (source)-[e:RELATES_TO {uuid: $edge_data.uuid}]->(target)
78
+ SET e = removeKeyFromMap(removeKeyFromMap($edge_data, "fact_embedding"), "episodes")
79
+ SET e.fact_embedding = join([x IN coalesce($edge_data.fact_embedding, []) | toString(x) ], ",")
80
+ SET e.episodes = join($edge_data.episodes, ",")
81
+ RETURN $edge_data.uuid AS uuid
82
+ """
83
+ case GraphProvider.KUZU:
84
+ return """
85
+ MATCH (source:Entity {uuid: $source_uuid})
86
+ MATCH (target:Entity {uuid: $target_uuid})
87
+ MERGE (source)-[:RELATES_TO]->(e:RelatesToNode_ {uuid: $uuid})-[:RELATES_TO]->(target)
88
+ SET
89
+ e.group_id = $group_id,
90
+ e.created_at = $created_at,
91
+ e.name = $name,
92
+ e.fact = $fact,
93
+ e.fact_embedding = $fact_embedding,
94
+ e.episodes = $episodes,
95
+ e.expired_at = $expired_at,
96
+ e.valid_at = $valid_at,
97
+ e.invalid_at = $invalid_at,
98
+ e.attributes = $attributes
99
+ RETURN e.uuid AS uuid
100
+ """
101
+ case _: # Neo4j
102
+ return """
103
+ MATCH (source:Entity {uuid: $edge_data.source_uuid})
104
+ MATCH (target:Entity {uuid: $edge_data.target_uuid})
105
+ MERGE (source)-[e:RELATES_TO {uuid: $edge_data.uuid}]->(target)
106
+ SET e = $edge_data
107
+ WITH e CALL db.create.setRelationshipVectorProperty(e, "fact_embedding", $edge_data.fact_embedding)
108
+ RETURN e.uuid AS uuid
109
+ """
63
110
 
64
111
 
65
112
  def get_entity_edge_save_bulk_query(provider: GraphProvider) -> str:
66
- if provider == GraphProvider.FALKORDB:
67
- return """
68
- UNWIND $entity_edges AS edge
69
- MATCH (source:Entity {uuid: edge.source_node_uuid})
70
- MATCH (target:Entity {uuid: edge.target_node_uuid})
71
- MERGE (source)-[r:RELATES_TO {uuid: edge.uuid}]->(target)
72
- SET r = {uuid: edge.uuid, name: edge.name, group_id: edge.group_id, fact: edge.fact, episodes: edge.episodes,
73
- created_at: edge.created_at, expired_at: edge.expired_at, valid_at: edge.valid_at, invalid_at: edge.invalid_at, fact_embedding: vecf32(edge.fact_embedding)}
74
- WITH r, edge
75
- RETURN edge.uuid AS uuid
76
- """
77
-
78
- return """
79
- UNWIND $entity_edges AS edge
80
- MATCH (source:Entity {uuid: edge.source_node_uuid})
81
- MATCH (target:Entity {uuid: edge.target_node_uuid})
82
- MERGE (source)-[e:RELATES_TO {uuid: edge.uuid}]->(target)
83
- SET e = edge
84
- WITH e, edge CALL db.create.setRelationshipVectorProperty(e, "fact_embedding", edge.fact_embedding)
85
- RETURN edge.uuid AS uuid
86
- """
87
-
113
+ match provider:
114
+ case GraphProvider.FALKORDB:
115
+ return """
116
+ UNWIND $entity_edges AS edge
117
+ MATCH (source:Entity {uuid: edge.source_node_uuid})
118
+ MATCH (target:Entity {uuid: edge.target_node_uuid})
119
+ MERGE (source)-[r:RELATES_TO {uuid: edge.uuid}]->(target)
120
+ SET r = {uuid: edge.uuid, name: edge.name, group_id: edge.group_id, fact: edge.fact, episodes: edge.episodes,
121
+ created_at: edge.created_at, expired_at: edge.expired_at, valid_at: edge.valid_at, invalid_at: edge.invalid_at, fact_embedding: vecf32(edge.fact_embedding)}
122
+ WITH r, edge
123
+ RETURN edge.uuid AS uuid
124
+ """
125
+ case GraphProvider.NEPTUNE:
126
+ return """
127
+ UNWIND $entity_edges AS edge
128
+ MATCH (source:Entity {uuid: edge.source_node_uuid})
129
+ MATCH (target:Entity {uuid: edge.target_node_uuid})
130
+ MERGE (source)-[r:RELATES_TO {uuid: edge.uuid}]->(target)
131
+ SET r = removeKeyFromMap(removeKeyFromMap(edge, "fact_embedding"), "episodes")
132
+ SET r.fact_embedding = join([x IN coalesce(edge.fact_embedding, []) | toString(x) ], ",")
133
+ SET r.episodes = join(edge.episodes, ",")
134
+ RETURN edge.uuid AS uuid
135
+ """
136
+ case GraphProvider.KUZU:
137
+ return """
138
+ MATCH (source:Entity {uuid: $source_node_uuid})
139
+ MATCH (target:Entity {uuid: $target_node_uuid})
140
+ MERGE (source)-[:RELATES_TO]->(e:RelatesToNode_ {uuid: $uuid})-[:RELATES_TO]->(target)
141
+ SET
142
+ e.group_id = $group_id,
143
+ e.created_at = $created_at,
144
+ e.name = $name,
145
+ e.fact = $fact,
146
+ e.fact_embedding = $fact_embedding,
147
+ e.episodes = $episodes,
148
+ e.expired_at = $expired_at,
149
+ e.valid_at = $valid_at,
150
+ e.invalid_at = $invalid_at,
151
+ e.attributes = $attributes
152
+ RETURN e.uuid AS uuid
153
+ """
154
+ case _:
155
+ return """
156
+ UNWIND $entity_edges AS edge
157
+ MATCH (source:Entity {uuid: edge.source_node_uuid})
158
+ MATCH (target:Entity {uuid: edge.target_node_uuid})
159
+ MERGE (source)-[e:RELATES_TO {uuid: edge.uuid}]->(target)
160
+ SET e = edge
161
+ WITH e, edge CALL db.create.setRelationshipVectorProperty(e, "fact_embedding", edge.fact_embedding)
162
+ RETURN edge.uuid AS uuid
163
+ """
88
164
 
89
- ENTITY_EDGE_RETURN = """
90
- e.uuid AS uuid,
91
- n.uuid AS source_node_uuid,
92
- m.uuid AS target_node_uuid,
93
- e.group_id AS group_id,
94
- e.name AS name,
95
- e.fact AS fact,
96
- e.episodes AS episodes,
97
- e.created_at AS created_at,
98
- e.expired_at AS expired_at,
99
- e.valid_at AS valid_at,
100
- e.invalid_at AS invalid_at,
101
- properties(e) AS attributes
102
- """
103
165
 
166
+ def get_entity_edge_return_query(provider: GraphProvider) -> str:
167
+ # `fact_embedding` is not returned by default and must be manually loaded using `load_fact_embedding()`.
104
168
 
105
- def get_community_edge_save_query(provider: GraphProvider) -> str:
106
- if provider == GraphProvider.FALKORDB:
169
+ if provider == GraphProvider.NEPTUNE:
107
170
  return """
108
- MATCH (community:Community {uuid: $community_uuid})
109
- MATCH (node {uuid: $entity_uuid})
110
- MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
111
- SET e = {uuid: $uuid, group_id: $group_id, created_at: $created_at}
112
- RETURN e.uuid AS uuid
113
- """
171
+ e.uuid AS uuid,
172
+ n.uuid AS source_node_uuid,
173
+ m.uuid AS target_node_uuid,
174
+ e.group_id AS group_id,
175
+ e.name AS name,
176
+ e.fact AS fact,
177
+ split(e.episodes, ',') AS episodes,
178
+ e.created_at AS created_at,
179
+ e.expired_at AS expired_at,
180
+ e.valid_at AS valid_at,
181
+ e.invalid_at AS invalid_at,
182
+ properties(e) AS attributes
183
+ """
114
184
 
115
185
  return """
116
- MATCH (community:Community {uuid: $community_uuid})
117
- MATCH (node:Entity | Community {uuid: $entity_uuid})
118
- MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
119
- SET e = {uuid: $uuid, group_id: $group_id, created_at: $created_at}
120
- RETURN e.uuid AS uuid
121
- """
186
+ e.uuid AS uuid,
187
+ n.uuid AS source_node_uuid,
188
+ m.uuid AS target_node_uuid,
189
+ e.group_id AS group_id,
190
+ e.created_at AS created_at,
191
+ e.name AS name,
192
+ e.fact AS fact,
193
+ e.episodes AS episodes,
194
+ e.expired_at AS expired_at,
195
+ e.valid_at AS valid_at,
196
+ e.invalid_at AS invalid_at,
197
+ """ + (
198
+ 'e.attributes AS attributes'
199
+ if provider == GraphProvider.KUZU
200
+ else 'properties(e) AS attributes'
201
+ )
202
+
203
+
204
+ def get_community_edge_save_query(provider: GraphProvider) -> str:
205
+ match provider:
206
+ case GraphProvider.FALKORDB:
207
+ return """
208
+ MATCH (community:Community {uuid: $community_uuid})
209
+ MATCH (node {uuid: $entity_uuid})
210
+ MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
211
+ SET e = {uuid: $uuid, group_id: $group_id, created_at: $created_at}
212
+ RETURN e.uuid AS uuid
213
+ """
214
+ case GraphProvider.NEPTUNE:
215
+ return """
216
+ MATCH (community:Community {uuid: $community_uuid})
217
+ MATCH (node {uuid: $entity_uuid})
218
+ WHERE node:Entity OR node:Community
219
+ MERGE (community)-[r:HAS_MEMBER {uuid: $uuid}]->(node)
220
+ SET r.uuid= $uuid
221
+ SET r.group_id= $group_id
222
+ SET r.created_at= $created_at
223
+ RETURN r.uuid AS uuid
224
+ """
225
+ case GraphProvider.KUZU:
226
+ return """
227
+ MATCH (community:Community {uuid: $community_uuid})
228
+ MATCH (node:Entity {uuid: $entity_uuid})
229
+ MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
230
+ SET
231
+ e.group_id = $group_id,
232
+ e.created_at = $created_at
233
+ RETURN e.uuid AS uuid
234
+ UNION
235
+ MATCH (community:Community {uuid: $community_uuid})
236
+ MATCH (node:Community {uuid: $entity_uuid})
237
+ MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
238
+ SET
239
+ e.group_id = $group_id,
240
+ e.created_at = $created_at
241
+ RETURN e.uuid AS uuid
242
+ """
243
+ case _: # Neo4j
244
+ return """
245
+ MATCH (community:Community {uuid: $community_uuid})
246
+ MATCH (node:Entity | Community {uuid: $entity_uuid})
247
+ MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
248
+ SET e = {uuid: $uuid, group_id: $group_id, created_at: $created_at}
249
+ RETURN e.uuid AS uuid
250
+ """
122
251
 
123
252
 
124
253
  COMMUNITY_EDGE_RETURN = """