graphiti-core 0.3.18__tar.gz → 0.3.20__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.
Files changed (57) hide show
  1. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/PKG-INFO +14 -3
  2. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/README.md +13 -2
  3. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/edges.py +1 -1
  4. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/embedder/client.py +1 -1
  5. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/graphiti.py +4 -4
  6. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/helpers.py +1 -0
  7. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/llm_client/utils.py +1 -1
  8. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/models/edges/edge_db_queries.py +20 -0
  9. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/models/nodes/node_db_queries.py +17 -0
  10. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/nodes.py +2 -2
  11. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/search/search.py +1 -1
  12. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/search/search_utils.py +41 -24
  13. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/utils/bulk_utils.py +38 -1
  14. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/pyproject.toml +1 -1
  15. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/LICENSE +0 -0
  16. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/__init__.py +0 -0
  17. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/cross_encoder/__init__.py +0 -0
  18. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/cross_encoder/bge_reranker_client.py +0 -0
  19. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/cross_encoder/client.py +0 -0
  20. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/cross_encoder/openai_reranker_client.py +0 -0
  21. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/embedder/__init__.py +0 -0
  22. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/embedder/openai.py +0 -0
  23. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/embedder/voyage.py +0 -0
  24. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/errors.py +0 -0
  25. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/llm_client/__init__.py +0 -0
  26. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/llm_client/anthropic_client.py +0 -0
  27. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/llm_client/client.py +0 -0
  28. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/llm_client/config.py +0 -0
  29. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/llm_client/errors.py +0 -0
  30. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/llm_client/groq_client.py +0 -0
  31. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/llm_client/openai_client.py +0 -0
  32. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/models/__init__.py +0 -0
  33. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/models/edges/__init__.py +0 -0
  34. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/models/nodes/__init__.py +0 -0
  35. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/__init__.py +0 -0
  36. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/dedupe_edges.py +0 -0
  37. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/dedupe_nodes.py +0 -0
  38. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/eval.py +0 -0
  39. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/extract_edge_dates.py +0 -0
  40. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/extract_edges.py +0 -0
  41. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/extract_nodes.py +0 -0
  42. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/invalidate_edges.py +0 -0
  43. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/lib.py +0 -0
  44. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/models.py +0 -0
  45. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/prompts/summarize_nodes.py +0 -0
  46. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/py.typed +0 -0
  47. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/search/__init__.py +0 -0
  48. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/search/search_config.py +0 -0
  49. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/search/search_config_recipes.py +0 -0
  50. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/utils/__init__.py +0 -0
  51. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/utils/maintenance/__init__.py +0 -0
  52. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/utils/maintenance/community_operations.py +0 -0
  53. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/utils/maintenance/edge_operations.py +0 -0
  54. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/utils/maintenance/graph_data_operations.py +0 -0
  55. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/utils/maintenance/node_operations.py +0 -0
  56. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/utils/maintenance/temporal_operations.py +0 -0
  57. {graphiti_core-0.3.18 → graphiti_core-0.3.20}/graphiti_core/utils/maintenance/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: graphiti-core
3
- Version: 0.3.18
3
+ Version: 0.3.20
4
4
  Summary: A temporal graph building library
5
5
  License: Apache-2.0
6
6
  Author: Paul Paliychuk
@@ -194,6 +194,17 @@ The `server` directory contains an API service for interacting with the Graphiti
194
194
 
195
195
  Please see the [server README](./server/README.md) for more information.
196
196
 
197
+ ## Optional Environment Variables
198
+
199
+ In addition to the Neo4j and OpenAi-compatible credentials, Graphiti also has a few optional environment variables.
200
+ If you are using one of our supported models, such as Anthropic or Voyage models, the necessary environment variables
201
+ must be set.
202
+
203
+ `USE_PARALLEL_RUNTIME` is an optional boolean variable that can be set to true if you wish
204
+ to enable Neo4j's parallel runtime feature for several of our search queries.
205
+ Note that this feature is not supported for Neo4j Community edition or for smaller AuraDB instances,
206
+ as such this feature is off by default.
207
+
197
208
  ## Documentation
