planar 0.5.0__py3-none-any.whl → 0.8.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.
Files changed (211) hide show
  1. planar/_version.py +1 -1
  2. planar/ai/agent.py +155 -283
  3. planar/ai/agent_base.py +170 -0
  4. planar/ai/agent_utils.py +7 -0
  5. planar/ai/pydantic_ai.py +638 -0
  6. planar/ai/test_agent_serialization.py +1 -1
  7. planar/app.py +64 -20
  8. planar/cli.py +39 -27
  9. planar/config.py +45 -36
  10. planar/db/db.py +2 -1
  11. planar/files/storage/azure_blob.py +343 -0
  12. planar/files/storage/base.py +7 -0
  13. planar/files/storage/config.py +70 -7
  14. planar/files/storage/s3.py +6 -6
  15. planar/files/storage/test_azure_blob.py +435 -0
  16. planar/logging/formatter.py +17 -4
  17. planar/logging/test_formatter.py +327 -0
  18. planar/registry_items.py +2 -1
  19. planar/routers/agents_router.py +3 -1
  20. planar/routers/files.py +11 -2
  21. planar/routers/models.py +14 -1
  22. planar/routers/test_agents_router.py +1 -1
  23. planar/routers/test_files_router.py +49 -0
  24. planar/routers/test_routes_security.py +5 -7
  25. planar/routers/test_workflow_router.py +270 -3
  26. planar/routers/workflow.py +95 -36
  27. planar/rules/models.py +36 -39
  28. planar/rules/test_data/account_dormancy_management.json +223 -0
  29. planar/rules/test_data/airline_loyalty_points_calculator.json +262 -0
  30. planar/rules/test_data/applicant_risk_assessment.json +435 -0
  31. planar/rules/test_data/booking_fraud_detection.json +407 -0
  32. planar/rules/test_data/cellular_data_rollover_system.json +258 -0
  33. planar/rules/test_data/clinical_trial_eligibility_screener.json +437 -0
  34. planar/rules/test_data/customer_lifetime_value.json +143 -0
  35. planar/rules/test_data/import_duties_calculator.json +289 -0
  36. planar/rules/test_data/insurance_prior_authorization.json +443 -0
  37. planar/rules/test_data/online_check_in_eligibility_system.json +254 -0
  38. planar/rules/test_data/order_consolidation_system.json +375 -0
  39. planar/rules/test_data/portfolio_risk_monitor.json +471 -0
  40. planar/rules/test_data/supply_chain_risk.json +253 -0
  41. planar/rules/test_data/warehouse_cross_docking.json +237 -0
  42. planar/rules/test_rules.py +750 -6
  43. planar/scaffold_templates/planar.dev.yaml.j2 +6 -6
  44. planar/scaffold_templates/planar.prod.yaml.j2 +9 -5
  45. planar/scaffold_templates/pyproject.toml.j2 +1 -1
  46. planar/security/auth_context.py +21 -0
  47. planar/security/{jwt_middleware.py → auth_middleware.py} +70 -17
  48. planar/security/authorization.py +9 -15
  49. planar/security/tests/test_auth_middleware.py +162 -0
  50. planar/sse/proxy.py +4 -9
  51. planar/test_app.py +92 -1
  52. planar/test_cli.py +81 -59
  53. planar/test_config.py +17 -14
  54. planar/testing/fixtures.py +325 -0
  55. planar/testing/planar_test_client.py +5 -2
  56. planar/utils.py +41 -1
  57. planar/workflows/execution.py +1 -1
  58. planar/workflows/orchestrator.py +5 -0
  59. planar/workflows/serialization.py +12 -6
  60. planar/workflows/step_core.py +3 -1
  61. planar/workflows/test_serialization.py +9 -1
  62. {planar-0.5.0.dist-info → planar-0.8.0.dist-info}/METADATA +30 -5
  63. planar-0.8.0.dist-info/RECORD +166 -0
  64. planar/.__init__.py.un~ +0 -0
  65. planar/._version.py.un~ +0 -0
  66. planar/.app.py.un~ +0 -0
  67. planar/.cli.py.un~ +0 -0
  68. planar/.config.py.un~ +0 -0
  69. planar/.context.py.un~ +0 -0
  70. planar/.db.py.un~ +0 -0
  71. planar/.di.py.un~ +0 -0
  72. planar/.engine.py.un~ +0 -0
  73. planar/.files.py.un~ +0 -0
  74. planar/.log_context.py.un~ +0 -0
  75. planar/.log_metadata.py.un~ +0 -0
  76. planar/.logging.py.un~ +0 -0
  77. planar/.object_registry.py.un~ +0 -0
  78. planar/.otel.py.un~ +0 -0
  79. planar/.server.py.un~ +0 -0
  80. planar/.session.py.un~ +0 -0
  81. planar/.sqlalchemy.py.un~ +0 -0
  82. planar/.task_local.py.un~ +0 -0
  83. planar/.test_app.py.un~ +0 -0
  84. planar/.test_config.py.un~ +0 -0
  85. planar/.test_object_config.py.un~ +0 -0
  86. planar/.test_sqlalchemy.py.un~ +0 -0
  87. planar/.test_utils.py.un~ +0 -0
  88. planar/.util.py.un~ +0 -0
  89. planar/.utils.py.un~ +0 -0
  90. planar/ai/.__init__.py.un~ +0 -0
  91. planar/ai/._models.py.un~ +0 -0
  92. planar/ai/.agent.py.un~ +0 -0
  93. planar/ai/.agent_utils.py.un~ +0 -0
  94. planar/ai/.events.py.un~ +0 -0
  95. planar/ai/.files.py.un~ +0 -0
  96. planar/ai/.models.py.un~ +0 -0
  97. planar/ai/.providers.py.un~ +0 -0
  98. planar/ai/.pydantic_ai.py.un~ +0 -0
  99. planar/ai/.pydantic_ai_agent.py.un~ +0 -0
  100. planar/ai/.pydantic_ai_provider.py.un~ +0 -0
  101. planar/ai/.step.py.un~ +0 -0
  102. planar/ai/.test_agent.py.un~ +0 -0
  103. planar/ai/.test_agent_serialization.py.un~ +0 -0
  104. planar/ai/.test_providers.py.un~ +0 -0
  105. planar/ai/.utils.py.un~ +0 -0
  106. planar/ai/providers.py +0 -1088
  107. planar/ai/test_agent.py +0 -1298
  108. planar/ai/test_providers.py +0 -463
  109. planar/db/.db.py.un~ +0 -0
  110. planar/files/.config.py.un~ +0 -0
  111. planar/files/.local.py.un~ +0 -0
  112. planar/files/.local_filesystem.py.un~ +0 -0
  113. planar/files/.model.py.un~ +0 -0
  114. planar/files/.models.py.un~ +0 -0
  115. planar/files/.s3.py.un~ +0 -0
  116. planar/files/.storage.py.un~ +0 -0
  117. planar/files/.test_files.py.un~ +0 -0
  118. planar/files/storage/.__init__.py.un~ +0 -0
  119. planar/files/storage/.base.py.un~ +0 -0
  120. planar/files/storage/.config.py.un~ +0 -0
  121. planar/files/storage/.context.py.un~ +0 -0
  122. planar/files/storage/.local_directory.py.un~ +0 -0
  123. planar/files/storage/.test_local_directory.py.un~ +0 -0
  124. planar/files/storage/.test_s3.py.un~ +0 -0
  125. planar/human/.human.py.un~ +0 -0
  126. planar/human/.test_human.py.un~ +0 -0
  127. planar/logging/.__init__.py.un~ +0 -0
  128. planar/logging/.attributes.py.un~ +0 -0
  129. planar/logging/.formatter.py.un~ +0 -0
  130. planar/logging/.logger.py.un~ +0 -0
  131. planar/logging/.otel.py.un~ +0 -0
  132. planar/logging/.tracer.py.un~ +0 -0
  133. planar/modeling/.mixin.py.un~ +0 -0
  134. planar/modeling/.storage.py.un~ +0 -0
  135. planar/modeling/orm/.planar_base_model.py.un~ +0 -0
  136. planar/object_config/.object_config.py.un~ +0 -0
  137. planar/routers/.__init__.py.un~ +0 -0
  138. planar/routers/.agents_router.py.un~ +0 -0
  139. planar/routers/.crud.py.un~ +0 -0
  140. planar/routers/.decision.py.un~ +0 -0
  141. planar/routers/.event.py.un~ +0 -0
  142. planar/routers/.file_attachment.py.un~ +0 -0
  143. planar/routers/.files.py.un~ +0 -0
  144. planar/routers/.files_router.py.un~ +0 -0
  145. planar/routers/.human.py.un~ +0 -0
  146. planar/routers/.info.py.un~ +0 -0
  147. planar/routers/.models.py.un~ +0 -0
  148. planar/routers/.object_config_router.py.un~ +0 -0
  149. planar/routers/.rule.py.un~ +0 -0
  150. planar/routers/.test_object_config_router.py.un~ +0 -0
  151. planar/routers/.test_workflow_router.py.un~ +0 -0
  152. planar/routers/.workflow.py.un~ +0 -0
  153. planar/rules/.decorator.py.un~ +0 -0
  154. planar/rules/.runner.py.un~ +0 -0
  155. planar/rules/.test_rules.py.un~ +0 -0
  156. planar/security/.jwt_middleware.py.un~ +0 -0
  157. planar/sse/.constants.py.un~ +0 -0
  158. planar/sse/.example.html.un~ +0 -0
  159. planar/sse/.hub.py.un~ +0 -0
  160. planar/sse/.model.py.un~ +0 -0
  161. planar/sse/.proxy.py.un~ +0 -0
  162. planar/testing/.client.py.un~ +0 -0
  163. planar/testing/.memory_storage.py.un~ +0 -0
  164. planar/testing/.planar_test_client.py.un~ +0 -0
  165. planar/testing/.predictable_tracer.py.un~ +0 -0
  166. planar/testing/.synchronizable_tracer.py.un~ +0 -0
  167. planar/testing/.test_memory_storage.py.un~ +0 -0
  168. planar/testing/.workflow_observer.py.un~ +0 -0
  169. planar/workflows/.__init__.py.un~ +0 -0
  170. planar/workflows/.builtin_steps.py.un~ +0 -0
  171. planar/workflows/.concurrency_tracing.py.un~ +0 -0
  172. planar/workflows/.context.py.un~ +0 -0
  173. planar/workflows/.contrib.py.un~ +0 -0
  174. planar/workflows/.decorators.py.un~ +0 -0
  175. planar/workflows/.durable_test.py.un~ +0 -0
  176. planar/workflows/.errors.py.un~ +0 -0
  177. planar/workflows/.events.py.un~ +0 -0
  178. planar/workflows/.exceptions.py.un~ +0 -0
  179. planar/workflows/.execution.py.un~ +0 -0
  180. planar/workflows/.human.py.un~ +0 -0
  181. planar/workflows/.lock.py.un~ +0 -0
  182. planar/workflows/.misc.py.un~ +0 -0
  183. planar/workflows/.model.py.un~ +0 -0
  184. planar/workflows/.models.py.un~ +0 -0
  185. planar/workflows/.notifications.py.un~ +0 -0
  186. planar/workflows/.orchestrator.py.un~ +0 -0
  187. planar/workflows/.runtime.py.un~ +0 -0
  188. planar/workflows/.serialization.py.un~ +0 -0
  189. planar/workflows/.step.py.un~ +0 -0
  190. planar/workflows/.step_core.py.un~ +0 -0
  191. planar/workflows/.sub_workflow_runner.py.un~ +0 -0
  192. planar/workflows/.sub_workflow_scheduler.py.un~ +0 -0
  193. planar/workflows/.test_concurrency.py.un~ +0 -0
  194. planar/workflows/.test_concurrency_detection.py.un~ +0 -0
  195. planar/workflows/.test_human.py.un~ +0 -0
  196. planar/workflows/.test_lock_timeout.py.un~ +0 -0
  197. planar/workflows/.test_orchestrator.py.un~ +0 -0
  198. planar/workflows/.test_race_conditions.py.un~ +0 -0
  199. planar/workflows/.test_serialization.py.un~ +0 -0
  200. planar/workflows/.test_suspend_deserialization.py.un~ +0 -0
  201. planar/workflows/.test_workflow.py.un~ +0 -0
  202. planar/workflows/.tracing.py.un~ +0 -0
  203. planar/workflows/.types.py.un~ +0 -0
  204. planar/workflows/.util.py.un~ +0 -0
  205. planar/workflows/.utils.py.un~ +0 -0
  206. planar/workflows/.workflow.py.un~ +0 -0
  207. planar/workflows/.workflow_wrapper.py.un~ +0 -0
  208. planar/workflows/.wrappers.py.un~ +0 -0
  209. planar-0.5.0.dist-info/RECORD +0 -289
  210. {planar-0.5.0.dist-info → planar-0.8.0.dist-info}/WHEEL +0 -0
  211. {planar-0.5.0.dist-info → planar-0.8.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,375 @@
1
+ {
2
+ "nodes": [
3
+ {
4
+ "type": "inputNode",
5
+ "content": {
6
+ "schema": ""
7
+ },
8
+ "id": "ip1",
9
+ "name": "request",
10
+ "position": {
11
+ "x": 110,
12
+ "y": 114
13
+ }
14
+ },
15
+ {
16
+ "type": "decisionTableNode",
17
+ "content": {
18
+ "hitPolicy": "first",
19
+ "rules": [
20
+ {
21
+ "_id": "r1-1",
22
+ "i1-1": "< 50",
23
+ "i1-2": "< 48",
24
+ "i1-3": "> 3",
25
+ "o1-1": "'high'",
26
+ "o1-2": "'Orders are nearby, delivery window compatible, and carrier has capacity'"
27
+ },
28
+ {
29
+ "_id": "r1-2",
30
+ "i1-1": "< 50",
31
+ "i1-2": "< 72",
32
+ "i1-3": "> 2",
33
+ "o1-1": "'medium'",
34
+ "o1-2": "'Orders are nearby with slightly different delivery windows but carrier has capacity'"
35
+ },
36
+ {
37
+ "_id": "r1-3",
38
+ "i1-1": "< 100",
39
+ "i1-2": "< 48",
40
+ "i1-3": "> 2",
41
+ "o1-1": "'medium'",
42
+ "o1-2": "'Orders are reasonably close with compatible delivery windows and carrier has capacity'"
43
+ },
44
+ {
45
+ "_id": "r1-4",
46
+ "i1-1": "< 100",
47
+ "i1-2": "< 72",
48
+ "i1-3": "> 1",
49
+ "o1-1": "'low'",
50
+ "o1-2": "'Orders are reasonably close with slightly different delivery windows and minimal carrier capacity'"
51
+ },
52
+ {
53
+ "_id": "r1-5",
54
+ "i1-1": "",
55
+ "i1-2": "",
56
+ "i1-3": "",
57
+ "o1-1": "'none'",
58
+ "o1-2": "'Orders cannot be consolidated due to distance, delivery timing, or carrier capacity'"
59
+ }
60
+ ],
61
+ "inputs": [
62
+ {
63
+ "id": "i1-1",
64
+ "name": "Distance (km)",
65
+ "field": "distanceKm"
66
+ },
67
+ {
68
+ "id": "i1-2",
69
+ "name": "Delivery Window Difference (hours)",
70
+ "field": "deliveryWindowDifferenceHours"
71
+ },
72
+ {
73
+ "id": "i1-3",
74
+ "name": "Available Carrier Capacity",
75
+ "field": "availableCarrierCapacity"
76
+ }
77
+ ],
78
+ "outputs": [
79
+ {
80
+ "id": "o1-1",
81
+ "name": "Consolidation Priority",
82
+ "field": "consolidationPriority"
83
+ },
84
+ {
85
+ "id": "o1-2",
86
+ "name": "Explanation",
87
+ "field": "explanation"
88
+ }
89
+ ],
90
+ "passThrough": true,
91
+ "inputField": null,
92
+ "outputPath": null,
93
+ "executionMode": "single"
94
+ },
95
+ "id": "dt1",
96
+ "name": "evaluateConsolidationFactors",
97
+ "position": {
98
+ "x": 430,
99
+ "y": 114
100
+ }
101
+ },
102
+ {
103
+ "type": "expressionNode",
104
+ "content": {
105
+ "expressions": [
106
+ {
107
+ "id": "e1-1",
108
+ "key": "canConsolidate",
109
+ "value": "consolidationPriority != 'none'"
110
+ },
111
+ {
112
+ "id": "e1-2",
113
+ "key": "consolidationWeight",
114
+ "value": "orderWeight1 + orderWeight2"
115
+ },
116
+ {
117
+ "id": "e1-3",
118
+ "key": "expectedFuelSavings",
119
+ "value": "distanceKm * 0.15"
120
+ },
121
+ {
122
+ "id": "e1-4",
123
+ "key": "costSavingEstimate",
124
+ "value": "distanceKm * 0.25 + 15"
125
+ }
126
+ ],
127
+ "passThrough": true,
128
+ "inputField": null,
129
+ "outputPath": null,
130
+ "executionMode": "single"
131
+ },
132
+ "id": "ex1",
133
+ "name": "calculateConsolidationMetrics",
134
+ "position": {
135
+ "x": 750,
136
+ "y": 114
137
+ }
138
+ },
139
+ {
140
+ "type": "switchNode",
141
+ "content": {
142
+ "hitPolicy": "first",
143
+ "statements": [
144
+ {
145
+ "id": "s1-1",
146
+ "condition": "consolidationPriority == 'high'",
147
+ "isDefault": false
148
+ },
149
+ {
150
+ "id": "s1-2",
151
+ "condition": "consolidationPriority == 'medium'",
152
+ "isDefault": false
153
+ },
154
+ {
155
+ "id": "s1-3",
156
+ "condition": "consolidationPriority == 'low'",
157
+ "isDefault": false
158
+ },
159
+ {
160
+ "id": "s1-4",
161
+ "condition": "",
162
+ "isDefault": true
163
+ }
164
+ ]
165
+ },
166
+ "id": "sw1",
167
+ "name": "routeByConsolidationPriority",
168
+ "position": {
169
+ "x": 1070,
170
+ "y": 114
171
+ }
172
+ },
173
+ {
174
+ "type": "expressionNode",
175
+ "content": {
176
+ "expressions": [
177
+ {
178
+ "id": "e2-1",
179
+ "key": "consolidationAction",
180
+ "value": "'immediate_consolidation'"
181
+ },
182
+ {
183
+ "id": "e2-2",
184
+ "key": "schedulingPriority",
185
+ "value": "'high'"
186
+ },
187
+ {
188
+ "id": "e2-3",
189
+ "key": "deliverySchedule",
190
+ "value": "{'type': 'consolidated', 'estimatedDeliveryTime': requestedDeliveryDate1, 'notificationRequired': false}"
191
+ },
192
+ {
193
+ "id": "e2-4",
194
+ "key": "costSavingsReport",
195
+ "value": "{'fuelSavings': expectedFuelSavings, 'laborSavings': costSavingEstimate, 'totalSavings': expectedFuelSavings + costSavingEstimate}"
196
+ }
197
+ ],
198
+ "passThrough": true,
199
+ "inputField": null,
200
+ "outputPath": null,
201
+ "executionMode": "single"
202
+ },
203
+ "id": "ex2",
204
+ "name": "highPriorityProcessing",
205
+ "position": {
206
+ "x": 1390,
207
+ "y": -33
208
+ }
209
+ },
210
+ {
211
+ "type": "expressionNode",
212
+ "content": {
213
+ "expressions": [
214
+ {
215
+ "id": "e3-1",
216
+ "key": "consolidationAction",
217
+ "value": "'scheduled_consolidation'"
218
+ },
219
+ {
220
+ "id": "e3-2",
221
+ "key": "schedulingPriority",
222
+ "value": "'medium'"
223
+ },
224
+ {
225
+ "id": "e3-3",
226
+ "key": "deliverySchedule",
227
+ "value": "{'type': 'consolidated', 'estimatedDeliveryTime': requestedDeliveryDate1, 'customerConfirmationRequired': deliveryWindowDifferenceHours > 24}"
228
+ },
229
+ {
230
+ "id": "e3-4",
231
+ "key": "costSavingsReport",
232
+ "value": "{'fuelSavings': expectedFuelSavings * 0.9, 'laborSavings': costSavingEstimate * 0.8, 'totalSavings': (expectedFuelSavings * 0.9) + (costSavingEstimate * 0.8)}"
233
+ }
234
+ ],
235
+ "passThrough": true,
236
+ "inputField": null,
237
+ "outputPath": null,
238
+ "executionMode": "single"
239
+ },
240
+ "id": "ex3",
241
+ "name": "mediumPriorityProcessing",
242
+ "position": {
243
+ "x": 1390,
244
+ "y": 65
245
+ }
246
+ },
247
+ {
248
+ "type": "expressionNode",
249
+ "content": {
250
+ "expressions": [
251
+ {
252
+ "id": "e4-1",
253
+ "key": "consolidationAction",
254
+ "value": "'optional_consolidation'"
255
+ },
256
+ {
257
+ "id": "e4-2",
258
+ "key": "schedulingPriority",
259
+ "value": "'low'"
260
+ },
261
+ {
262
+ "id": "e4-3",
263
+ "key": "deliverySchedule",
264
+ "value": "{'type': 'potential_consolidation', 'earliestDeliveryTime': requestedDeliveryDate1, 'latestDeliveryTime': requestedDeliveryDate2, 'customerConfirmationRequired': true}"
265
+ },
266
+ {
267
+ "id": "e4-4",
268
+ "key": "additionalReview",
269
+ "value": "'required'"
270
+ },
271
+ {
272
+ "id": "e4-5",
273
+ "key": "costSavingsReport",
274
+ "value": "{'fuelSavings': expectedFuelSavings * 0.7, 'laborSavings': costSavingEstimate * 0.6, 'totalSavings': (expectedFuelSavings * 0.7) + (costSavingEstimate * 0.6)}"
275
+ }
276
+ ],
277
+ "passThrough": true,
278
+ "inputField": null,
279
+ "outputPath": null,
280
+ "executionMode": "single"
281
+ },
282
+ "id": "ex4",
283
+ "name": "lowPriorityProcessing",
284
+ "position": {
285
+ "x": 1390,
286
+ "y": 163
287
+ }
288
+ },
289
+ {
290
+ "type": "expressionNode",
291
+ "content": {
292
+ "expressions": [
293
+ {
294
+ "id": "e5-1",
295
+ "key": "consolidationAction",
296
+ "value": "'no_consolidation'"
297
+ },
298
+ {
299
+ "id": "e5-2",
300
+ "key": "schedulingPriority",
301
+ "value": "'standard'"
302
+ },
303
+ {
304
+ "id": "e5-3",
305
+ "key": "deliverySchedule",
306
+ "value": "{'type': 'separate', 'order1DeliveryTime': requestedDeliveryDate1, 'order2DeliveryTime': requestedDeliveryDate2}"
307
+ },
308
+ {
309
+ "id": "e5-4",
310
+ "key": "costSavingsReport",
311
+ "value": "{'fuelSavings': 0, 'laborSavings': 0, 'totalSavings': 0}"
312
+ }
313
+ ],
314
+ "passThrough": true,
315
+ "inputField": null,
316
+ "outputPath": null,
317
+ "executionMode": "single"
318
+ },
319
+ "id": "ex5",
320
+ "name": "standardProcessing",
321
+ "position": {
322
+ "x": 1390,
323
+ "y": 261
324
+ }
325
+ }
326
+ ],
327
+ "edges": [
328
+ {
329
+ "id": "ed1",
330
+ "sourceId": "ip1",
331
+ "targetId": "dt1",
332
+ "type": "edge"
333
+ },
334
+ {
335
+ "id": "ed2",
336
+ "sourceId": "dt1",
337
+ "targetId": "ex1",
338
+ "type": "edge"
339
+ },
340
+ {
341
+ "id": "ed3",
342
+ "sourceId": "ex1",
343
+ "targetId": "sw1",
344
+ "type": "edge"
345
+ },
346
+ {
347
+ "id": "ed4",
348
+ "sourceId": "sw1",
349
+ "targetId": "ex2",
350
+ "sourceHandle": "s1-1",
351
+ "type": "edge"
352
+ },
353
+ {
354
+ "id": "ed5",
355
+ "sourceId": "sw1",
356
+ "targetId": "ex3",
357
+ "sourceHandle": "s1-2",
358
+ "type": "edge"
359
+ },
360
+ {
361
+ "id": "ed6",
362
+ "sourceId": "sw1",
363
+ "targetId": "ex4",
364
+ "sourceHandle": "s1-3",
365
+ "type": "edge"
366
+ },
367
+ {
368
+ "id": "ed7",
369
+ "sourceId": "sw1",
370
+ "targetId": "ex5",
371
+ "sourceHandle": "s1-4",
372
+ "type": "edge"
373
+ }
374
+ ]
375
+ }