graphiti-core 0.7.0__tar.gz → 0.7.1__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.

Files changed (61) hide show
  1. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/PKG-INFO +1 -1
  2. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/graphiti.py +6 -3
  3. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/search/search.py +10 -4
  4. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/search/search_filters.py +23 -4
  5. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/search/search_utils.py +55 -20
  6. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/utils/bulk_utils.py +2 -1
  7. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/pyproject.toml +1 -1
  8. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/LICENSE +0 -0
  9. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/README.md +0 -0
  10. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/__init__.py +0 -0
  11. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/cross_encoder/__init__.py +0 -0
  12. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/cross_encoder/bge_reranker_client.py +0 -0
  13. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/cross_encoder/client.py +0 -0
  14. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/cross_encoder/openai_reranker_client.py +0 -0
  15. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/edges.py +0 -0
  16. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/embedder/__init__.py +0 -0
  17. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/embedder/client.py +0 -0
  18. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/embedder/openai.py +0 -0
  19. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/embedder/voyage.py +0 -0
  20. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/errors.py +0 -0
  21. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/helpers.py +0 -0
  22. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/llm_client/__init__.py +0 -0
  23. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/llm_client/anthropic_client.py +0 -0
  24. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/llm_client/client.py +0 -0
  25. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/llm_client/config.py +0 -0
  26. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/llm_client/errors.py +0 -0
  27. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/llm_client/groq_client.py +0 -0
  28. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/llm_client/openai_client.py +0 -0
  29. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/llm_client/openai_generic_client.py +0 -0
  30. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/llm_client/utils.py +0 -0
  31. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/models/__init__.py +0 -0
  32. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/models/edges/__init__.py +0 -0
  33. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/models/edges/edge_db_queries.py +0 -0
  34. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/models/nodes/__init__.py +0 -0
  35. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/models/nodes/node_db_queries.py +0 -0
  36. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/nodes.py +0 -0
  37. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/__init__.py +0 -0
  38. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/dedupe_edges.py +0 -0
  39. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/dedupe_nodes.py +0 -0
  40. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/eval.py +0 -0
  41. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/extract_edge_dates.py +0 -0
  42. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/extract_edges.py +0 -0
  43. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/extract_nodes.py +0 -0
  44. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/invalidate_edges.py +0 -0
  45. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/lib.py +0 -0
  46. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/models.py +0 -0
  47. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/prompt_helpers.py +0 -0
  48. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/prompts/summarize_nodes.py +0 -0
  49. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/py.typed +0 -0
  50. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/search/__init__.py +0 -0
  51. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/search/search_config.py +0 -0
  52. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/search/search_config_recipes.py +0 -0
  53. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/utils/__init__.py +0 -0
  54. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/utils/datetime_utils.py +0 -0
  55. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/utils/maintenance/__init__.py +0 -0
  56. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/utils/maintenance/community_operations.py +0 -0
  57. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/utils/maintenance/edge_operations.py +0 -0
  58. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/utils/maintenance/graph_data_operations.py +0 -0
  59. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/utils/maintenance/node_operations.py +0 -0
  60. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/graphiti_core/utils/maintenance/temporal_operations.py +0 -0
  61. {graphiti_core-0.7.0 → graphiti_core-0.7.1}/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.7.0
3
+ Version: 0.7.1
4
4
  Summary: A temporal graph building library
5
5
  License: Apache-2.0
6
6
  Author: Paul Paliychuk
@@ -351,7 +351,10 @@ class Graphiti:
351
351
  # Find relevant nodes already in the graph
352
352
  existing_nodes_lists: list[list[EntityNode]] = list(
353
353
  await semaphore_gather(
354
- *[get_relevant_nodes(self.driver, [node]) for node in extracted_nodes]
354
+ *[
355
+ get_relevant_nodes(self.driver, SearchFilters(), [node])
356
+ for node in extracted_nodes
357
+ ]
355
358
  )
356
359
  )
357
360
 