198
209
 
199
210
  - [Guides and API documentation](https://help.getzep.com/graphiti).
@@ -207,11 +218,11 @@ Graphiti is under active development. We aim to maintain API stability while wor
207
218
  - [x] Implementing node and edge CRUD operations
208
219
  - [ ] Improving performance and scalability
209
220
  - [ ] Achieving good performance with different LLM and embedding models
210
- - [ ] Creating a dedicated embedder interface
221
+ - [x] Creating a dedicated embedder interface
211
222
  - [ ] Supporting custom graph schemas:
212
223
  - Allow developers to provide their own defined node and edge classes when ingesting episodes
213
224
  - Enable more flexible knowledge representation tailored to specific use cases
214
- - [ ] Enhancing retrieval capabilities with more robust and configurable options
225
+ - [x] Enhancing retrieval capabilities with more robust and configurable options
215
226
  - [ ] Expanding test coverage to ensure reliability and catch edge cases
216
227
 
217
228
  ## Contributing
@@ -173,6 +173,17 @@ The `server` directory contains an API service for interacting with the Graphiti
173
173
 
174
174
  Please see the [server README](./server/README.md) for more information.
175
175
 
176
+ ## Optional Environment Variables
177
+
178
+ In addition to the Neo4j and OpenAi-compatible credentials, Graphiti also has a few optional environment variables.
179
+ If you are using one of our supported models, such as Anthropic or Voyage models, the necessary environment variables
180
+ must be set.
181
+
182
+ `USE_PARALLEL_RUNTIME` is an optional boolean variable that can be set to true if you wish
183
+ to enable Neo4j's parallel runtime feature for several of our search queries.
184
+ Note that this feature is not supported for Neo4j Community edition or for smaller AuraDB instances,
185
+ as such this feature is off by default.
186
+
176
187
  ## Documentation
177
188
 
178
189
  - [Guides and API documentation](https://help.getzep.com/graphiti).
@@ -186,11 +197,11 @@ Graphiti is under active development. We aim to maintain API stability while wor
186
197
  - [x] Implementing node and edge CRUD operations
187
198
  - [ ] Improving performance and scalability
188
199
  - [ ] Achieving good performance with different LLM and embedding models
189
- - [ ] Creating a dedicated embedder interface
200
+ - [x] Creating a dedicated embedder interface
190
201
  - [ ] Supporting custom graph schemas:
191
202
  - Allow developers to provide their own defined node and edge classes when ingesting episodes
192
203
  - Enable more flexible knowledge representation tailored to specific use cases
193
- - [ ] Enhancing retrieval capabilities with more robust and configurable options
204
+ - [x] Enhancing retrieval capabilities with more robust and configurable options
194
205
  - [ ] Expanding test coverage to ensure reliability and catch edge cases
195
206
 
196
207
  ## Contributing
@@ -180,7 +180,7 @@ class EntityEdge(Edge):
180
180
  start = time()
181
181
 
182
182
  text = self.fact.replace('\n', ' ')
183
- self.fact_embedding = await embedder.create(input=[text])
183
+ self.fact_embedding = await embedder.create(input_data=[text])
184
184
 
185
185
  end = time()
186
186
  logger.debug(f'embedded {text} in {end - start} ms')
@@ -29,6 +29,6 @@ class EmbedderConfig(BaseModel):
29
29
  class EmbedderClient(ABC):
30
30
  @abstractmethod
31
31
  async def create(
32
- self, input: str | List[str] | Iterable[int] | Iterable[Iterable[int]]
32
+ self, input_data: str | List[str] | Iterable[int] | Iterable[Iterable[int]]
33
33
  ) -> list[float]:
34
34
  pass
@@ -51,6 +51,7 @@ from graphiti_core.utils import (
51
51
  )
52
52
  from graphiti_core.utils.bulk_utils import (
53
53
  RawEpisode,
54
+ add_nodes_and_edges_bulk,
54
55
  dedupe_edges_bulk,
55
56
  dedupe_nodes_bulk,
56
57
  extract_edge_dates_bulk,
@@ -451,10 +452,9 @@ class Graphiti:
451
452
  if not self.store_raw_episode_content:
452
453
  episode.content = ''
453
454
 
454
- await episode.save(self.driver)
455
- await asyncio.gather(*[node.save(self.driver) for node in nodes])
456
- await asyncio.gather(*[edge.save(self.driver) for edge in episodic_edges])
457
- await asyncio.gather(*[edge.save(self.driver) for edge in entity_edges])
455
+ await add_nodes_and_edges_bulk(
456
+ self.driver, [episode], episodic_edges, nodes, entity_edges
457
+ )
458
458
 
459
459
  # Update any communities
460
460
  if update_communities:
@@ -24,6 +24,7 @@ from neo4j import time as neo4j_time
24
24
  load_dotenv()
25
25
 
26
26
  DEFAULT_DATABASE = os.getenv('DEFAULT_DATABASE', None)
27
+ USE_PARALLEL_RUNTIME = bool(os.getenv('USE_PARALLEL_RUNTIME', False))
27
28
 
28
29
 
29
30
  def parse_db_date(neo_date: neo4j_time.DateTime | None) -> datetime | None:
@@ -26,7 +26,7 @@ async def generate_embedding(embedder: EmbedderClient, text: str):
26
26
  start = time()
27
27
 
28
28
  text = text.replace('\n', ' ')
29
- embedding = await embedder.create(input=[text])
29
+ embedding = await embedder.create(input_data=[text])
30
30
 
31
31
  end = time()
32
32
  logger.debug(f'embedded text of length {len(text)} in {end - start} ms')
@@ -5,6 +5,15 @@ EPISODIC_EDGE_SAVE = """
5
5
  SET r = {uuid: $uuid, group_id: $group_id, created_at: $created_at}
6
6
  RETURN r.uuid AS uuid"""
7
7
 
8
+ EPISODIC_EDGE_SAVE_BULK = """
9
+ UNWIND $episodic_edges AS edge
10
+ MATCH (episode:Episodic {uuid: edge.source_node_uuid})
11
+ MATCH (node:Entity {uuid: edge.target_node_uuid})
12
+ MERGE (episode)-[r:MENTIONS {uuid: edge.uuid}]->(node)
13
+ SET r = {uuid: edge.uuid, group_id: edge.group_id, created_at: edge.created_at}
14
+ RETURN r.uuid AS uuid
15
+ """
16
+
8
17
  ENTITY_EDGE_SAVE = """
9
18
  MATCH (source:Entity {uuid: $source_uuid})
10
19
  MATCH (target:Entity {uuid: $target_uuid})
@@ -14,6 +23,17 @@ ENTITY_EDGE_SAVE = """
14
23
  WITH r CALL db.create.setRelationshipVectorProperty(r, "fact_embedding", $fact_embedding)
15
24
  RETURN r.uuid AS uuid"""
16
25
 
26
+ ENTITY_EDGE_SAVE_BULK = """
27
+ UNWIND $entity_edges AS edge
28
+ MATCH (source:Entity {uuid: edge.source_node_uuid})
29
+ MATCH (target:Entity {uuid: edge.target_node_uuid})
30
+ MERGE (source)-[r:RELATES_TO {uuid: edge.uuid}]->(target)
31
+ SET r = {uuid: edge.uuid, name: edge.name, group_id: edge.group_id, fact: edge.fact, episodes: edge.episodes,
32
+ created_at: edge.created_at, expired_at: edge.expired_at, valid_at: edge.valid_at, invalid_at: edge.invalid_at}
33
+ WITH r, edge CALL db.create.setRelationshipVectorProperty(r, "fact_embedding", edge.fact_embedding)
34
+ RETURN r.uuid AS uuid
35
+ """
36
+
17
37
  COMMUNITY_EDGE_SAVE = """
18
38
  MATCH (community:Community {uuid: $community_uuid})
19
39
  MATCH (node:Entity | Community {uuid: $entity_uuid})
@@ -4,12 +4,29 @@ EPISODIC_NODE_SAVE = """
4
4
  entity_edges: $entity_edges, created_at: $created_at, valid_at: $valid_at}
5
5
  RETURN n.uuid AS uuid"""
6
6
 
7
+ EPISODIC_NODE_SAVE_BULK = """
8
+ UNWIND $episodes AS episode
9
+ MERGE (n:Episodic {uuid: episode.uuid})
10
+ SET n = {uuid: episode.uuid, name: episode.name, group_id: episode.group_id, source_description: episode.source_description,
11
+ source: episode.source, content: episode.content,
12
+ entity_edges: episode.entity_edges, created_at: episode.created_at, valid_at: episode.valid_at}
13
+ RETURN n.uuid AS uuid
14
+ """
15
+
7
16
  ENTITY_NODE_SAVE = """
8
17
  MERGE (n:Entity {uuid: $uuid})
9
18
  SET n = {uuid: $uuid, name: $name, group_id: $group_id, summary: $summary, created_at: $created_at}
10
19
  WITH n CALL db.create.setNodeVectorProperty(n, "name_embedding", $name_embedding)
11
20
  RETURN n.uuid AS uuid"""
12
21
 
22
+ ENTITY_NODE_SAVE_BULK = """
23
+ UNWIND $nodes AS node
24
+ MERGE (n:Entity {uuid: node.uuid})
25
+ SET n = {uuid: node.uuid, name: node.name, group_id: node.group_id, summary: node.summary, created_at: node.created_at}
26
+ WITH n, node CALL db.create.setNodeVectorProperty(n, "name_embedding", node.name_embedding)
27
+ RETURN n.uuid AS uuid
28
+ """
29
+
13
30
  COMMUNITY_NODE_SAVE = """
14
31
  MERGE (n:Community {uuid: $uuid})
15
32
  SET n = {uuid: $uuid, name: $name, group_id: $group_id, summary: $summary, created_at: $created_at}
@@ -222,7 +222,7 @@ class EntityNode(Node):
222
222
  async def generate_name_embedding(self, embedder: EmbedderClient):
223
223
  start = time()
224
224
  text = self.name.replace('\n', ' ')
225
- self.name_embedding = await embedder.create(input=[text])
225
+ self.name_embedding = await embedder.create(input_data=[text])
226
226
  end = time()
227
227
  logger.debug(f'embedded {text} in {end - start} ms')
228
228
 
@@ -334,7 +334,7 @@ class CommunityNode(Node):
334
334
  async def generate_name_embedding(self, embedder: EmbedderClient):
335
335
  start = time()
336
336
  text = self.name.replace('\n', ' ')
337
- self.name_embedding = await embedder.create(input=[text])
337
+ self.name_embedding = await embedder.create(input_data=[text])
338
338
  end = time()
339
339
  logger.debug(f'embedded {text} in {end - start} ms')
340
340
 
@@ -66,7 +66,7 @@ async def search(
66
66
  bfs_origin_node_uuids: list[str] | None = None,
67
67
  ) -> SearchResults:
68
68
  start = time()
69
- query_vector = await embedder.create(input=[query.replace('\n', ' ')])
69
+ query_vector = await embedder.create(input_data=[query.replace('\n', ' ')])
70
70
 
71
71
  # if group_ids is empty, set it to None
72
72
  group_ids = group_ids if group_ids else None
@@ -21,9 +21,15 @@ from time import time
21
21
 
22
22
  import numpy as np
23
23
  from neo4j import AsyncDriver, Query
24
+ from typing_extensions import LiteralString
24
25
 
25
26
  from graphiti_core.edges import EntityEdge, get_entity_edge_from_record
26
- from graphiti_core.helpers import DEFAULT_DATABASE, lucene_sanitize, normalize_l2
27
+ from graphiti_core.helpers import (
28
+ DEFAULT_DATABASE,
29
+ USE_PARALLEL_RUNTIME,
30
+ lucene_sanitize,
31
+ normalize_l2,
32
+ )
27
33
  from graphiti_core.nodes import (
28
34
  CommunityNode,
29
35
  EntityNode,
@@ -38,7 +44,7 @@ RELEVANT_SCHEMA_LIMIT = 3
38
44
  DEFAULT_MIN_SCORE = 0.6
39
45
  DEFAULT_MMR_LAMBDA = 0.5
40
46
  MAX_SEARCH_DEPTH = 3
41
- MAX_QUERY_LENGTH = 128
47
+ MAX_QUERY_LENGTH = 32
42
48
 
43
49
 
44
50
  def fulltext_query(query: str, group_ids: list[str] | None = None):
@@ -187,8 +193,11 @@ async def edge_similarity_search(
187
193
  min_score: float = DEFAULT_MIN_SCORE,
188
194
  ) -> list[EntityEdge]:
189
195
  # vector similarity search over embedded facts
190
- query = Query("""
191
- CYPHER runtime = parallel parallelRuntimeSupport=all
196
+ runtime_query: LiteralString = (
197
+ 'CYPHER runtime = parallel parallelRuntimeSupport=all\n' if USE_PARALLEL_RUNTIME else ''
198
+ )
199
+
200
+ query: LiteralString = """
192
201
  MATCH (n:Entity)-[r:RELATES_TO]->(m:Entity)
193
202
  WHERE ($group_ids IS NULL OR r.group_id IN $group_ids)
194
203
  AND ($source_uuid IS NULL OR n.uuid IN [$source_uuid, $target_uuid])
@@ -210,10 +219,10 @@ async def edge_similarity_search(
210
219
  r.invalid_at AS invalid_at
211
220
  ORDER BY score DESC
212
221
  LIMIT $limit
213
- """)
222
+ """
214
223
 
215
224
  records, _, _ = await driver.execute_query(
216
- query,
225
+ runtime_query + query,
217
226
  search_vector=search_vector,
218
227
  source_uuid=source_node_uuid,
219
228
  target_uuid=target_node_uuid,
@@ -318,9 +327,13 @@ async def node_similarity_search(
318
327
  min_score: float = DEFAULT_MIN_SCORE,
319
328
  ) -> list[EntityNode]:
320
329
  # vector similarity search over entity names
330
+ runtime_query: LiteralString = (
331
+ 'CYPHER runtime = parallel parallelRuntimeSupport=all\n' if USE_PARALLEL_RUNTIME else ''
332
+ )
333
+
321
334
  records, _, _ = await driver.execute_query(
322
- """
323
- CYPHER runtime = parallel parallelRuntimeSupport=all
335
+ runtime_query
336
+ + """
324
337
  MATCH (n:Entity)
325
338
  WHERE $group_ids IS NULL OR n.group_id IN $group_ids
326
339
  WITH n, vector.similarity.cosine(n.name_embedding, $search_vector) AS score
@@ -425,23 +438,27 @@ async def community_similarity_search(
425
438
  min_score=DEFAULT_MIN_SCORE,
426
439
  ) -> list[CommunityNode]:
427
440
  # vector similarity search over entity names
441
+ runtime_query: LiteralString = (
442
+ 'CYPHER runtime = parallel parallelRuntimeSupport=all\n' if USE_PARALLEL_RUNTIME else ''
443
+ )
444
+
428
445
  records, _, _ = await driver.execute_query(
429
- """
430
- CYPHER runtime = parallel parallelRuntimeSupport=all
431
- MATCH (comm:Community)
432
- WHERE ($group_ids IS NULL OR comm.group_id IN $group_ids)
433
- WITH comm, vector.similarity.cosine(comm.name_embedding, $search_vector) AS score
434
- WHERE score > $min_score
435
- RETURN
436
- comm.uuid As uuid,
437
- comm.group_id AS group_id,
438
- comm.name AS name,
439
- comm.name_embedding AS name_embedding,
440
- comm.created_at AS created_at,
441
- comm.summary AS summary
442
- ORDER BY score DESC
443
- LIMIT $limit
444
- """,
446
+ runtime_query
447
+ + """
448
+ MATCH (comm:Community)
449
+ WHERE ($group_ids IS NULL OR comm.group_id IN $group_ids)
450
+ WITH comm, vector.similarity.cosine(comm.name_embedding, $search_vector) AS score
451
+ WHERE score > $min_score
452
+ RETURN
453
+ comm.uuid As uuid,
454
+ comm.group_id AS group_id,
455
+ comm.name AS name,
456
+ comm.name_embedding AS name_embedding,
457
+ comm.created_at AS created_at,
458
+ comm.summary AS summary
459
+ ORDER BY score DESC
460
+ LIMIT $limit
461
+ """,
445
462
  search_vector=search_vector,
446
463
  group_ids=group_ids,
447
464
  limit=limit,
@@ -21,12 +21,20 @@ from collections import defaultdict
21
21
  from datetime import datetime
22
22
  from math import ceil
23
23
 
24
- from neo4j import AsyncDriver
24
+ from neo4j import AsyncDriver, AsyncManagedTransaction
25
25
  from numpy import dot, sqrt
26
26
  from pydantic import BaseModel
27
27
 
28
28
  from graphiti_core.edges import Edge, EntityEdge, EpisodicEdge
29
29
  from graphiti_core.llm_client import LLMClient
30
+ from graphiti_core.models.edges.edge_db_queries import (
31
+ ENTITY_EDGE_SAVE_BULK,
32
+ EPISODIC_EDGE_SAVE_BULK,
33
+ )
34
+ from graphiti_core.models.nodes.node_db_queries import (
35
+ ENTITY_NODE_SAVE_BULK,
36
+ EPISODIC_NODE_SAVE_BULK,
37
+ )
30
38
  from graphiti_core.nodes import EntityNode, EpisodeType, EpisodicNode
31
39
  from graphiti_core.search.search_utils import get_relevant_edges, get_relevant_nodes
32
40
  from graphiti_core.utils import retrieve_episodes
@@ -75,6 +83,35 @@ async def retrieve_previous_episodes_bulk(
75
83
  return episode_tuples
76
84
 
77
85
 
86
+ async def add_nodes_and_edges_bulk(
87
+ driver: AsyncDriver,
88
+ episodic_nodes: list[EpisodicNode],
89
+ episodic_edges: list[EpisodicEdge],
90
+ entity_nodes: list[EntityNode],
91
+ entity_edges: list[EntityEdge],
92
+ ):
93
+ async with driver.session() as session:
94
+ await session.execute_write(
95
+ add_nodes_and_edges_bulk_tx, episodic_nodes, episodic_edges, entity_nodes, entity_edges
96
+ )
97
+
98
+
99
+ async def add_nodes_and_edges_bulk_tx(
100
+ tx: AsyncManagedTransaction,
101
+ episodic_nodes: list[EpisodicNode],
102
+ episodic_edges: list[EpisodicEdge],
103
+ entity_nodes: list[EntityNode],
104
+ entity_edges: list[EntityEdge],
105
+ ):
106
+ episodes = [dict(episode) for episode in episodic_nodes]
107
+ for episode in episodes:
108
+ episode['source'] = str(episode['source'].value)
109
+ await tx.run(EPISODIC_NODE_SAVE_BULK, episodes=episodes)
110
+ await tx.run(ENTITY_NODE_SAVE_BULK, nodes=[dict(entity) for entity in entity_nodes])
111
+ await tx.run(EPISODIC_EDGE_SAVE_BULK, episodic_edges=[dict(edge) for edge in episodic_edges])
112
+ await tx.run(ENTITY_EDGE_SAVE_BULK, entity_edges=[dict(edge) for edge in entity_edges])
113
+
114
+
78
115
  async def extract_nodes_and_edges_bulk(
79
116
  llm_client: LLMClient, episode_tuples: list[tuple[EpisodicNode, list[EpisodicNode]]]
80
117
  ) -> tuple[list[EntityNode], list[EntityEdge], list[EpisodicEdge]]:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "graphiti-core"
3
- version = "0.3.18"
3
+ version = "0.3.20"
4
4
  description = "A temporal graph building library"
5
5
  authors = [
6
6
  "Paul Paliychuk <paul@getzep.com>",
File without changes