graphiti-core 0.17.4__py3-none-any.whl → 0.25.3__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.
Files changed (59) hide show
  1. graphiti_core/cross_encoder/gemini_reranker_client.py +1 -1
  2. graphiti_core/cross_encoder/openai_reranker_client.py +1 -1
  3. graphiti_core/decorators.py +110 -0
  4. graphiti_core/driver/driver.py +62 -2
  5. graphiti_core/driver/falkordb_driver.py +215 -23
  6. graphiti_core/driver/graph_operations/graph_operations.py +191 -0
  7. graphiti_core/driver/kuzu_driver.py +182 -0
  8. graphiti_core/driver/neo4j_driver.py +70 -8
  9. graphiti_core/driver/neptune_driver.py +305 -0
  10. graphiti_core/driver/search_interface/search_interface.py +89 -0
  11. graphiti_core/edges.py +264 -132
  12. graphiti_core/embedder/azure_openai.py +10 -3
  13. graphiti_core/embedder/client.py +2 -1
  14. graphiti_core/graph_queries.py +114 -101
  15. graphiti_core/graphiti.py +635 -260
  16. graphiti_core/graphiti_types.py +2 -0
  17. graphiti_core/helpers.py +37 -15
  18. graphiti_core/llm_client/anthropic_client.py +142 -52
  19. graphiti_core/llm_client/azure_openai_client.py +57 -19
  20. graphiti_core/llm_client/client.py +83 -21
  21. graphiti_core/llm_client/config.py +1 -1
  22. graphiti_core/llm_client/gemini_client.py +75 -57
  23. graphiti_core/llm_client/openai_base_client.py +92 -48
  24. graphiti_core/llm_client/openai_client.py +39 -9
  25. graphiti_core/llm_client/openai_generic_client.py +91 -56
  26. graphiti_core/models/edges/edge_db_queries.py +259 -35
  27. graphiti_core/models/nodes/node_db_queries.py +311 -32
  28. graphiti_core/nodes.py +388 -164
  29. graphiti_core/prompts/dedupe_edges.py +42 -31
  30. graphiti_core/prompts/dedupe_nodes.py +56 -39
  31. graphiti_core/prompts/eval.py +4 -4
  32. graphiti_core/prompts/extract_edges.py +24 -15
  33. graphiti_core/prompts/extract_nodes.py +76 -35
  34. graphiti_core/prompts/prompt_helpers.py +39 -0
  35. graphiti_core/prompts/snippets.py +29 -0
  36. graphiti_core/prompts/summarize_nodes.py +23 -25
  37. graphiti_core/search/search.py +154 -74
  38. graphiti_core/search/search_config.py +39 -4
  39. graphiti_core/search/search_filters.py +110 -31
  40. graphiti_core/search/search_helpers.py +5 -6
  41. graphiti_core/search/search_utils.py +1360 -473
  42. graphiti_core/tracer.py +193 -0
  43. graphiti_core/utils/bulk_utils.py +216 -90
  44. graphiti_core/utils/content_chunking.py +702 -0
  45. graphiti_core/utils/datetime_utils.py +13 -0
  46. graphiti_core/utils/maintenance/community_operations.py +62 -38
  47. graphiti_core/utils/maintenance/dedup_helpers.py +262 -0
  48. graphiti_core/utils/maintenance/edge_operations.py +306 -156
  49. graphiti_core/utils/maintenance/graph_data_operations.py +44 -74
  50. graphiti_core/utils/maintenance/node_operations.py +466 -206
  51. graphiti_core/utils/maintenance/temporal_operations.py +11 -3
  52. graphiti_core/utils/ontology_utils/entity_types_utils.py +1 -1
  53. graphiti_core/utils/text_utils.py +53 -0
  54. {graphiti_core-0.17.4.dist-info → graphiti_core-0.25.3.dist-info}/METADATA +221 -87
  55. graphiti_core-0.25.3.dist-info/RECORD +87 -0
  56. {graphiti_core-0.17.4.dist-info → graphiti_core-0.25.3.dist-info}/WHEEL +1 -1
  57. graphiti_core-0.17.4.dist-info/RECORD +0 -77
  58. /graphiti_core/{utils/maintenance/utils.py → migrations/__init__.py} +0 -0
  59. {graphiti_core-0.17.4.dist-info → graphiti_core-0.25.3.dist-info}/licenses/LICENSE +0 -0