@@ -732,8 +735,8 @@ class Graphiti:
732
735
  self.llm_client,
733
736
  [source_node, target_node],
734
737
  [
735
- await get_relevant_nodes(self.driver, [source_node]),
736
- await get_relevant_nodes(self.driver, [target_node]),
738
+ await get_relevant_nodes(self.driver, SearchFilters(), [source_node]),
739
+ await get_relevant_nodes(self.driver, SearchFilters(), [target_node]),
737
740
  ],
738
741
  )
739
742
 
@@ -100,6 +100,7 @@ async def search(
100
100
  query_vector,
101
101
  group_ids,
102
102
  config.node_config,
103
+ search_filter,
103
104
  center_node_uuid,
104
105
  bfs_origin_node_uuids,
105
106
  config.limit,
@@ -233,6 +234,7 @@ async def node_search(
233
234
  query_vector: list[float],
234
235
  group_ids: list[str] | None,
235
236
  config: NodeSearchConfig | None,
237
+ search_filter: SearchFilters,
236
238
  center_node_uuid: str | None = None,
237
239
  bfs_origin_node_uuids: list[str] | None = None,
238
240
  limit=DEFAULT_SEARCH_LIMIT,
@@ -243,11 +245,13 @@ async def node_search(
243
245
  search_results: list[list[EntityNode]] = list(
244
246
  await semaphore_gather(
245
247
  *[
246
- node_fulltext_search(driver, query, group_ids, 2 * limit),
248
+ node_fulltext_search(driver, query, search_filter, group_ids, 2 * limit),
247
249
  node_similarity_search(
248
- driver, query_vector, group_ids, 2 * limit, config.sim_min_score
250
+ driver, query_vector, search_filter, group_ids, 2 * limit, config.sim_min_score
251
+ ),
252
+ node_bfs_search(
253
+ driver, bfs_origin_node_uuids, search_filter, config.bfs_max_depth, 2 * limit
249
254
  ),
250
- node_bfs_search(driver, bfs_origin_node_uuids, config.bfs_max_depth, 2 * limit),
251
255
  ]
252
256
  )
253
257
  )
@@ -255,7 +259,9 @@ async def node_search(
255
259
  if NodeSearchMethod.bfs in config.search_methods and bfs_origin_node_uuids is None:
256
260
  origin_node_uuids = [node.uuid for result in search_results for node in result]
257
261
  search_results.append(
258
- await node_bfs_search(driver, origin_node_uuids, config.bfs_max_depth, 2 * limit)
262
+ await node_bfs_search(
263
+ driver, origin_node_uuids, search_filter, config.bfs_max_depth, 2 * limit
264
+ )
259
265
  )
260
266
 
261
267
  search_result_uuids = [[node.uuid for node in result] for result in search_results]
@@ -39,18 +39,37 @@ class DateFilter(BaseModel):
39
39
 
40
40
 
41
41
  class SearchFilters(BaseModel):
42
+ node_labels: list[str] | None = Field(
43
+ default=None, description='List of node labels to filter on'
44
+ )
42
45
  valid_at: list[list[DateFilter]] | None = Field(default=None)
43
46
  invalid_at: list[list[DateFilter]] | None = Field(default=None)
44
47
  created_at: list[list[DateFilter]] | None = Field(default=None)
45
48
  expired_at: list[list[DateFilter]] | None = Field(default=None)
46
49
 
47
50
 
48
- def search_filter_query_constructor(filters: SearchFilters) -> tuple[LiteralString, dict[str, Any]]:
51
+ def node_search_filter_query_constructor(
52
+ filters: SearchFilters,
53
+ ) -> tuple[LiteralString, dict[str, Any]]:
54
+ filter_query: LiteralString = ''
55
+ filter_params: dict[str, Any] = {}
56
+
57
+ if filters.node_labels is not None:
58
+ node_labels = ':'.join(filters.node_labels)
59
+ node_label_filter = ' AND n:' + node_labels
60
+ filter_query += node_label_filter
61
+
62
+ return filter_query, filter_params
63
+
64
+
65
+ def edge_search_filter_query_constructor(
66
+ filters: SearchFilters,
67
+ ) -> tuple[LiteralString, dict[str, Any]]:
49
68
  filter_query: LiteralString = ''
50
69
  filter_params: dict[str, Any] = {}
51
70
 
52
71
  if filters.valid_at is not None:
53
- valid_at_filter = 'AND ('
72
+ valid_at_filter = ' AND ('
54
73
  for i, or_list in enumerate(filters.valid_at):
55
74
  for j, date_filter in enumerate(or_list):
56
75
  filter_params['valid_at_' + str(j)] = date_filter.date
@@ -75,7 +94,7 @@ def search_filter_query_constructor(filters: SearchFilters) -> tuple[LiteralStri
75
94
  filter_query += valid_at_filter
76
95
 
77
96
  if filters.invalid_at is not None:
78
- invalid_at_filter = 'AND ('
97
+ invalid_at_filter = ' AND ('
79
98
  for i, or_list in enumerate(filters.invalid_at):
80
99
  for j, date_filter in enumerate(or_list):
81
100
  filter_params['invalid_at_' + str(j)] = date_filter.date
@@ -100,7 +119,7 @@ def search_filter_query_constructor(filters: SearchFilters) -> tuple[LiteralStri
100
119
  filter_query += invalid_at_filter
101
120
 
102
121
  if filters.created_at is not None:
103
- created_at_filter = 'AND ('
122
+ created_at_filter = ' AND ('
104
123
  for i, or_list in enumerate(filters.created_at):
105
124
  for j, date_filter in enumerate(or_list):
106
125
  filter_params['created_at_' + str(j)] = date_filter.date
@@ -38,7 +38,11 @@ from graphiti_core.nodes import (
38
38
  get_community_node_from_record,
39
39
  get_entity_node_from_record,
40
40
  )
41
- from graphiti_core.search.search_filters import SearchFilters, search_filter_query_constructor
41
+ from graphiti_core.search.search_filters import (
42
+ SearchFilters,
43
+ edge_search_filter_query_constructor,
44
+ node_search_filter_query_constructor,
45
+ )
42
46
 
43
47
  logger = logging.getLogger(__name__)
44
48
 
@@ -148,7 +152,7 @@ async def edge_fulltext_search(
148
152
  if fuzzy_query == '':
149
153
  return []
150
154
 
151
- filter_query, filter_params = search_filter_query_constructor(search_filter)
155
+ filter_query, filter_params = edge_search_filter_query_constructor(search_filter)
152
156
 
153
157
  cypher_query = Query(
154
158
  """
@@ -207,7 +211,7 @@ async def edge_similarity_search(
207
211
 
208
212
  query_params: dict[str, Any] = {}
209
213
 
210
- filter_query, filter_params = search_filter_query_constructor(search_filter)
214
+ filter_query, filter_params = edge_search_filter_query_constructor(search_filter)
211
215
  query_params.update(filter_params)
212
216
 
213
217
  group_filter_query: LiteralString = ''
@@ -225,8 +229,8 @@ async def edge_similarity_search(
225
229
 
226
230
  query: LiteralString = (
227
231
  """
228
- MATCH (n:Entity)-[r:RELATES_TO]->(m:Entity)
229
- """
232
+ MATCH (n:Entity)-[r:RELATES_TO]->(m:Entity)
233
+ """
230
234
  + group_filter_query
231
235
  + filter_query
232
236
  + """\nWITH DISTINCT r, vector.similarity.cosine(r.fact_embedding, $search_vector) AS score
@@ -278,7 +282,7 @@ async def edge_bfs_search(
278
282
  if bfs_origin_node_uuids is None:
279
283
  return []
280
284
 
281
- filter_query, filter_params = search_filter_query_constructor(search_filter)
285
+ filter_query, filter_params = edge_search_filter_query_constructor(search_filter)
282
286
 
283
287
  query = Query(
284
288
  """
@@ -325,6 +329,7 @@ async def edge_bfs_search(
325
329
  async def node_fulltext_search(
326
330
  driver: AsyncDriver,
327
331
  query: str,
332
+ search_filter: SearchFilters,
328
333
  group_ids: list[str] | None = None,
329
334
  limit=RELEVANT_SCHEMA_LIMIT,
330
335
  ) -> list[EntityNode]:
@@ -333,10 +338,17 @@ async def node_fulltext_search(
333
338
  if fuzzy_query == '':
334
339
  return []
335
340
 
341
+ filter_query, filter_params = node_search_filter_query_constructor(search_filter)
342
+
336
343
  records, _, _ = await driver.execute_query(
337
344
  """
338
345
  CALL db.index.fulltext.queryNodes("node_name_and_summary", $query, {limit: $limit})
339
- YIELD node AS n, score
346
+ YIELD node AS node, score
347
+ MATCH (n:Entity)
348
+ WHERE n.uuid = node.uuid
349
+ """
350
+ + filter_query
351
+ + """
340
352
  RETURN
341
353
  n.uuid AS uuid,
342
354
  n.group_id AS group_id,
@@ -349,6 +361,7 @@ async def node_fulltext_search(
349
361
  ORDER BY score DESC
350
362
  LIMIT $limit
351
363
  """,
364
+ filter_params,
352
365
  query=fuzzy_query,
353
366
  group_ids=group_ids,
354
367
  limit=limit,
@@ -363,6 +376,7 @@ async def node_fulltext_search(
363
376
  async def node_similarity_search(
364
377
  driver: AsyncDriver,
365
378
  search_vector: list[float],
379
+ search_filter: SearchFilters,
366
380
  group_ids: list[str] | None = None,
367
381
  limit=RELEVANT_SCHEMA_LIMIT,
368
382
  min_score: float = DEFAULT_MIN_SCORE,
@@ -379,12 +393,16 @@ async def node_similarity_search(
379
393
  group_filter_query += 'WHERE n.group_id IN $group_ids'
380
394
  query_params['group_ids'] = group_ids
381
395
 
396
+ filter_query, filter_params = node_search_filter_query_constructor(search_filter)
397
+ query_params.update(filter_params)
398
+
382
399
  records, _, _ = await driver.execute_query(
383
400
  runtime_query
384
401
  + """
385
402
  MATCH (n:Entity)
386
403
  """
387
404
  + group_filter_query
405
+ + filter_query
388
406
  + """
389
407
  WITH n, vector.similarity.cosine(n.name_embedding, $search_vector) AS score
390
408
  WHERE score > $min_score
@@ -416,6 +434,7 @@ async def node_similarity_search(
416
434
  async def node_bfs_search(
417
435
  driver: AsyncDriver,
418
436
  bfs_origin_node_uuids: list[str] | None,
437
+ search_filter: SearchFilters,
419
438
  bfs_max_depth: int,
420
439
  limit: int,
421
440
  ) -> list[EntityNode]:
@@ -423,21 +442,28 @@ async def node_bfs_search(
423
442
  if bfs_origin_node_uuids is None:
424
443
  return []
425
444
 
445
+ filter_query, filter_params = node_search_filter_query_constructor(search_filter)
446
+
426
447
  records, _, _ = await driver.execute_query(
427
448
  """
428
449
  UNWIND $bfs_origin_node_uuids AS origin_uuid
429
450
  MATCH (origin:Entity|Episodic {uuid: origin_uuid})-[:RELATES_TO|MENTIONS]->{1,3}(n:Entity)
430
- RETURN DISTINCT
431
- n.uuid As uuid,
432
- n.group_id AS group_id,
433
- n.name AS name,
434
- n.name_embedding AS name_embedding,
435
- n.created_at AS created_at,
436
- n.summary AS summary,
437
- labels(n) AS labels,
438
- properties(n) AS attributes
439
- LIMIT $limit
440
- """,
451
+ WHERE n.group_id = origin.group_id
452
+ """
453
+ + filter_query
454
+ + """
455
+ RETURN DISTINCT
456
+ n.uuid As uuid,
457
+ n.group_id AS group_id,
458
+ n.name AS name,
459
+ n.name_embedding AS name_embedding,
460
+ n.created_at AS created_at,
461
+ n.summary AS summary,
462
+ labels(n) AS labels,
463
+ properties(n) AS attributes
464
+ LIMIT $limit
465
+ """,
466
+ filter_params,
441
467
  bfs_origin_node_uuids=bfs_origin_node_uuids,
442
468
  depth=bfs_max_depth,
443
469
  limit=limit,
@@ -539,6 +565,7 @@ async def hybrid_node_search(
539
565
  queries: list[str],
540
566
  embeddings: list[list[float]],
541
567
  driver: AsyncDriver,
568
+ search_filter: SearchFilters,
542
569
  group_ids: list[str] | None = None,
543
570
  limit: int = RELEVANT_SCHEMA_LIMIT,
544
571
  ) -> list[EntityNode]:
@@ -583,8 +610,14 @@ async def hybrid_node_search(
583
610
  start = time()
584
611
  results: list[list[EntityNode]] = list(
585
612
  await semaphore_gather(
586
- *[node_fulltext_search(driver, q, group_ids, 2 * limit) for q in queries],
587
- *[node_similarity_search(driver, e, group_ids, 2 * limit) for e in embeddings],
613
+ *[
614
+ node_fulltext_search(driver, q, search_filter, group_ids, 2 * limit)
615
+ for q in queries
616
+ ],
617
+ *[
618
+ node_similarity_search(driver, e, search_filter, group_ids, 2 * limit)
619
+ for e in embeddings
620
+ ],
588
621
  )
589
622
  )
590
623
 
@@ -604,6 +637,7 @@ async def hybrid_node_search(
604
637
 
605
638
  async def get_relevant_nodes(
606
639
  driver: AsyncDriver,
640
+ search_filter: SearchFilters,
607
641
  nodes: list[EntityNode],
608
642
  ) -> list[EntityNode]:
609
643
  """
@@ -635,6 +669,7 @@ async def get_relevant_nodes(
635
669
  [node.name for node in nodes],
636
670
  [node.name_embedding for node in nodes if node.name_embedding is not None],
637
671
  driver,
672
+ search_filter,
638
673
  [node.group_id for node in nodes],
639
674
  )
640
675
 
@@ -37,6 +37,7 @@ from graphiti_core.models.nodes.node_db_queries import (
37
37
  EPISODIC_NODE_SAVE_BULK,
38
38
  )
39
39
  from graphiti_core.nodes import EntityNode, EpisodeType, EpisodicNode
40
+ from graphiti_core.search.search_filters import SearchFilters
40
41
  from graphiti_core.search.search_utils import get_relevant_edges, get_relevant_nodes
41
42
  from graphiti_core.utils.datetime_utils import utc_now
42
43
  from graphiti_core.utils.maintenance.edge_operations import (
@@ -188,7 +189,7 @@ async def dedupe_nodes_bulk(
188
189
 
189
190
  existing_nodes_chunks: list[list[EntityNode]] = list(
190
191
  await semaphore_gather(
191
- *[get_relevant_nodes(driver, node_chunk) for node_chunk in node_chunks]
192
+ *[get_relevant_nodes(driver, SearchFilters(), node_chunk) for node_chunk in node_chunks]
192
193
  )
193
194
  )
194
195
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "graphiti-core"
3
- version = "0.7.0"
3
+ version = "0.7.1"
4
4
  description = "A temporal graph building library"
5
5
  authors = [
6
6
  "Paul Paliychuk <paul@getzep.com>",
File without changes
File without changes