graphiti-core 0.19.0rc3__py3-none-any.whl → 0.20.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/graphiti.py CHANGED
@@ -627,6 +627,7 @@ class Graphiti:
627
627
  # if group_id is None, use the default group id by the provider
628
628
  group_id = group_id or get_default_group_id(self.driver.provider)
629
629
  validate_group_id(group_id)
630
+ await build_dynamic_indexes(self.driver, group_id)
630
631
 
631
632
  # Create default edge type map
632
633
  edge_type_map_default = (
@@ -1008,6 +1009,8 @@ class Graphiti:
1008
1009
  if edge.fact_embedding is None:
1009
1010
  await edge.generate_embedding(self.embedder)
1010
1011
 
1012
+ await build_dynamic_indexes(self.driver, source_node.group_id)
1013
+
1011
1014
  nodes, uuid_map, _ = await resolve_extracted_nodes(
1012
1015
  self.clients,
1013
1016
  [source_node, target_node],
@@ -1070,7 +1073,7 @@ class Graphiti:
1070
1073
  if record['episode_count'] == 1:
1071
1074
  nodes_to_delete.append(node)
1072
1075
 
1076
+ await Edge.delete_by_uuids(self.driver, [edge.uuid for edge in edges_to_delete])
1073
1077
  await Node.delete_by_uuids(self.driver, [node.uuid for node in nodes_to_delete])
1074
1078
 
1075
- await Edge.delete_by_uuids(self.driver, [edge.uuid for edge in edges_to_delete])
1076
1079
  await episode.delete(self.driver)
graphiti_core/helpers.py CHANGED
@@ -26,7 +26,6 @@ from dotenv import load_dotenv
26
26
  from neo4j import time as neo4j_time
27
27
  from numpy._typing import NDArray
28
28
  from pydantic import BaseModel
29
- from typing_extensions import LiteralString
30
29
 
31
30
  from graphiti_core.driver.driver import GraphProvider
32
31
  from graphiti_core.errors import GroupIdValidationError
@@ -38,19 +37,15 @@ SEMAPHORE_LIMIT = int(os.getenv('SEMAPHORE_LIMIT', 20))
38
37
  MAX_REFLEXION_ITERATIONS = int(os.getenv('MAX_REFLEXION_ITERATIONS', 0))
39
38
  DEFAULT_PAGE_LIMIT = 20
40
39
 
41
- RUNTIME_QUERY: LiteralString = (
42
- 'CYPHER runtime = parallel parallelRuntimeSupport=all\n' if USE_PARALLEL_RUNTIME else ''
43
- )
44
40
 
41
+ def parse_db_date(input_date: neo4j_time.DateTime | str | None) -> datetime | None:
42
+ if isinstance(input_date, neo4j_time.DateTime):
43
+ return input_date.to_native()
45
44
 
46
- def parse_db_date(neo_date: neo4j_time.DateTime | str | None) -> datetime | None:
47
- return (
48
- neo_date.to_native()
49
- if isinstance(neo_date, neo4j_time.DateTime)
50
- else datetime.fromisoformat(neo_date)
51
- if neo_date
52
- else None
53
- )
45
+ if isinstance(input_date, str):
46
+ return datetime.fromisoformat(input_date)
47
+
48
+ return input_date
54
49
 
55
50
 
56
51
  def get_default_group_id(provider: GraphProvider) -> str:
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import csv
2
3
  import os
3
4
 
4
5
  from graphiti_core.driver.driver import GraphDriver
@@ -57,14 +58,41 @@ async def neo4j_node_group_labels(driver: GraphDriver, group_id: str, batch_size
57
58
  )
58
59
 
59
60
 
60
- async def neo4j_node_label_migration(driver: GraphDriver):
61
+ def pop_last_n_group_ids(csv_file: str = 'group_ids.csv', count: int = 10):
62
+ with open(csv_file) as file:
63
+ reader = csv.reader(file)
64
+ group_ids = [row[0] for row in reader]
65
+
66
+ total_count = len(group_ids)
67
+ popped = group_ids[-count:]
68
+ remaining = group_ids[:-count]
69
+
70
+ with open(csv_file, 'w', newline='') as file:
71
+ writer = csv.writer(file)
72
+ for gid in remaining:
73
+ writer.writerow([gid])
74
+
75
+ return popped, total_count
76
+
77
+
78
+ async def get_group_ids(driver: GraphDriver):
61
79
  query = """MATCH (n:Episodic)
62
80
  RETURN DISTINCT n.group_id AS group_id"""
63
81
 
64
82
  results, _, _ = await driver.execute_query(query)
65
- for result in results:
66
- group_id = result['group_id']
67
- await neo4j_node_group_labels(driver, group_id)
83
+ group_ids = [result['group_id'] for result in results]
84
+
85
+ with open('group_ids.csv', 'w', newline='') as file:
86
+ writer = csv.writer(file)
87
+ for gid in group_ids:
88
+ writer.writerow([gid])
89
+
90
+
91
+ async def neo4j_node_label_migration(driver: GraphDriver, batch_size: int = 10):
92
+ group_ids, total = pop_last_n_group_ids(csv_file='group_ids.csv', count=batch_size)
93
+ while len(group_ids) > 0:
94
+ await asyncio.gather(*[neo4j_node_group_labels(driver, group_id) for group_id in group_ids])
95
+ group_ids, _ = pop_last_n_group_ids(csv_file='group_ids.csv', count=batch_size)
68
96
 
69
97
 
70
98
  async def main():
@@ -77,6 +105,7 @@ async def main():
77
105
  user=neo4j_user,
78
106
  password=neo4j_password,
79
107
  )
108
+ await get_group_ids(driver)
80
109
  await neo4j_node_label_migration(driver)
81
110
  await driver.close()
82
111
 
@@ -20,18 +20,36 @@ EPISODIC_EDGE_SAVE = """
20
20
  MATCH (episode:Episodic {uuid: $episode_uuid})
21
21
  MATCH (node:Entity {uuid: $entity_uuid})
22
22
  MERGE (episode)-[e:MENTIONS {uuid: $uuid}]->(node)
23
- SET e = {uuid: $uuid, group_id: $group_id, created_at: $created_at}
23
+ SET
24
+ e.group_id = $group_id,
25
+ e.created_at = $created_at
24
26
  RETURN e.uuid AS uuid
25
27
  """
26
28
 
27
- EPISODIC_EDGE_SAVE_BULK = """
28
- UNWIND $episodic_edges AS edge
29
- MATCH (episode:Episodic {uuid: edge.source_node_uuid})
30
- MATCH (node:Entity {uuid: edge.target_node_uuid})
31
- MERGE (episode)-[e:MENTIONS {uuid: edge.uuid}]->(node)
32
- SET e = {uuid: edge.uuid, group_id: edge.group_id, created_at: edge.created_at}
33
- RETURN e.uuid AS uuid
34
- """
29
+
30
+ def get_episodic_edge_save_bulk_query(provider: GraphProvider) -> str:
31
+ if provider == GraphProvider.KUZU:
32
+ return """
33
+ MATCH (episode:Episodic {uuid: $source_node_uuid})
34
+ MATCH (node:Entity {uuid: $target_node_uuid})
35
+ MERGE (episode)-[e:MENTIONS {uuid: $uuid}]->(node)
36
+ SET
37
+ e.group_id = $group_id,
38
+ e.created_at = $created_at
39
+ RETURN e.uuid AS uuid
40
+ """
41
+
42
+ return """
43
+ UNWIND $episodic_edges AS edge
44
+ MATCH (episode:Episodic {uuid: edge.source_node_uuid})
45
+ MATCH (node:Entity {uuid: edge.target_node_uuid})
46
+ MERGE (episode)-[e:MENTIONS {uuid: edge.uuid}]->(node)
47
+ SET
48
+ e.group_id = edge.group_id,
49
+ e.created_at = edge.created_at
50
+ RETURN e.uuid AS uuid
51
+ """
52
+
35
53
 
36
54
  EPISODIC_EDGE_RETURN = """
37
55
  e.uuid AS uuid,
@@ -54,14 +72,32 @@ def get_entity_edge_save_query(provider: GraphProvider) -> str:
54
72
  """
55
73
  case GraphProvider.NEPTUNE:
56
74
  return """
57
- MATCH (source:Entity {uuid: $edge_data.source_uuid})
58
- MATCH (target:Entity {uuid: $edge_data.target_uuid})
75
+ MATCH (source:Entity {uuid: $edge_data.source_uuid})
76
+ MATCH (target:Entity {uuid: $edge_data.target_uuid})
59
77
  MERGE (source)-[e:RELATES_TO {uuid: $edge_data.uuid}]->(target)
60
78
  SET e = removeKeyFromMap(removeKeyFromMap($edge_data, "fact_embedding"), "episodes")
61
79
  SET e.fact_embedding = join([x IN coalesce($edge_data.fact_embedding, []) | toString(x) ], ",")
62
80
  SET e.episodes = join($edge_data.episodes, ",")
63
81
  RETURN $edge_data.uuid AS uuid
64
82
  """
83
+ case GraphProvider.KUZU:
84
+ return """
85
+ MATCH (source:Entity {uuid: $source_uuid})
86
+ MATCH (target:Entity {uuid: $target_uuid})
87
+ MERGE (source)-[:RELATES_TO]->(e:RelatesToNode_ {uuid: $uuid})-[:RELATES_TO]->(target)
88
+ SET
89
+ e.group_id = $group_id,
90
+ e.created_at = $created_at,
91
+ e.name = $name,
92
+ e.fact = $fact,
93
+ e.fact_embedding = $fact_embedding,
94
+ e.episodes = $episodes,
95
+ e.expired_at = $expired_at,
96
+ e.valid_at = $valid_at,
97
+ e.invalid_at = $invalid_at,
98
+ e.attributes = $attributes
99
+ RETURN e.uuid AS uuid
100
+ """
65
101
  case _: # Neo4j
66
102
  return """
67
103
  MATCH (source:Entity {uuid: $edge_data.source_uuid})
@@ -89,14 +125,32 @@ def get_entity_edge_save_bulk_query(provider: GraphProvider) -> str:
89
125
  case GraphProvider.NEPTUNE:
90
126
  return """
91
127
  UNWIND $entity_edges AS edge
92
- MATCH (source:Entity {uuid: edge.source_node_uuid})
93
- MATCH (target:Entity {uuid: edge.target_node_uuid})
128
+ MATCH (source:Entity {uuid: edge.source_node_uuid})
129
+ MATCH (target:Entity {uuid: edge.target_node_uuid})
94
130
  MERGE (source)-[r:RELATES_TO {uuid: edge.uuid}]->(target)
95
131
  SET r = removeKeyFromMap(removeKeyFromMap(edge, "fact_embedding"), "episodes")
96
132
  SET r.fact_embedding = join([x IN coalesce(edge.fact_embedding, []) | toString(x) ], ",")
97
133
  SET r.episodes = join(edge.episodes, ",")
98
134
  RETURN edge.uuid AS uuid
99
135
  """
136
+ case GraphProvider.KUZU:
137
+ return """
138
+ MATCH (source:Entity {uuid: $source_node_uuid})
139
+ MATCH (target:Entity {uuid: $target_node_uuid})
140
+ MERGE (source)-[:RELATES_TO]->(e:RelatesToNode_ {uuid: $uuid})-[:RELATES_TO]->(target)
141
+ SET
142
+ e.group_id = $group_id,
143
+ e.created_at = $created_at,
144
+ e.name = $name,
145
+ e.fact = $fact,
146
+ e.fact_embedding = $fact_embedding,
147
+ e.episodes = $episodes,
148
+ e.expired_at = $expired_at,
149
+ e.valid_at = $valid_at,
150
+ e.invalid_at = $invalid_at,
151
+ e.attributes = $attributes
152
+ RETURN e.uuid AS uuid
153
+ """
100
154
  case _:
101
155
  return """
102
156
  UNWIND $entity_edges AS edge
@@ -109,35 +163,42 @@ def get_entity_edge_save_bulk_query(provider: GraphProvider) -> str:
109
163
  """
110
164
 
111
165
 
112
- ENTITY_EDGE_RETURN = """
113
- e.uuid AS uuid,
114
- n.uuid AS source_node_uuid,
115
- m.uuid AS target_node_uuid,
116
- e.group_id AS group_id,
117
- e.name AS name,
118
- e.fact AS fact,
119
- e.episodes AS episodes,
120
- e.created_at AS created_at,
121
- e.expired_at AS expired_at,
122
- e.valid_at AS valid_at,
123
- e.invalid_at AS invalid_at,
124
- properties(e) AS attributes
125
- """
166
+ def get_entity_edge_return_query(provider: GraphProvider) -> str:
167
+ # `fact_embedding` is not returned by default and must be manually loaded using `load_fact_embedding()`.
126
168
 
127
- ENTITY_EDGE_RETURN_NEPTUNE = """
128
- e.uuid AS uuid,
129
- n.uuid AS source_node_uuid,
130
- m.uuid AS target_node_uuid,
131
- e.group_id AS group_id,
132
- e.name AS name,
133
- e.fact AS fact,
134
- split(e.episodes, ',') AS episodes,
135
- e.created_at AS created_at,
136
- e.expired_at AS expired_at,
137
- e.valid_at AS valid_at,
138
- e.invalid_at AS invalid_at,
139
- properties(e) AS attributes
140
- """
169
+ if provider == GraphProvider.NEPTUNE:
170
+ return """
171
+ e.uuid AS uuid,
172
+ n.uuid AS source_node_uuid,
173
+ m.uuid AS target_node_uuid,
174
+ e.group_id AS group_id,
175
+ e.name AS name,
176
+ e.fact AS fact,
177
+ split(e.episodes, ',') AS episodes,
178
+ e.created_at AS created_at,
179
+ e.expired_at AS expired_at,
180
+ e.valid_at AS valid_at,
181
+ e.invalid_at AS invalid_at,
182
+ properties(e) AS attributes
183
+ """
184
+
185
+ return """
186
+ e.uuid AS uuid,
187
+ n.uuid AS source_node_uuid,
188
+ m.uuid AS target_node_uuid,
189
+ e.group_id AS group_id,
190
+ e.created_at AS created_at,
191
+ e.name AS name,
192
+ e.fact AS fact,
193
+ e.episodes AS episodes,
194
+ e.expired_at AS expired_at,
195
+ e.valid_at AS valid_at,
196
+ e.invalid_at AS invalid_at,
197
+ """ + (
198
+ 'e.attributes AS attributes'
199
+ if provider == GraphProvider.KUZU
200
+ else 'properties(e) AS attributes'
201
+ )
141
202
 
142
203
 
143
204
  def get_community_edge_save_query(provider: GraphProvider) -> str:
@@ -152,7 +213,7 @@ def get_community_edge_save_query(provider: GraphProvider) -> str:
152
213
  """
153
214
  case GraphProvider.NEPTUNE:
154
215
  return """
155
- MATCH (community:Community {uuid: $community_uuid})
216
+ MATCH (community:Community {uuid: $community_uuid})
156
217
  MATCH (node {uuid: $entity_uuid})
157
218
  WHERE node:Entity OR node:Community
158
219
  MERGE (community)-[r:HAS_MEMBER {uuid: $uuid}]->(node)
@@ -161,6 +222,24 @@ def get_community_edge_save_query(provider: GraphProvider) -> str:
161
222
  SET r.created_at= $created_at
162
223
  RETURN r.uuid AS uuid
163
224
  """
225
+ case GraphProvider.KUZU:
226
+ return """
227
+ MATCH (community:Community {uuid: $community_uuid})
228
+ MATCH (node:Entity {uuid: $entity_uuid})
229
+ MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
230
+ SET
231
+ e.group_id = $group_id,
232
+ e.created_at = $created_at
233
+ RETURN e.uuid AS uuid
234
+ UNION
235
+ MATCH (community:Community {uuid: $community_uuid})
236
+ MATCH (node:Community {uuid: $entity_uuid})
237
+ MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
238
+ SET
239
+ e.group_id = $group_id,
240
+ e.created_at = $created_at
241
+ RETURN e.uuid AS uuid
242
+ """
164
243
  case _: # Neo4j
165
244
  return """
166
245
  MATCH (community:Community {uuid: $community_uuid})
@@ -24,10 +24,24 @@ def get_episode_node_save_query(provider: GraphProvider) -> str:
24
24
  case GraphProvider.NEPTUNE:
25
25
  return """
26
26
  MERGE (n:Episodic {uuid: $uuid})
27
- SET n = {uuid: $uuid, name: $name, group_id: $group_id, source_description: $source_description, source: $source, content: $content,
27
+ SET n = {uuid: $uuid, name: $name, group_id: $group_id, source_description: $source_description, source: $source, content: $content,
28
28
  entity_edges: join([x IN coalesce($entity_edges, []) | toString(x) ], '|'), created_at: $created_at, valid_at: $valid_at}
29
29
  RETURN n.uuid AS uuid
30
30
  """
31
+ case GraphProvider.KUZU:
32
+ return """
33
+ MERGE (n:Episodic {uuid: $uuid})
34
+ SET
35
+ n.name = $name,
36
+ n.group_id = $group_id,
37
+ n.created_at = $created_at,
38
+ n.source = $source,
39
+ n.source_description = $source_description,
40
+ n.content = $content,
41
+ n.valid_at = $valid_at,
42
+ n.entity_edges = $entity_edges
43
+ RETURN n.uuid AS uuid
44
+ """
31
45
  case GraphProvider.FALKORDB:
32
46
  return """
33
47
  MERGE (n:Episodic {uuid: $uuid})
@@ -51,11 +65,25 @@ def get_episode_node_save_bulk_query(provider: GraphProvider) -> str:
51
65
  return """
52
66
  UNWIND $episodes AS episode
53
67
  MERGE (n:Episodic {uuid: episode.uuid})
54
- SET n = {uuid: episode.uuid, name: episode.name, group_id: episode.group_id, source_description: episode.source_description,
55
- source: episode.source, content: episode.content,
68
+ SET n = {uuid: episode.uuid, name: episode.name, group_id: episode.group_id, source_description: episode.source_description,
69
+ source: episode.source, content: episode.content,
56
70
  entity_edges: join([x IN coalesce(episode.entity_edges, []) | toString(x) ], '|'), created_at: episode.created_at, valid_at: episode.valid_at}
57
71
  RETURN n.uuid AS uuid
58
72
  """
73
+ case GraphProvider.KUZU:
74
+ return """
75
+ MERGE (n:Episodic {uuid: $uuid})
76
+ SET
77
+ n.name = $name,
78
+ n.group_id = $group_id,
79
+ n.created_at = $created_at,
80
+ n.source = $source,
81
+ n.source_description = $source_description,
82
+ n.content = $content,
83
+ n.valid_at = $valid_at,
84
+ n.entity_edges = $entity_edges
85
+ RETURN n.uuid AS uuid
86
+ """
59
87
  case GraphProvider.FALKORDB:
60
88
  return """
61
89
  UNWIND $episodes AS episode
@@ -76,14 +104,14 @@ def get_episode_node_save_bulk_query(provider: GraphProvider) -> str:
76
104
 
77
105
 
78
106
  EPISODIC_NODE_RETURN = """
79
- e.content AS content,
80
- e.created_at AS created_at,
81
- e.valid_at AS valid_at,
82
107
  e.uuid AS uuid,
83
108
  e.name AS name,
84
109
  e.group_id AS group_id,
85
- e.source_description AS source_description,
110
+ e.created_at AS created_at,
86
111
  e.source AS source,
112
+ e.source_description AS source_description,
113
+ e.content AS content,
114
+ e.valid_at AS valid_at,
87
115
  e.entity_edges AS entity_edges
88
116
  """
89
117
 
@@ -109,6 +137,20 @@ def get_entity_node_save_query(provider: GraphProvider, labels: str) -> str:
109
137
  SET n = $entity_data
110
138
  RETURN n.uuid AS uuid
111
139
  """
140
+ case GraphProvider.KUZU:
141
+ return """
142
+ MERGE (n:Entity {uuid: $uuid})
143
+ SET
144
+ n.name = $name,
145
+ n.group_id = $group_id,
146
+ n.labels = $labels,
147
+ n.created_at = $created_at,
148
+ n.name_embedding = $name_embedding,
149
+ n.summary = $summary,
150
+ n.attributes = $attributes
151
+ WITH n
152
+ RETURN n.uuid AS uuid
153
+ """
112
154
  case GraphProvider.NEPTUNE:
113
155
  label_subquery = ''
114
156
  for label in labels.split(':'):
@@ -168,6 +210,19 @@ def get_entity_node_save_bulk_query(provider: GraphProvider, nodes: list[dict])
168
210
  """
169
211
  )
170
212
  return queries
213
+ case GraphProvider.KUZU:
214
+ return """
215
+ MERGE (n:Entity {uuid: $uuid})
216
+ SET
217
+ n.name = $name,
218
+ n.group_id = $group_id,
219
+ n.labels = $labels,
220
+ n.created_at = $created_at,
221
+ n.name_embedding = $name_embedding,
222
+ n.summary = $summary,
223
+ n.attributes = $attributes
224
+ RETURN n.uuid AS uuid
225
+ """
171
226
  case _: # Neo4j
172
227
  return """
173
228
  UNWIND $nodes AS node
@@ -179,15 +234,28 @@ def get_entity_node_save_bulk_query(provider: GraphProvider, nodes: list[dict])
179
234
  """
180
235
 
181
236
 
182
- ENTITY_NODE_RETURN = """
183
- n.uuid AS uuid,
184
- n.name AS name,
185
- n.group_id AS group_id,
186
- n.created_at AS created_at,
187
- n.summary AS summary,
188
- labels(n) AS labels,
189
- properties(n) AS attributes
190
- """
237
+ def get_entity_node_return_query(provider: GraphProvider) -> str:
238
+ # `name_embedding` is not returned by default and must be loaded manually using `load_name_embedding()`.
239
+ if provider == GraphProvider.KUZU:
240
+ return """
241
+ n.uuid AS uuid,
242
+ n.name AS name,
243
+ n.group_id AS group_id,
244
+ n.labels AS labels,
245
+ n.created_at AS created_at,
246
+ n.summary AS summary,
247
+ n.attributes AS attributes
248
+ """
249
+
250
+ return """
251
+ n.uuid AS uuid,
252
+ n.name AS name,
253
+ n.group_id AS group_id,
254
+ n.created_at AS created_at,
255
+ n.summary AS summary,
256
+ labels(n) AS labels,
257
+ properties(n) AS attributes
258
+ """
191
259
 
192
260
 
193
261
  def get_community_node_save_query(provider: GraphProvider) -> str:
@@ -201,10 +269,21 @@ def get_community_node_save_query(provider: GraphProvider) -> str:
201
269
  case GraphProvider.NEPTUNE:
202
270
  return """
203
271
  MERGE (n:Community {uuid: $uuid})
204
- SET n = {uuid: $uuid, name: $name, group_id: $group_id, summary: $summary, created_at: $created_at}
272
+ SET n = {uuid: $uuid, name: $name, group_id: $group_id, summary: $summary, created_at: $created_at}
205
273
  SET n.name_embedding = join([x IN coalesce($name_embedding, []) | toString(x) ], ",")
206
274
  RETURN n.uuid AS uuid
207
275
  """
276
+ case GraphProvider.KUZU:
277
+ return """
278
+ MERGE (n:Community {uuid: $uuid})
279
+ SET
280
+ n.name = $name,
281
+ n.group_id = $group_id,
282
+ n.created_at = $created_at,
283
+ n.name_embedding = $name_embedding,
284
+ n.summary = $summary
285
+ RETURN n.uuid AS uuid
286
+ """
208
287
  case _: # Neo4j
209
288
  return """
210
289
  MERGE (n:Community {uuid: $uuid})
@@ -215,12 +294,12 @@ def get_community_node_save_query(provider: GraphProvider) -> str:
215
294
 
216
295
 
217
296
  COMMUNITY_NODE_RETURN = """
218
- n.uuid AS uuid,
219
- n.name AS name,
220
- n.name_embedding AS name_embedding,
221
- n.group_id AS group_id,
222
- n.summary AS summary,
223
- n.created_at AS created_at
297
+ c.uuid AS uuid,
298
+ c.name AS name,
299
+ c.group_id AS group_id,
300
+ c.created_at AS created_at,
301
+ c.name_embedding AS name_embedding,
302
+ c.summary AS summary
224
303
  """
225
304
 
226
305
  COMMUNITY_NODE_RETURN_NEPTUNE = """