@@ -20,6 +20,8 @@ from typing import Any
20
20
 
21
21
  from pydantic import BaseModel, Field
22
22
 
23
+ from graphiti_core.driver.driver import GraphProvider
24
+
23
25
 
24
26
  class ComparisonOperator(Enum):
25
27
  equals = '='
@@ -28,15 +30,28 @@ class ComparisonOperator(Enum):
28
30
  less_than = '<'
29
31
  greater_than_equal = '>='
30
32
  less_than_equal = '<='
33
+ is_null = 'IS NULL'
34
+ is_not_null = 'IS NOT NULL'
31
35
 
32
36
 
33
37
  class DateFilter(BaseModel):
34
- date: datetime = Field(description='A datetime to filter on')
38
+ date: datetime | None = Field(default=None, description='A datetime to filter on')
35
39
  comparison_operator: ComparisonOperator = Field(
36
40
  description='Comparison operator for date filter'
37
41
  )
38
42
 
39
43
 
44
+ class PropertyFilter(BaseModel):
45
+ property_name: str = Field(description='Property name')
46
+ property_value: str | int | float | None = Field(
47
+ default=None,
48
+ description='Value you want to match on for the property'
49
+ )
50
+ comparison_operator: ComparisonOperator = Field(
51
+ description='Comparison operator for the property'
52
+ )
53
+
54
+
40
55
  class SearchFilters(BaseModel):
41
56
  node_labels: list[str] | None = Field(
42
57
  default=None, description='List of node labels to filter on'
@@ -48,47 +63,93 @@ class SearchFilters(BaseModel):
48
63
  invalid_at: list[list[DateFilter]] | None = Field(default=None)
49
64
  created_at: list[list[DateFilter]] | None = Field(default=None)
50
65
  expired_at: list[list[DateFilter]] | None = Field(default=None)
66
+ edge_uuids: list[str] | None = Field(default=None)
67
+ property_filters: list[PropertyFilter] | None = Field(default=None)
68
+
69
+
70
+ def cypher_to_opensearch_operator(op: ComparisonOperator) -> str:
71
+ mapping = {
72
+ ComparisonOperator.greater_than: 'gt',
73
+ ComparisonOperator.less_than: 'lt',
74
+ ComparisonOperator.greater_than_equal: 'gte',
75
+ ComparisonOperator.less_than_equal: 'lte',
76
+ }
77
+ return mapping.get(op, op.value)
51
78
 
52
79
 
53
80
  def node_search_filter_query_constructor(
54
81
  filters: SearchFilters,
55
- ) -> tuple[str, dict[str, Any]]:
56
- filter_query: str = ''
82
+ provider: GraphProvider,
83
+ ) -> tuple[list[str], dict[str, Any]]:
84
+ filter_queries: list[str] = []
57
85
  filter_params: dict[str, Any] = {}
58
86
 
59
87
  if filters.node_labels is not None:
60
- node_labels = '|'.join(filters.node_labels)
61
- node_label_filter = ' AND n:' + node_labels
62
- filter_query += node_label_filter
88
+ if provider == GraphProvider.KUZU:
89
+ node_label_filter = 'list_has_all(n.labels, $labels)'
90
+ filter_params['labels'] = filters.node_labels
91
+ else:
92
+ node_labels = '|'.join(filters.node_labels)
93
+ node_label_filter = 'n:' + node_labels
94
+ filter_queries.append(node_label_filter)
95
+
96
+ return filter_queries, filter_params
97
+
63
98
 
64
- return filter_query, filter_params
99
+ def date_filter_query_constructor(
100
+ value_name: str, param_name: str, operator: ComparisonOperator
101
+ ) -> str:
102
+ query = '(' + value_name + ' '
103
+
104
+ if operator == ComparisonOperator.is_null or operator == ComparisonOperator.is_not_null:
105
+ query += operator.value + ')'
106
+ else:
107
+ query += operator.value + ' ' + param_name + ')'
108
+
109
+ return query
65
110
 
