graphiti-core 0.17.4__py3-none-any.whl → 0.24.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 (58) 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 +61 -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 +582 -255
  16. graphiti_core/graphiti_types.py +2 -0
  17. graphiti_core/helpers.py +21 -14
  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 +94 -50
  24. graphiti_core/llm_client/openai_client.py +28 -8
  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 +23 -14
  33. graphiti_core/prompts/extract_nodes.py +73 -32
  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 +109 -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/datetime_utils.py +13 -0
  45. graphiti_core/utils/maintenance/community_operations.py +62 -38
  46. graphiti_core/utils/maintenance/dedup_helpers.py +262 -0
  47. graphiti_core/utils/maintenance/edge_operations.py +286 -126
  48. graphiti_core/utils/maintenance/graph_data_operations.py +44 -74
  49. graphiti_core/utils/maintenance/node_operations.py +320 -158
  50. graphiti_core/utils/maintenance/temporal_operations.py +11 -3
  51. graphiti_core/utils/ontology_utils/entity_types_utils.py +1 -1
  52. graphiti_core/utils/text_utils.py +53 -0
  53. {graphiti_core-0.17.4.dist-info → graphiti_core-0.24.3.dist-info}/METADATA +221 -87
  54. graphiti_core-0.24.3.dist-info/RECORD +86 -0
  55. {graphiti_core-0.17.4.dist-info → graphiti_core-0.24.3.dist-info}/WHEEL +1 -1
  56. graphiti_core-0.17.4.dist-info/RECORD +0 -77
  57. /graphiti_core/{utils/maintenance/utils.py → migrations/__init__.py} +0 -0
  58. {graphiti_core-0.17.4.dist-info → graphiti_core-0.24.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,27 @@ 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(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
+ description='Value you want to match on for the property'
48
+ )
49
+ comparison_operator: ComparisonOperator = Field(
50
+ description='Comparison operator for the property'
51
+ )
52
+
53
+
40
54
  class SearchFilters(BaseModel):
41
55
  node_labels: list[str] | None = Field(
42
56
  default=None, description='List of node labels to filter on'
@@ -48,47 +62,93 @@ class SearchFilters(BaseModel):
48
62
  invalid_at: list[list[DateFilter]] | None = Field(default=None)
49
63
  created_at: list[list[DateFilter]] | None = Field(default=None)
50
64
  expired_at: list[list[DateFilter]] | None = Field(default=None)
65
+ edge_uuids: list[str] | None = Field(default=None)
66
+ property_filters: list[PropertyFilter] | None = Field(default=None)
67
+
68
+
69
+ def cypher_to_opensearch_operator(op: ComparisonOperator) -> str:
70
+ mapping = {
71
+ ComparisonOperator.greater_than: 'gt',
72
+ ComparisonOperator.less_than: 'lt',
73
+ ComparisonOperator.greater_than_equal: 'gte',
74
+ ComparisonOperator.less_than_equal: 'lte',
75
+ }
76
+ return mapping.get(op, op.value)
51
77
 
52
78
 
53
79
  def node_search_filter_query_constructor(
54
80
  filters: SearchFilters,
55
- ) -> tuple[str, dict[str, Any]]:
56
- filter_query: str = ''
81
+ provider: GraphProvider,
82
+ ) -> tuple[list[str], dict[str, Any]]:
83
+ filter_queries: list[str] = []
57
84
  filter_params: dict[str, Any] = {}
58
85
 
59
86
  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
87
+ if provider == GraphProvider.KUZU:
88
+ node_label_filter = 'list_has_all(n.labels, $labels)'
89
+ filter_params['labels'] = filters.node_labels
90
+ else:
91
+ node_labels = '|'.join(filters.node_labels)
92
+ node_label_filter = 'n:' + node_labels
93
+ filter_queries.append(node_label_filter)
94
+
95
+ return filter_queries, filter_params
96
+
63
97
 
64
- return filter_query, filter_params
98
+ def date_filter_query_constructor(
99
+ value_name: str, param_name: str, operator: ComparisonOperator
100
+ ) -> str:
101
+ query = '(' + value_name + ' '
102
+
103
+ if operator == ComparisonOperator.is_null or operator == ComparisonOperator.is_not_null:
104
+ query += operator.value + ')'
105
+ else:
106
+ query += operator.value + ' ' + param_name + ')'
107
+
108
+ return query
65
109
 
66
110
 
67
111
  def edge_search_filter_query_constructor(
68
112
  filters: SearchFilters,
69
- ) -> tuple[str, dict[str, Any]]:
70
- filter_query: str = ''
113
+ provider: GraphProvider,
114
+ ) -> tuple[list[str], dict[str, Any]]:
115
+ filter_queries: list[str] = []
71
116
  filter_params: dict[str, Any] = {}
72
117
 
73
118
  if filters.edge_types is not None:
74
119
  edge_types = filters.edge_types
75
- edge_types_filter = '\nAND r.name in $edge_types'
76
- filter_query += edge_types_filter
120
+ filter_queries.append('e.name in $edge_types')
77
121
  filter_params['edge_types'] = edge_types
78
122
 
123
+ if filters.edge_uuids is not None:
124
+ filter_queries.append('e.uuid in $edge_uuids')
125
+ filter_params['edge_uuids'] = filters.edge_uuids
126
+
79
127
  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
