graphiti-core 0.10.4__py3-none-any.whl → 0.11.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.
- graphiti_core/edges.py +32 -57
- graphiti_core/embedder/client.py +3 -0
- graphiti_core/embedder/gemini.py +10 -0
- graphiti_core/embedder/openai.py +6 -0
- graphiti_core/embedder/voyage.py +7 -0
- graphiti_core/graphiti.py +42 -138
- graphiti_core/graphiti_types.py +31 -0
- graphiti_core/helpers.py +6 -1
- graphiti_core/llm_client/anthropic_client.py +4 -1
- graphiti_core/llm_client/client.py +4 -1
- graphiti_core/llm_client/gemini_client.py +4 -1
- graphiti_core/llm_client/openai_client.py +4 -1
- graphiti_core/llm_client/openai_generic_client.py +4 -1
- graphiti_core/models/edges/edge_db_queries.py +1 -1
- graphiti_core/nodes.py +10 -10
- graphiti_core/prompts/dedupe_edges.py +5 -7
- graphiti_core/prompts/dedupe_nodes.py +8 -21
- graphiti_core/prompts/extract_edges.py +61 -26
- graphiti_core/prompts/extract_nodes.py +89 -18
- graphiti_core/prompts/invalidate_edges.py +11 -11
- graphiti_core/search/search.py +13 -5
- graphiti_core/search/search_utils.py +206 -98
- graphiti_core/utils/bulk_utils.py +10 -7
- graphiti_core/utils/maintenance/edge_operations.py +88 -40
- graphiti_core/utils/maintenance/graph_data_operations.py +20 -6
- graphiti_core/utils/maintenance/node_operations.py +216 -223
- graphiti_core/utils/maintenance/temporal_operations.py +4 -11
- {graphiti_core-0.10.4.dist-info → graphiti_core-0.11.0.dist-info}/METADATA +25 -11
- {graphiti_core-0.10.4.dist-info → graphiti_core-0.11.0.dist-info}/RECORD +31 -30
- {graphiti_core-0.10.4.dist-info → graphiti_core-0.11.0.dist-info}/LICENSE +0 -0
- {graphiti_core-0.10.4.dist-info → graphiti_core-0.11.0.dist-info}/WHEEL +0 -0
|
@@ -26,7 +26,7 @@ from typing_extensions import LiteralString
|
|
|
26
26
|
from graphiti_core.edges import EntityEdge, get_entity_edge_from_record
|
|
27
27
|
from graphiti_core.helpers import (
|
|
28
28
|
DEFAULT_DATABASE,
|
|
29
|
-
|
|
29
|
+
RUNTIME_QUERY,
|
|
30
30
|
lucene_sanitize,
|
|
31
31
|
normalize_l2,
|
|
32
32
|
semaphore_gather,
|
|
@@ -54,20 +54,6 @@ DEFAULT_MMR_LAMBDA = 0.5
|
|
|
54
54
|
MAX_SEARCH_DEPTH = 3
|
|
55
55
|
MAX_QUERY_LENGTH = 32
|
|
56
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
|
-
|
|
71
57
|
|
|
72
58
|
def fulltext_query(query: str, group_ids: list[str] | None = None):
|
|
73
59
|
group_ids_filter_list = (
|
|
@@ -221,10 +207,6 @@ async def edge_similarity_search(
|
|
|
221
207
|
min_score: float = DEFAULT_MIN_SCORE,
|
|
222
208
|
) -> list[EntityEdge]:
|
|
223
209
|
# vector similarity search over embedded facts
|
|
224
|
-
runtime_query: LiteralString = (
|
|
225
|
-
'CYPHER runtime = parallel parallelRuntimeSupport=all\n' if USE_PARALLEL_RUNTIME else ''
|
|
226
|
-
)
|
|
227
|
-
|
|
228
210
|
query_params: dict[str, Any] = {}
|
|
229
211
|
|
|
230
212
|
filter_query, filter_params = edge_search_filter_query_constructor(search_filter)
|
|
@@ -244,9 +226,10 @@ async def edge_similarity_search(
|
|
|
244
226
|
group_filter_query += '\nAND (m.uuid IN [$source_uuid, $target_uuid])'
|
|
245
227
|
|
|
246
228
|
query: LiteralString = (
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
229
|
+
RUNTIME_QUERY
|
|
230
|
+
+ """
|
|
231
|
+
MATCH (n:Entity)-[r:RELATES_TO]->(m:Entity)
|
|
232
|
+
"""
|
|
250
233
|
+ group_filter_query
|
|
251
234
|
+ filter_query
|
|
252
235
|
+ """\nWITH DISTINCT r, vector.similarity.cosine(r.fact_embedding, $search_vector) AS score
|
|
@@ -270,7 +253,7 @@ async def edge_similarity_search(
|
|
|
270
253
|
)
|
|
271
254
|
|
|
272
255
|
records, _, _ = await driver.execute_query(
|
|
273
|
-
|
|
256
|
+
query,
|
|
274
257
|
query_params,
|
|
275
258
|
search_vector=search_vector,
|
|
276
259
|
source_uuid=source_node_uuid,
|
|
@@ -358,12 +341,12 @@ async def node_fulltext_search(
|
|
|
358
341
|
|
|
359
342
|
query = (
|
|
360
343
|
"""
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
344
|
+
CALL db.index.fulltext.queryNodes("node_name_and_summary", $query, {limit: $limit})
|
|
345
|
+
YIELD node AS n, score
|
|
346
|
+
WHERE n:Entity
|
|
347
|
+
"""
|
|
365
348
|
+ filter_query
|
|
366
|
-
+
|
|
349
|
+
+ ENTITY_NODE_RETURN
|
|
367
350
|
+ """
|
|
368
351
|
ORDER BY score DESC
|
|
369
352
|
"""
|
|
@@ -392,10 +375,6 @@ async def node_similarity_search(
|
|
|
392
375
|
min_score: float = DEFAULT_MIN_SCORE,
|
|
393
376
|
) -> list[EntityNode]:
|
|
394
377
|
# vector similarity search over entity names
|
|
395
|
-
runtime_query: LiteralString = (
|
|
396
|
-
'CYPHER runtime = parallel parallelRuntimeSupport=all\n' if USE_PARALLEL_RUNTIME else ''
|
|
397
|
-
)
|
|
398
|
-
|
|
399
378
|
query_params: dict[str, Any] = {}
|
|
400
379
|
|
|
401
380
|
group_filter_query: LiteralString = ''
|
|
@@ -407,7 +386,7 @@ async def node_similarity_search(
|
|
|
407
386
|
query_params.update(filter_params)
|
|
408
387
|
|
|
409
388
|
records, _, _ = await driver.execute_query(
|
|
410
|
-
|
|
389
|
+
RUNTIME_QUERY
|
|
411
390
|
+ """
|
|
412
391
|
MATCH (n:Entity)
|
|
413
392
|
"""
|
|
@@ -416,7 +395,7 @@ async def node_similarity_search(
|
|
|
416
395
|
+ """
|
|
417
396
|
WITH n, vector.similarity.cosine(n.name_embedding, $search_vector) AS score
|
|
418
397
|
WHERE score > $min_score"""
|
|
419
|
-
+
|
|
398
|
+
+ ENTITY_NODE_RETURN
|
|
420
399
|
+ """
|
|
421
400
|
ORDER BY score DESC
|
|
422
401
|
LIMIT $limit
|
|
@@ -556,10 +535,6 @@ async def community_similarity_search(
|
|
|
556
535
|
min_score=DEFAULT_MIN_SCORE,
|
|
557
536
|
) -> list[CommunityNode]:
|
|
558
537
|
# vector similarity search over entity names
|
|
559
|
-
runtime_query: LiteralString = (
|
|
560
|
-
'CYPHER runtime = parallel parallelRuntimeSupport=all\n' if USE_PARALLEL_RUNTIME else ''
|
|
561
|
-
)
|
|
562
|
-
|
|
563
538
|
query_params: dict[str, Any] = {}
|
|
564
539
|
|
|
565
540
|
group_filter_query: LiteralString = ''
|
|
@@ -568,7 +543,7 @@ async def community_similarity_search(
|
|
|
568
543
|
query_params['group_ids'] = group_ids
|
|
569
544
|
|
|
570
545
|
records, _, _ = await driver.execute_query(
|
|
571
|
-
|
|
546
|
+
RUNTIME_QUERY
|
|
572
547
|
+ """
|
|
573
548
|
MATCH (comm:Community)
|
|
574
549
|
"""
|
|
@@ -674,86 +649,219 @@ async def hybrid_node_search(
|
|
|
674
649
|
|
|
675
650
|
async def get_relevant_nodes(
|
|
676
651
|
driver: AsyncDriver,
|
|
677
|
-
search_filter: SearchFilters,
|
|
678
652
|
nodes: list[EntityNode],
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
653
|
+
search_filter: SearchFilters,
|
|
654
|
+
min_score: float = DEFAULT_MIN_SCORE,
|
|
655
|
+
limit: int = RELEVANT_SCHEMA_LIMIT,
|
|
656
|
+
) -> list[list[EntityNode]]:
|
|
657
|
+
if len(nodes) == 0:
|
|
658
|
+
return []
|
|
682
659
|
|
|
683
|
-
|
|
684
|
-
of the input nodes to find relevant nodes in the graph database.
|
|
660
|
+
group_id = nodes[0].group_id
|
|
685
661
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
nodes : list[EntityNode]
|
|
689
|
-
A list of EntityNode objects to use as the basis for the search.
|
|
690
|
-
driver : AsyncDriver
|
|
691
|
-
The Neo4j driver instance for database operations.
|
|
662
|
+
# vector similarity search over entity names
|
|
663
|
+
query_params: dict[str, Any] = {}
|
|
692
664
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
list[EntityNode]
|
|
696
|
-
A list of EntityNode objects that are deemed relevant based on the input nodes.
|
|
665
|
+
filter_query, filter_params = node_search_filter_query_constructor(search_filter)
|
|
666
|
+
query_params.update(filter_params)
|
|
697
667
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
668
|
+
query = (
|
|
669
|
+
RUNTIME_QUERY
|
|
670
|
+
+ """UNWIND $nodes AS node
|
|
671
|
+
MATCH (n:Entity {group_id: $group_id})
|
|
672
|
+
"""
|
|
673
|
+
+ filter_query
|
|
674
|
+
+ """
|
|
675
|
+
WITH node, n, vector.similarity.cosine(n.name_embedding, node.name_embedding) AS score
|
|
676
|
+
WHERE score > $min_score
|
|
677
|
+
WITH node, collect(n)[..$limit] AS top_vector_nodes, collect(n.uuid) AS vector_node_uuids
|
|
678
|
+
|
|
679
|
+
CALL db.index.fulltext.queryNodes("node_name_and_summary", 'group_id:"' + $group_id + '" AND ' + node.name, {limit: $limit})
|
|
680
|
+
YIELD node AS m
|
|
681
|
+
WHERE m.group_id = $group_id
|
|
682
|
+
WITH node, top_vector_nodes, vector_node_uuids, collect(m) AS fulltext_nodes
|
|
683
|
+
|
|
684
|
+
WITH node,
|
|
685
|
+
top_vector_nodes,
|
|
686
|
+
[m IN fulltext_nodes WHERE NOT m.uuid IN vector_node_uuids] AS filtered_fulltext_nodes
|
|
687
|
+
|
|
688
|
+
WITH node, top_vector_nodes + filtered_fulltext_nodes AS combined_nodes
|
|
689
|
+
|
|
690
|
+
UNWIND combined_nodes AS combined_node
|
|
691
|
+
WITH node, collect(DISTINCT combined_node) AS deduped_nodes
|
|
692
|
+
|
|
693
|
+
RETURN
|
|
694
|
+
node.uuid AS search_node_uuid,
|
|
695
|
+
[x IN deduped_nodes | {
|
|
696
|
+
uuid: x.uuid,
|
|
697
|
+
name: x.name,
|
|
698
|
+
name_embedding: x.name_embedding,
|
|
699
|
+
group_id: x.group_id,
|
|
700
|
+
created_at: x.created_at,
|
|
701
|
+
summary: x.summary,
|
|
702
|
+
labels: labels(x),
|
|
703
|
+
attributes: properties(x)
|
|
704
|
+
}] AS matches
|
|
705
|
+
"""
|
|
711
706
|
)
|
|
712
707
|
|
|
708
|
+
results, _, _ = await driver.execute_query(
|
|
709
|
+
query,
|
|
710
|
+
query_params,
|
|
711
|
+
nodes=[
|
|
712
|
+
{'uuid': node.uuid, 'name': node.name, 'name_embedding': node.name_embedding}
|
|
713
|
+
for node in nodes
|
|
714
|
+
],
|
|
715
|
+
group_id=group_id,
|
|
716
|
+
limit=limit,
|
|
717
|
+
min_score=min_score,
|
|
718
|
+
database_=DEFAULT_DATABASE,
|
|
719
|
+
routing_='r',
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
relevant_nodes_dict: dict[str, list[EntityNode]] = {
|
|
723
|
+
result['search_node_uuid']: [
|
|
724
|
+
get_entity_node_from_record(record) for record in result['matches']
|
|
725
|
+
]
|
|
726
|
+
for result in results
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
relevant_nodes = [relevant_nodes_dict.get(node.uuid, []) for node in nodes]
|
|
730
|
+
|
|
713
731
|
return relevant_nodes
|
|
714
732
|
|
|
715
733
|
|
|
716
734
|
async def get_relevant_edges(
|
|
717
735
|
driver: AsyncDriver,
|
|
718
736
|
edges: list[EntityEdge],
|
|
719
|
-
|
|
720
|
-
|
|
737
|
+
search_filter: SearchFilters,
|
|
738
|
+
min_score: float = DEFAULT_MIN_SCORE,
|
|
721
739
|
limit: int = RELEVANT_SCHEMA_LIMIT,
|
|
722
|
-
) -> list[EntityEdge]:
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
relevant_edge_uuids = set()
|
|
726
|
-
|
|
727
|
-
results = await semaphore_gather(
|
|
728
|
-
*[
|
|
729
|
-
edge_similarity_search(
|
|
730
|
-
driver,
|
|
731
|
-
edge.fact_embedding,
|
|
732
|
-
source_node_uuid,
|
|
733
|
-
target_node_uuid,
|
|
734
|
-
SearchFilters(),
|
|
735
|
-
[edge.group_id],
|
|
736
|
-
limit,
|
|
737
|
-
)
|
|
738
|
-
for edge in edges
|
|
739
|
-
if edge.fact_embedding is not None
|
|
740
|
-
]
|
|
741
|
-
)
|
|
740
|
+
) -> list[list[EntityEdge]]:
|
|
741
|
+
if len(edges) == 0:
|
|
742
|
+
return []
|
|
742
743
|
|
|
743
|
-
|
|
744
|
-
for edge in result:
|
|
745
|
-
if edge.uuid in relevant_edge_uuids:
|
|
746
|
-
continue
|
|
744
|
+
query_params: dict[str, Any] = {}
|
|
747
745
|
|
|
748
|
-
|
|
749
|
-
|
|
746
|
+
filter_query, filter_params = edge_search_filter_query_constructor(search_filter)
|
|
747
|
+
query_params.update(filter_params)
|
|
750
748
|
|
|
751
|
-
|
|
752
|
-
|
|
749
|
+
query = (
|
|
750
|
+
RUNTIME_QUERY
|
|
751
|
+
+ """UNWIND $edges AS edge
|
|
752
|
+
MATCH (n:Entity {uuid: edge.source_node_uuid})-[e:RELATES_TO {group_id: edge.group_id}]-(m:Entity {uuid: edge.target_node_uuid})
|
|
753
|
+
"""
|
|
754
|
+
+ filter_query
|
|
755
|
+
+ """
|
|
756
|
+
WITH e, edge, vector.similarity.cosine(e.fact_embedding, edge.fact_embedding) AS score
|
|
757
|
+
WHERE score > $min_score
|
|
758
|
+
WITH edge, e, score
|
|
759
|
+
ORDER BY score DESC
|
|
760
|
+
RETURN edge.uuid AS search_edge_uuid,
|
|
761
|
+
collect({
|
|
762
|
+
uuid: e.uuid,
|
|
763
|
+
source_node_uuid: startNode(e).uuid,
|
|
764
|
+
target_node_uuid: endNode(e).uuid,
|
|
765
|
+
created_at: e.created_at,
|
|
766
|
+
name: e.name,
|
|
767
|
+
group_id: e.group_id,
|
|
768
|
+
fact: e.fact,
|
|
769
|
+
fact_embedding: e.fact_embedding,
|
|
770
|
+
episodes: e.episodes,
|
|
771
|
+
expired_at: e.expired_at,
|
|
772
|
+
valid_at: e.valid_at,
|
|
773
|
+
invalid_at: e.invalid_at
|
|
774
|
+
})[..$limit] AS matches
|
|
775
|
+
"""
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
results, _, _ = await driver.execute_query(
|
|
779
|
+
query,
|
|
780
|
+
query_params,
|
|
781
|
+
edges=[edge.model_dump() for edge in edges],
|
|
782
|
+
limit=limit,
|
|
783
|
+
min_score=min_score,
|
|
784
|
+
database_=DEFAULT_DATABASE,
|
|
785
|
+
routing_='r',
|
|
786
|
+
)
|
|
787
|
+
relevant_edges_dict: dict[str, list[EntityEdge]] = {
|
|
788
|
+
result['search_edge_uuid']: [
|
|
789
|
+
get_entity_edge_from_record(record) for record in result['matches']
|
|
790
|
+
]
|
|
791
|
+
for result in results
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
relevant_edges = [relevant_edges_dict.get(edge.uuid, []) for edge in edges]
|
|
753
795
|
|
|
754
796
|
return relevant_edges
|
|
755
797
|
|
|
756
798
|
|
|
799
|
+
async def get_edge_invalidation_candidates(
|
|
800
|
+
driver: AsyncDriver,
|
|
801
|
+
edges: list[EntityEdge],
|
|
802
|
+
search_filter: SearchFilters,
|
|
803
|
+
min_score: float = DEFAULT_MIN_SCORE,
|
|
804
|
+
limit: int = RELEVANT_SCHEMA_LIMIT,
|
|
805
|
+
) -> list[list[EntityEdge]]:
|
|
806
|
+
if len(edges) == 0:
|
|
807
|
+
return []
|
|
808
|
+
|
|
809
|
+
query_params: dict[str, Any] = {}
|
|
810
|
+
|
|
811
|
+
filter_query, filter_params = edge_search_filter_query_constructor(search_filter)
|
|
812
|
+
query_params.update(filter_params)
|
|
813
|
+
|
|
814
|
+
query = (
|
|
815
|
+
RUNTIME_QUERY
|
|
816
|
+
+ """UNWIND $edges AS edge
|
|
817
|
+
MATCH (n:Entity)-[e:RELATES_TO {group_id: edge.group_id}]->(m:Entity)
|
|
818
|
+
WHERE n.uuid IN [edge.source_node_uuid, edge.target_node_uuid] OR m.uuid IN [edge.target_node_uuid, edge.source_node_uuid]
|
|
819
|
+
"""
|
|
820
|
+
+ filter_query
|
|
821
|
+
+ """
|
|
822
|
+
WITH edge, e, vector.similarity.cosine(e.fact_embedding, edge.fact_embedding) AS score
|
|
823
|
+
WHERE score > $min_score
|
|
824
|
+
WITH edge, e, score
|
|
825
|
+
ORDER BY score DESC
|
|
826
|
+
RETURN edge.uuid AS search_edge_uuid,
|
|
827
|
+
collect({
|
|
828
|
+
uuid: e.uuid,
|
|
829
|
+
source_node_uuid: startNode(e).uuid,
|
|
830
|
+
target_node_uuid: endNode(e).uuid,
|
|
831
|
+
created_at: e.created_at,
|
|
832
|
+
name: e.name,
|
|
833
|
+
group_id: e.group_id,
|
|
834
|
+
fact: e.fact,
|
|
835
|
+
fact_embedding: e.fact_embedding,
|
|
836
|
+
episodes: e.episodes,
|
|
837
|
+
expired_at: e.expired_at,
|
|
838
|
+
valid_at: e.valid_at,
|
|
839
|
+
invalid_at: e.invalid_at
|
|
840
|
+
})[..$limit] AS matches
|
|
841
|
+
"""
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
results, _, _ = await driver.execute_query(
|
|
845
|
+
query,
|
|
846
|
+
query_params,
|
|
847
|
+
edges=[edge.model_dump() for edge in edges],
|
|
848
|
+
limit=limit,
|
|
849
|
+
min_score=min_score,
|
|
850
|
+
database_=DEFAULT_DATABASE,
|
|
851
|
+
routing_='r',
|
|
852
|
+
)
|
|
853
|
+
invalidation_edges_dict: dict[str, list[EntityEdge]] = {
|
|
854
|
+
result['search_edge_uuid']: [
|
|
855
|
+
get_entity_edge_from_record(record) for record in result['matches']
|
|
856
|
+
]
|
|
857
|
+
for result in results
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
invalidation_edges = [invalidation_edges_dict.get(edge.uuid, []) for edge in edges]
|
|
861
|
+
|
|
862
|
+
return invalidation_edges
|
|
863
|
+
|
|
864
|
+
|
|
757
865
|
# takes in a list of rankings of uuids
|
|
758
866
|
def rrf(results: list[list[str]], rank_const=1, min_score: float = 0) -> list[str]:
|
|
759
867
|
scores: dict[str, float] = defaultdict(float)
|
|
@@ -26,6 +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.graphiti_types import GraphitiClients
|
|
29
30
|
from graphiti_core.helpers import DEFAULT_DATABASE, semaphore_gather
|
|
30
31
|
from graphiti_core.llm_client import LLMClient
|
|
31
32
|
from graphiti_core.models.edges.edge_db_queries import (
|
|
@@ -128,16 +129,18 @@ async def add_nodes_and_edges_bulk_tx(
|
|
|
128
129
|
|
|
129
130
|
await tx.run(EPISODIC_NODE_SAVE_BULK, episodes=episodes)
|
|
130
131
|
await tx.run(ENTITY_NODE_SAVE_BULK, nodes=nodes)
|
|
131
|
-
await tx.run(
|
|
132
|
-
|
|
132
|
+
await tx.run(
|
|
133
|
+
EPISODIC_EDGE_SAVE_BULK, episodic_edges=[edge.model_dump() for edge in episodic_edges]
|
|
134
|
+
)
|
|
135
|
+
await tx.run(ENTITY_EDGE_SAVE_BULK, entity_edges=[edge.model_dump() for edge in entity_edges])
|
|
133
136
|
|
|
134
137
|
|
|
135
138
|
async def extract_nodes_and_edges_bulk(
|
|
136
|
-
|
|
139
|
+
clients: GraphitiClients, episode_tuples: list[tuple[EpisodicNode, list[EpisodicNode]]]
|
|
137
140
|
) -> tuple[list[EntityNode], list[EntityEdge], list[EpisodicEdge]]:
|
|
138
141
|
extracted_nodes_bulk = await semaphore_gather(
|
|
139
142
|
*[
|
|
140
|
-
extract_nodes(
|
|
143
|
+
extract_nodes(clients, episode, previous_episodes)
|
|
141
144
|
for episode, previous_episodes in episode_tuples
|
|
142
145
|
]
|
|
143
146
|
)
|
|
@@ -150,7 +153,7 @@ async def extract_nodes_and_edges_bulk(
|
|
|
150
153
|
extracted_edges_bulk = await semaphore_gather(
|
|
151
154
|
*[
|
|
152
155
|
extract_edges(
|
|
153
|
-
|
|
156
|
+
clients,
|
|
154
157
|
episode,
|
|
155
158
|
extracted_nodes_bulk[i],
|
|
156
159
|
previous_episodes_list[i],
|
|
@@ -189,7 +192,7 @@ async def dedupe_nodes_bulk(
|
|
|
189
192
|
|
|
190
193
|
existing_nodes_chunks: list[list[EntityNode]] = list(
|
|
191
194
|
await semaphore_gather(
|
|
192
|
-
*[get_relevant_nodes(driver, SearchFilters()
|
|
195
|
+
*[get_relevant_nodes(driver, node_chunk, SearchFilters()) for node_chunk in node_chunks]
|
|
193
196
|
)
|
|
194
197
|
)
|
|
195
198
|
|
|
@@ -223,7 +226,7 @@ async def dedupe_edges_bulk(
|
|
|
223
226
|
|
|
224
227
|
relevant_edges_chunks: list[list[EntityEdge]] = list(
|
|
225
228
|
await semaphore_gather(
|
|
226
|
-
*[get_relevant_edges(driver, edge_chunk,
|
|
229
|
+
*[get_relevant_edges(driver, edge_chunk, SearchFilters()) for edge_chunk in edge_chunks]
|
|
227
230
|
)
|
|
228
231
|
)
|
|
229
232
|
|