66
111
 
67
112
  def edge_search_filter_query_constructor(
68
113
  filters: SearchFilters,
69
- ) -> tuple[str, dict[str, Any]]:
70
- filter_query: str = ''
114
+ provider: GraphProvider,
115
+ ) -> tuple[list[str], dict[str, Any]]:
116
+ filter_queries: list[str] = []
71
117
  filter_params: dict[str, Any] = {}
72
118
 
73
119
  if filters.edge_types is not None:
74
120
  edge_types = filters.edge_types
75
- edge_types_filter = '\nAND r.name in $edge_types'
76
- filter_query += edge_types_filter
121
+ filter_queries.append('e.name in $edge_types')
77
122
  filter_params['edge_types'] = edge_types
78
123
 
124
+ if filters.edge_uuids is not None:
125
+ filter_queries.append('e.uuid in $edge_uuids')
126
+ filter_params['edge_uuids'] = filters.edge_uuids
127
+
79
128
  if filters.node_labels is not None:
80
- node_labels = '|'.join(filters.node_labels)
81
- node_label_filter = '\nAND n:' + node_labels + ' AND m:' + node_labels
82
- filter_query += node_label_filter
129
+ if provider == GraphProvider.KUZU:
130
+ node_label_filter = (
131
+ 'list_has_all(n.labels, $labels) AND list_has_all(m.labels, $labels)'
132
+ )
133
+ filter_params['labels'] = filters.node_labels
134
+ else:
135
+ node_labels = '|'.join(filters.node_labels)
136
+ node_label_filter = 'n:' + node_labels + ' AND m:' + node_labels
137
+ filter_queries.append(node_label_filter)
83
138
 
84
139
  if filters.valid_at is not None:
85
- valid_at_filter = '\nAND ('
140
+ valid_at_filter = '('
86
141
  for i, or_list in enumerate(filters.valid_at):
87
142
  for j, date_filter in enumerate(or_list):
88
- filter_params['valid_at_' + str(j)] = date_filter.date
143
+ if date_filter.comparison_operator not in [
144
+ ComparisonOperator.is_null,
145
+ ComparisonOperator.is_not_null,
146
+ ]:
147
+ filter_params['valid_at_' + str(j)] = date_filter.date
89
148
 
90
149
  and_filters = [
91
- '(r.valid_at ' + date_filter.comparison_operator.value + f' $valid_at_{j})'
150
+ date_filter_query_constructor(
151
+ 'e.valid_at', f'$valid_at_{j}', date_filter.comparison_operator
152
+ )
92
153
  for j, date_filter in enumerate(or_list)
93
154
  ]
94
155
  and_filter_query = ''