128
+ if provider == GraphProvider.KUZU:
129
+ node_label_filter = (
130
+ 'list_has_all(n.labels, $labels) AND list_has_all(m.labels, $labels)'
131
+ )
132
+ filter_params['labels'] = filters.node_labels
133
+ else:
134
+ node_labels = '|'.join(filters.node_labels)
135
+ node_label_filter = 'n:' + node_labels + ' AND m:' + node_labels
136
+ filter_queries.append(node_label_filter)
83
137
 
84
138
  if filters.valid_at is not None:
85
- valid_at_filter = '\nAND ('
139
+ valid_at_filter = '('
86
140
  for i, or_list in enumerate(filters.valid_at):
87
141
  for j, date_filter in enumerate(or_list):
88
- filter_params['valid_at_' + str(j)] = date_filter.date
142
+ if date_filter.comparison_operator not in [
143
+ ComparisonOperator.is_null,
144
+ ComparisonOperator.is_not_null,
145
+ ]:
146
+ filter_params['valid_at_' + str(j)] = date_filter.date
89
147
 
90
148
  and_filters = [
91
- '(r.valid_at ' + date_filter.comparison_operator.value + f' $valid_at_{j})'
149
+ date_filter_query_constructor(
150
+ 'e.valid_at', f'$valid_at_{j}', date_filter.comparison_operator
151
+ )
92
152
  for j, date_filter in enumerate(or_list)
93
153
  ]
94
154
  and_filter_query = ''
@@ -104,16 +164,22 @@ def edge_search_filter_query_constructor(
104
164
  else:
105
165
  valid_at_filter += ' OR '
106
166
 
107
- filter_query += valid_at_filter
167
+ filter_queries.append(valid_at_filter)
108
168
 
109
169
  if filters.invalid_at is not None:
110
- invalid_at_filter = ' AND ('
170
+ invalid_at_filter = '('
111
171
  for i, or_list in enumerate(filters.invalid_at):
112
172
  for j, date_filter in enumerate(or_list):
113
- filter_params['invalid_at_' + str(j)] = date_filter.date
173
+ if date_filter.comparison_operator not in [
174
+ ComparisonOperator.is_null,
175
+ ComparisonOperator.is_not_null,
176
+ ]:
177
+ filter_params['invalid_at_' + str(j)] = date_filter.date
114
178
 
115
179
  and_filters = [
116
- '(r.invalid_at ' + date_filter.comparison_operator.value + f' $invalid_at_{j})'
180
+ date_filter_query_constructor(
181
+ 'e.invalid_at', f'$invalid_at_{j}', date_filter.comparison_operator
182
+ )
117
183
  for j, date_filter in enumerate(or_list)
118
184
  ]
119
185
  and_filter_query = ''
@@ -129,16 +195,22 @@ def edge_search_filter_query_constructor(
129
195
  else:
130
196
  invalid_at_filter += ' OR '
131
197
 
132
- filter_query += invalid_at_filter
198
+ filter_queries.append(invalid_at_filter)
133
199
 
134
200
  if filters.created_at is not None:
135
- created_at_filter = ' AND ('
201
+ created_at_filter = '('
136
202
  for i, or_list in enumerate(filters.created_at):
137
203
  for j, date_filter in enumerate(or_list):
138
- filter_params['created_at_' + str(j)] = date_filter.date
204
+ if date_filter.comparison_operator not in [
205
+ ComparisonOperator.is_null,
206
+ ComparisonOperator.is_not_null,
207
+ ]:
208
+ filter_params['created_at_' + str(j)] = date_filter.date
139
209
 
140
210
  and_filters = [
141
- '(r.created_at ' + date_filter.comparison_operator.value + f' $created_at_{j})'
211
+ date_filter_query_constructor(
212
+ 'e.created_at', f'$created_at_{j}', date_filter.comparison_operator
213
+ )
142
214
  for j, date_filter in enumerate(or_list)
143
215
  ]
144
216
  and_filter_query = ''
@@ -154,16 +226,22 @@ def edge_search_filter_query_constructor(
154
226
  else:
155
227
  created_at_filter += ' OR '
156
228
 
157
- filter_query += created_at_filter
229
+ filter_queries.append(created_at_filter)
158
230
 
159
231
  if filters.expired_at is not None:
160
- expired_at_filter = ' AND ('
232
+ expired_at_filter = '('
161
233
  for i, or_list in enumerate(filters.expired_at):
162
234
  for j, date_filter in enumerate(or_list):
163
- filter_params['expired_at_' + str(j)] = date_filter.date
235
+ if date_filter.comparison_operator not in [
236
+ ComparisonOperator.is_null,
237
+ ComparisonOperator.is_not_null,
238
+ ]:
239
+ filter_params['expired_at_' + str(j)] = date_filter.date
164
240
 
165
241
  and_filters = [
166
- '(r.expired_at ' + date_filter.comparison_operator.value + f' $expired_at_{j})'
242
+ date_filter_query_constructor(
243
+ 'e.expired_at', f'$expired_at_{j}', date_filter.comparison_operator
244
+ )
167
245
  for j, date_filter in enumerate(or_list)
168
246
  ]
169
247
  and_filter_query = ''
@@ -179,6 +257,6 @@ def edge_search_filter_query_constructor(
179
257
  else:
180
258
  expired_at_filter += ' OR '
181
259
 
182
- filter_query += expired_at_filter
260
+ filter_queries.append(expired_at_filter)
183
261
 
184
- return filter_query, filter_params
262
+ 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