@@ -104,16 +165,22 @@ def edge_search_filter_query_constructor(
104
165
  else:
105
166
  valid_at_filter += ' OR '
106
167
 
107
- filter_query += valid_at_filter
168
+ filter_queries.append(valid_at_filter)
108
169
 
109
170
  if filters.invalid_at is not None:
110
- invalid_at_filter = ' AND ('
171
+ invalid_at_filter = '('
111
172
  for i, or_list in enumerate(filters.invalid_at):
112
173
  for j, date_filter in enumerate(or_list):
113
- filter_params['invalid_at_' + str(j)] = date_filter.date
174
+ if date_filter.comparison_operator not in [
175
+ ComparisonOperator.is_null,
176
+ ComparisonOperator.is_not_null,
177
+ ]:
178
+ filter_params['invalid_at_' + str(j)] = date_filter.date
114
179
 
115
180
  and_filters = [
116
- '(r.invalid_at ' + date_filter.comparison_operator.value + f' $invalid_at_{j})'
181
+ date_filter_query_constructor(
182
+ 'e.invalid_at', f'$invalid_at_{j}', date_filter.comparison_operator
183
+ )
117
184
  for j, date_filter in enumerate(or_list)
118
185
  ]
119
186
  and_filter_query = ''
@@ -129,16 +196,22 @@ def edge_search_filter_query_constructor(
129
196
  else:
130
197
  invalid_at_filter += ' OR '
131
198
 
132
- filter_query += invalid_at_filter
199
+ filter_queries.append(invalid_at_filter)
133
200
 
134
201
  if filters.created_at is not None:
135
- created_at_filter = ' AND ('
202
+ created_at_filter = '('
136
203
  for i, or_list in enumerate(filters.created_at):
137
204
  for j, date_filter in enumerate(or_list):
138
- filter_params['created_at_' + str(j)] = date_filter.date
205
+ if date_filter.comparison_operator not in [
206
+ ComparisonOperator.is_null,
207
+ ComparisonOperator.is_not_null,
208
+ ]:
209
+ filter_params['created_at_' + str(j)] = date_filter.date
139
210
 
140
211
  and_filters = [
141
- '(r.created_at ' + date_filter.comparison_operator.value + f' $created_at_{j})'
212
+ date_filter_query_constructor(
213
+ 'e.created_at', f'$created_at_{j}', date_filter.comparison_operator
214
+ )
142
215
  for j, date_filter in enumerate(or_list)
143
216
  ]
144
217
  and_filter_query = ''
@@ -154,16 +227,22 @@ def edge_search_filter_query_constructor(
154
227
  else:
155
228
  created_at_filter += ' OR '
156
229
 
157
- filter_query += created_at_filter
230
+ filter_queries.append(created_at_filter)
158
231
 
159
232
  if filters.expired_at is not None:
160
- expired_at_filter = ' AND ('
233
+ expired_at_filter = '('
161
234
  for i, or_list in enumerate(filters.expired_at):
162
235
  for j, date_filter in enumerate(or_list):
163
- filter_params['expired_at_' + str(j)] = date_filter.date
236
+ if date_filter.comparison_operator not in [
237
+ ComparisonOperator.is_null,
238
+ ComparisonOperator.is_not_null,
239
+ ]:
240
+ filter_params['expired_at_' + str(j)] = date_filter.date
164
241
 
165
242
  and_filters = [
166
- '(r.expired_at ' + date_filter.comparison_operator.value + f' $expired_at_{j})'
243
+ date_filter_query_constructor(
244
+ 'e.expired_at', f'$expired_at_{j}', date_filter.comparison_operator
245
+ )
167
246
  for j, date_filter in enumerate(or_list)
168
247
  ]
169
248
  and_filter_query = ''
@@ -179,6 +258,6 @@ def edge_search_filter_query_constructor(
179
258
  else:
180
259
  expired_at_filter += ' OR '
181
260
 
182
- filter_query += expired_at_filter
261
+ filter_queries.append(expired_at_filter)
183
262
 
184
- return filter_query, filter_params
263
+ return filter_queries, filter_params
@@ -14,9 +14,8 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
16
 
17
- import json
18
-
19
17
  from graphiti_core.edges import EntityEdge
18
+ from graphiti_core.prompts.prompt_helpers import to_prompt_json
20
19
  from graphiti_core.search.search_config import SearchResults
21
20
 
22
21
 
@@ -57,16 +56,16 @@ def search_results_to_context_string(search_results: SearchResults) -> str:
57
56
  These are the most relevant facts and their valid and invalid dates. Facts are considered valid
58
57
  between their valid_at and invalid_at dates. Facts with an invalid_at date of "Present" are considered valid.
59
58
  <FACTS>
60
- {json.dumps(fact_json, indent=12)}
59
+ {to_prompt_json(fact_json)}
61
60
  </FACTS>
62
61
  <ENTITIES>
63
- {json.dumps(entity_json, indent=12)}
62
+ {to_prompt_json(entity_json)}
64
63
  </ENTITIES>
65
64
  <EPISODES>
66
- {json.dumps(episode_json, indent=12)}
65
+ {to_prompt_json(episode_json)}
67
66
  </EPISODES>
68
67
  <COMMUNITIES>
69
- {json.dumps(community_json, indent=12)}
68
+ {to_prompt_json(community_json)}
70
69
  </COMMUNITIES>
71
70
  """
72
71