UncountablePythonSDK 0.0.83__py3-none-any.whl → 0.0.132__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 UncountablePythonSDK might be problematic. Click here for more details.

Files changed (298) hide show
  1. docs/conf.py +54 -7
  2. docs/index.md +107 -4
  3. docs/integration_examples/create_ingredient.md +43 -0
  4. docs/integration_examples/create_output.md +56 -0
  5. docs/integration_examples/index.md +6 -0
  6. docs/justfile +2 -2
  7. docs/requirements.txt +6 -4
  8. examples/basic_auth.py +7 -0
  9. examples/create_ingredient_sdk.py +34 -0
  10. examples/download_files.py +26 -0
  11. examples/integration-server/jobs/materials_auto/concurrent_cron.py +11 -0
  12. examples/integration-server/jobs/materials_auto/example_cron.py +3 -0
  13. examples/integration-server/jobs/materials_auto/example_http.py +47 -0
  14. examples/integration-server/jobs/materials_auto/example_instrument.py +100 -0
  15. examples/integration-server/jobs/materials_auto/example_parse.py +140 -0
  16. examples/integration-server/jobs/materials_auto/example_predictions.py +61 -0
  17. examples/integration-server/jobs/materials_auto/example_runsheet_wh.py +39 -0
  18. examples/integration-server/jobs/materials_auto/example_wh.py +17 -9
  19. examples/integration-server/jobs/materials_auto/profile.yaml +61 -0
  20. examples/integration-server/pyproject.toml +10 -10
  21. examples/oauth.py +7 -0
  22. examples/set_recipe_metadata_file.py +1 -1
  23. examples/upload_files.py +1 -2
  24. pkgs/argument_parser/__init__.py +8 -0
  25. pkgs/argument_parser/_is_namedtuple.py +3 -0
  26. pkgs/argument_parser/argument_parser.py +196 -63
  27. pkgs/filesystem_utils/__init__.py +1 -0
  28. pkgs/filesystem_utils/_blob_session.py +144 -0
  29. pkgs/filesystem_utils/_gdrive_session.py +5 -5
  30. pkgs/filesystem_utils/_s3_session.py +2 -1
  31. pkgs/filesystem_utils/_sftp_session.py +6 -3
  32. pkgs/filesystem_utils/file_type_utils.py +30 -10
  33. pkgs/serialization/__init__.py +7 -2
  34. pkgs/serialization/annotation.py +64 -0
  35. pkgs/serialization/missing_sentry.py +1 -1
  36. pkgs/serialization/opaque_key.py +1 -1
  37. pkgs/serialization/serial_alias.py +47 -0
  38. pkgs/serialization/serial_class.py +40 -48
  39. pkgs/serialization/serial_generic.py +16 -0
  40. pkgs/serialization/serial_union.py +16 -16
  41. pkgs/serialization_util/__init__.py +6 -0
  42. pkgs/serialization_util/dataclasses.py +14 -0
  43. pkgs/serialization_util/serialization_helpers.py +15 -5
  44. pkgs/type_spec/actions_registry/__main__.py +0 -4
  45. pkgs/type_spec/actions_registry/emit_typescript.py +2 -4
  46. pkgs/type_spec/builder.py +248 -70
  47. pkgs/type_spec/builder_types.py +9 -0
  48. pkgs/type_spec/config.py +40 -7
  49. pkgs/type_spec/cross_output_links.py +99 -0
  50. pkgs/type_spec/emit_open_api.py +121 -34
  51. pkgs/type_spec/emit_open_api_util.py +5 -5
  52. pkgs/type_spec/emit_python.py +277 -86
  53. pkgs/type_spec/emit_typescript.py +102 -29
  54. pkgs/type_spec/emit_typescript_util.py +66 -10
  55. pkgs/type_spec/load_types.py +16 -3
  56. pkgs/type_spec/non_discriminated_union_exceptions.py +14 -0
  57. pkgs/type_spec/open_api_util.py +29 -4
  58. pkgs/type_spec/parts/base.py.prepart +11 -8
  59. pkgs/type_spec/parts/base.ts.prepart +4 -0
  60. pkgs/type_spec/type_info/__main__.py +3 -1
  61. pkgs/type_spec/type_info/emit_type_info.py +115 -22
  62. pkgs/type_spec/ui_entry_actions/__init__.py +4 -0
  63. pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py +308 -0
  64. pkgs/type_spec/util.py +3 -3
  65. pkgs/type_spec/value_spec/__main__.py +26 -9
  66. pkgs/type_spec/value_spec/convert_type.py +18 -0
  67. pkgs/type_spec/value_spec/emit_python.py +13 -3
  68. pkgs/type_spec/value_spec/types.py +1 -1
  69. uncountable/core/async_batch.py +1 -1
  70. uncountable/core/client.py +133 -34
  71. uncountable/core/environment.py +3 -3
  72. uncountable/core/file_upload.py +39 -15
  73. uncountable/integration/cli.py +116 -23
  74. uncountable/integration/construct_client.py +3 -3
  75. uncountable/integration/executors/executors.py +12 -2
  76. uncountable/integration/executors/generic_upload_executor.py +66 -14
  77. uncountable/integration/http_server/__init__.py +5 -0
  78. uncountable/integration/http_server/types.py +69 -0
  79. uncountable/integration/job.py +192 -7
  80. uncountable/integration/queue_runner/command_server/__init__.py +4 -0
  81. uncountable/integration/queue_runner/command_server/command_client.py +65 -0
  82. uncountable/integration/queue_runner/command_server/command_server.py +83 -5
  83. uncountable/integration/queue_runner/command_server/constants.py +4 -0
  84. uncountable/integration/queue_runner/command_server/protocol/command_server.proto +36 -0
  85. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +28 -11
  86. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +77 -1
  87. uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +135 -0
  88. uncountable/integration/queue_runner/command_server/types.py +25 -2
  89. uncountable/integration/queue_runner/datastore/datastore_sqlite.py +168 -11
  90. uncountable/integration/queue_runner/datastore/interface.py +10 -0
  91. uncountable/integration/queue_runner/datastore/model.py +8 -1
  92. uncountable/integration/queue_runner/job_scheduler.py +63 -23
  93. uncountable/integration/queue_runner/queue_runner.py +10 -2
  94. uncountable/integration/queue_runner/worker.py +3 -5
  95. uncountable/integration/scan_profiles.py +1 -1
  96. uncountable/integration/scheduler.py +74 -25
  97. uncountable/integration/secret_retrieval/retrieve_secret.py +1 -1
  98. uncountable/integration/server.py +42 -12
  99. uncountable/integration/telemetry.py +63 -10
  100. uncountable/integration/webhook_server/entrypoint.py +39 -112
  101. uncountable/types/__init__.py +58 -1
  102. uncountable/types/api/batch/execute_batch.py +5 -6
  103. uncountable/types/api/batch/execute_batch_load_async.py +2 -3
  104. uncountable/types/api/chemical/convert_chemical_formats.py +10 -5
  105. uncountable/types/api/condition_parameters/__init__.py +1 -0
  106. uncountable/types/api/condition_parameters/upsert_condition_match.py +72 -0
  107. uncountable/types/api/entity/create_entities.py +7 -7
  108. uncountable/types/api/entity/create_entity.py +8 -8
  109. uncountable/types/api/entity/create_or_update_entity.py +48 -0
  110. uncountable/types/api/entity/export_entities.py +59 -0
  111. uncountable/types/api/entity/get_entities_data.py +3 -4
  112. uncountable/types/api/entity/grant_entity_permissions.py +6 -6
  113. uncountable/types/api/entity/list_aggregate.py +79 -0
  114. uncountable/types/api/entity/list_entities.py +34 -10
  115. uncountable/types/api/entity/lock_entity.py +4 -4
  116. uncountable/types/api/entity/lookup_entity.py +116 -0
  117. uncountable/types/api/entity/resolve_entity_ids.py +5 -6
  118. uncountable/types/api/entity/set_entity_field_values.py +44 -0
  119. uncountable/types/api/entity/set_values.py +3 -3
  120. uncountable/types/api/entity/transition_entity_phase.py +14 -7
  121. uncountable/types/api/entity/unlock_entity.py +3 -3
  122. uncountable/types/api/equipment/associate_equipment_input.py +2 -3
  123. uncountable/types/api/field_options/upsert_field_options.py +7 -7
  124. uncountable/types/api/files/__init__.py +1 -0
  125. uncountable/types/api/files/download_file.py +77 -0
  126. uncountable/types/api/id_source/list_id_source.py +6 -7
  127. uncountable/types/api/id_source/match_id_source.py +4 -5
  128. uncountable/types/api/input_groups/get_input_group_names.py +3 -4
  129. uncountable/types/api/inputs/create_inputs.py +10 -9
  130. uncountable/types/api/inputs/get_input_data.py +11 -12
  131. uncountable/types/api/inputs/get_input_names.py +6 -7
  132. uncountable/types/api/inputs/get_inputs_data.py +6 -7
  133. uncountable/types/api/inputs/set_input_attribute_values.py +5 -6
  134. uncountable/types/api/inputs/set_input_category.py +5 -5
  135. uncountable/types/api/inputs/set_input_subcategories.py +3 -3
  136. uncountable/types/api/inputs/set_intermediate_type.py +4 -4
  137. uncountable/types/api/integrations/__init__.py +1 -0
  138. uncountable/types/api/integrations/publish_realtime_data.py +41 -0
  139. uncountable/types/api/integrations/push_notification.py +49 -0
  140. uncountable/types/api/integrations/register_sockets_token.py +41 -0
  141. uncountable/types/api/listing/__init__.py +1 -0
  142. uncountable/types/api/listing/fetch_listing.py +58 -0
  143. uncountable/types/api/material_families/update_entity_material_families.py +3 -4
  144. uncountable/types/api/notebooks/__init__.py +1 -0
  145. uncountable/types/api/notebooks/add_notebook_content.py +119 -0
  146. uncountable/types/api/outputs/get_output_data.py +12 -13
  147. uncountable/types/api/outputs/get_output_names.py +5 -6
  148. uncountable/types/api/outputs/get_output_organization.py +173 -0
  149. uncountable/types/api/outputs/resolve_output_conditions.py +7 -8
  150. uncountable/types/api/permissions/set_core_permissions.py +16 -10
  151. uncountable/types/api/project/get_projects.py +6 -7
  152. uncountable/types/api/project/get_projects_data.py +7 -8
  153. uncountable/types/api/recipe_links/create_recipe_link.py +5 -5
  154. uncountable/types/api/recipe_links/remove_recipe_link.py +4 -4
  155. uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +6 -7
  156. uncountable/types/api/recipes/add_recipe_to_project.py +3 -3
  157. uncountable/types/api/recipes/add_time_series_data.py +64 -0
  158. uncountable/types/api/recipes/archive_recipes.py +4 -4
  159. uncountable/types/api/recipes/associate_recipe_as_input.py +5 -5
  160. uncountable/types/api/recipes/associate_recipe_as_lot.py +3 -3
  161. uncountable/types/api/recipes/clear_recipe_outputs.py +3 -3
  162. uncountable/types/api/recipes/create_mix_order.py +44 -0
  163. uncountable/types/api/recipes/create_recipe.py +8 -9
  164. uncountable/types/api/recipes/create_recipes.py +8 -9
  165. uncountable/types/api/recipes/disassociate_recipe_as_input.py +3 -3
  166. uncountable/types/api/recipes/edit_recipe_inputs.py +101 -24
  167. uncountable/types/api/recipes/get_column_calculation_values.py +4 -5
  168. uncountable/types/api/recipes/get_curve.py +4 -5
  169. uncountable/types/api/recipes/get_recipe_calculations.py +6 -7
  170. uncountable/types/api/recipes/get_recipe_links.py +3 -4
  171. uncountable/types/api/recipes/get_recipe_names.py +3 -4
  172. uncountable/types/api/recipes/get_recipe_output_metadata.py +5 -6
  173. uncountable/types/api/recipes/get_recipes_data.py +62 -34
  174. uncountable/types/api/recipes/lock_recipes.py +9 -8
  175. uncountable/types/api/recipes/remove_recipe_from_project.py +3 -3
  176. uncountable/types/api/recipes/set_recipe_inputs.py +9 -10
  177. uncountable/types/api/recipes/set_recipe_metadata.py +3 -3
  178. uncountable/types/api/recipes/set_recipe_output_annotations.py +11 -12
  179. uncountable/types/api/recipes/set_recipe_output_file.py +5 -6
  180. uncountable/types/api/recipes/set_recipe_outputs.py +24 -13
  181. uncountable/types/api/recipes/set_recipe_tags.py +14 -9
  182. uncountable/types/api/recipes/set_recipe_total.py +59 -0
  183. uncountable/types/api/recipes/unarchive_recipes.py +3 -3
  184. uncountable/types/api/recipes/unlock_recipes.py +7 -6
  185. uncountable/types/api/runsheet/__init__.py +1 -0
  186. uncountable/types/api/runsheet/complete_async_upload.py +41 -0
  187. uncountable/types/api/triggers/run_trigger.py +4 -4
  188. uncountable/types/api/uploader/complete_async_parse.py +46 -0
  189. uncountable/types/api/uploader/invoke_uploader.py +4 -5
  190. uncountable/types/api/user/__init__.py +1 -0
  191. uncountable/types/api/user/get_current_user_info.py +40 -0
  192. uncountable/types/async_batch.py +1 -1
  193. uncountable/types/async_batch_processor.py +506 -23
  194. uncountable/types/async_batch_t.py +35 -8
  195. uncountable/types/async_jobs.py +0 -1
  196. uncountable/types/async_jobs_t.py +1 -2
  197. uncountable/types/auth_retrieval.py +0 -1
  198. uncountable/types/auth_retrieval_t.py +6 -6
  199. uncountable/types/base.py +0 -1
  200. uncountable/types/base_t.py +11 -9
  201. uncountable/types/calculations.py +0 -1
  202. uncountable/types/calculations_t.py +1 -2
  203. uncountable/types/chemical_structure.py +0 -1
  204. uncountable/types/chemical_structure_t.py +5 -5
  205. uncountable/types/client_base.py +614 -69
  206. uncountable/types/client_config.py +1 -1
  207. uncountable/types/client_config_t.py +13 -3
  208. uncountable/types/curves.py +0 -1
  209. uncountable/types/curves_t.py +6 -7
  210. uncountable/types/data.py +12 -0
  211. uncountable/types/data_t.py +103 -0
  212. uncountable/types/entity.py +1 -1
  213. uncountable/types/entity_t.py +90 -10
  214. uncountable/types/experiment_groups.py +0 -1
  215. uncountable/types/experiment_groups_t.py +1 -2
  216. uncountable/types/exports.py +8 -0
  217. uncountable/types/exports_t.py +34 -0
  218. uncountable/types/field_values.py +19 -1
  219. uncountable/types/field_values_t.py +242 -9
  220. uncountable/types/fields.py +0 -1
  221. uncountable/types/fields_t.py +1 -2
  222. uncountable/types/generic_upload.py +0 -1
  223. uncountable/types/generic_upload_t.py +14 -14
  224. uncountable/types/id_source.py +0 -1
  225. uncountable/types/id_source_t.py +13 -7
  226. uncountable/types/identifier.py +0 -1
  227. uncountable/types/identifier_t.py +10 -5
  228. uncountable/types/input_attributes.py +0 -1
  229. uncountable/types/input_attributes_t.py +3 -4
  230. uncountable/types/inputs.py +0 -1
  231. uncountable/types/inputs_t.py +3 -4
  232. uncountable/types/integration_server.py +0 -1
  233. uncountable/types/integration_server_t.py +13 -4
  234. uncountable/types/integration_session.py +10 -0
  235. uncountable/types/integration_session_t.py +60 -0
  236. uncountable/types/integrations.py +10 -0
  237. uncountable/types/integrations_t.py +62 -0
  238. uncountable/types/job_definition.py +2 -1
  239. uncountable/types/job_definition_t.py +57 -32
  240. uncountable/types/listing.py +9 -0
  241. uncountable/types/listing_t.py +51 -0
  242. uncountable/types/notices.py +8 -0
  243. uncountable/types/notices_t.py +37 -0
  244. uncountable/types/notifications.py +11 -0
  245. uncountable/types/notifications_t.py +74 -0
  246. uncountable/types/outputs.py +0 -1
  247. uncountable/types/outputs_t.py +2 -3
  248. uncountable/types/overrides.py +0 -1
  249. uncountable/types/overrides_t.py +10 -4
  250. uncountable/types/permissions.py +0 -1
  251. uncountable/types/permissions_t.py +1 -2
  252. uncountable/types/phases.py +0 -1
  253. uncountable/types/phases_t.py +1 -2
  254. uncountable/types/post_base.py +0 -1
  255. uncountable/types/post_base_t.py +1 -2
  256. uncountable/types/queued_job.py +2 -1
  257. uncountable/types/queued_job_t.py +29 -12
  258. uncountable/types/recipe_identifiers.py +0 -1
  259. uncountable/types/recipe_identifiers_t.py +18 -8
  260. uncountable/types/recipe_inputs.py +0 -1
  261. uncountable/types/recipe_inputs_t.py +1 -2
  262. uncountable/types/recipe_links.py +0 -1
  263. uncountable/types/recipe_links_t.py +3 -4
  264. uncountable/types/recipe_metadata.py +0 -1
  265. uncountable/types/recipe_metadata_t.py +9 -10
  266. uncountable/types/recipe_output_metadata.py +0 -1
  267. uncountable/types/recipe_output_metadata_t.py +1 -2
  268. uncountable/types/recipe_tags.py +0 -1
  269. uncountable/types/recipe_tags_t.py +1 -2
  270. uncountable/types/recipe_workflow_steps.py +0 -1
  271. uncountable/types/recipe_workflow_steps_t.py +7 -7
  272. uncountable/types/recipes.py +0 -1
  273. uncountable/types/recipes_t.py +2 -2
  274. uncountable/types/response.py +0 -1
  275. uncountable/types/response_t.py +2 -2
  276. uncountable/types/secret_retrieval.py +0 -1
  277. uncountable/types/secret_retrieval_t.py +7 -7
  278. uncountable/types/sockets.py +20 -0
  279. uncountable/types/sockets_t.py +169 -0
  280. uncountable/types/structured_filters.py +25 -0
  281. uncountable/types/structured_filters_t.py +248 -0
  282. uncountable/types/units.py +0 -1
  283. uncountable/types/units_t.py +1 -2
  284. uncountable/types/uploader.py +24 -0
  285. uncountable/types/uploader_t.py +222 -0
  286. uncountable/types/users.py +0 -1
  287. uncountable/types/users_t.py +1 -2
  288. uncountable/types/webhook_job.py +1 -1
  289. uncountable/types/webhook_job_t.py +14 -3
  290. uncountable/types/workflows.py +0 -1
  291. uncountable/types/workflows_t.py +3 -4
  292. uncountablepythonsdk-0.0.132.dist-info/METADATA +64 -0
  293. uncountablepythonsdk-0.0.132.dist-info/RECORD +363 -0
  294. {UncountablePythonSDK-0.0.83.dist-info → uncountablepythonsdk-0.0.132.dist-info}/WHEEL +1 -1
  295. UncountablePythonSDK-0.0.83.dist-info/METADATA +0 -60
  296. UncountablePythonSDK-0.0.83.dist-info/RECORD +0 -292
  297. docs/quickstart.md +0 -19
  298. {UncountablePythonSDK-0.0.83.dist-info → uncountablepythonsdk-0.0.132.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  import signal
2
2
  from dataclasses import asdict
3
3
  from types import TracebackType
4
- from typing import Optional, assert_never
4
+ from typing import assert_never
5
5
 
6
6
  from apscheduler.executors.pool import ThreadPoolExecutor
7
7
  from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
@@ -11,16 +11,28 @@ from apscheduler.triggers.cron import CronTrigger
11
11
  from opentelemetry.trace import get_current_span
12
12
  from sqlalchemy.engine.base import Engine
13
13
 
14
+ from uncountable.core.environment import get_local_admin_server_port
14
15
  from uncountable.integration.cron import CronJobArgs, cron_job_executor
16
+ from uncountable.integration.queue_runner.command_server.command_client import (
17
+ send_vaccuum_queued_jobs_message,
18
+ )
15
19
  from uncountable.integration.telemetry import Logger
16
20
  from uncountable.types import base_t, job_definition_t
17
21
  from uncountable.types.job_definition_t import (
18
22
  CronJobDefinition,
19
- WebhookJobDefinition,
23
+ HttpJobDefinitionBase,
20
24
  )
21
25
 
22
26
  _MAX_APSCHEDULER_CONCURRENT_JOBS = 1
23
27
 
28
+ VACCUUM_QUEUED_JOBS_JOB_ID = "vacuum_queued_jobs"
29
+
30
+ STATIC_JOB_IDS = {VACCUUM_QUEUED_JOBS_JOB_ID}
31
+
32
+
33
+ def vaccuum_queued_jobs() -> None:
34
+ send_vaccuum_queued_jobs_message(port=get_local_admin_server_port())
35
+
24
36
 
25
37
  class IntegrationServer:
26
38
  _scheduler: BaseScheduler
@@ -36,11 +48,27 @@ class IntegrationServer:
36
48
  )
37
49
  self._server_logger = Logger(get_current_span())
38
50
 
51
+ def _register_static_jobs(self) -> None:
52
+ all_job_ids = {job.id for job in self._scheduler.get_jobs()}
53
+ if VACCUUM_QUEUED_JOBS_JOB_ID in all_job_ids:
54
+ self._scheduler.remove_job(VACCUUM_QUEUED_JOBS_JOB_ID)
55
+
56
+ self._scheduler.add_job(
57
+ vaccuum_queued_jobs,
58
+ max_instances=1,
59
+ coalesce=True,
60
+ trigger=CronTrigger.from_crontab("5 4 * * 4"),
61
+ name="Vaccuum queued jobs",
62
+ id=VACCUUM_QUEUED_JOBS_JOB_ID,
63
+ kwargs={},
64
+ misfire_grace_time=None,
65
+ )
66
+
39
67
  def register_jobs(self, profiles: list[job_definition_t.ProfileMetadata]) -> None:
40
- valid_job_ids = []
68
+ valid_job_ids: set[str] = set()
41
69
  for profile_metadata in profiles:
42
70
  for job_defn in profile_metadata.jobs:
43
- valid_job_ids.append(job_defn.id)
71
+ valid_job_ids.add(job_defn.id)
44
72
  match job_defn:
45
73
  case CronJobDefinition():
46
74
  # Add to ap scheduler
@@ -86,14 +114,15 @@ class IntegrationServer:
86
114
  misfire_grace_time=None,
87
115
  **job_opts,
88
116
  )
89
- case WebhookJobDefinition():
117
+ case HttpJobDefinitionBase():
90
118
  pass
91
119
  case _:
92
120
  assert_never(job_defn)
93
- all_jobs = self._scheduler.get_jobs()
94
- for job in all_jobs:
95
- if job.id not in valid_job_ids:
96
- self._scheduler.remove_job(job.id)
121
+ all_job_ids = {job.id for job in self._scheduler.get_jobs()}
122
+ invalid_job_ids = all_job_ids.difference(valid_job_ids.union(STATIC_JOB_IDS))
123
+
124
+ for job_id in invalid_job_ids:
125
+ self._scheduler.remove_job(job_id)
97
126
 
98
127
  def serve_forever(self) -> None:
99
128
  signal.pause()
@@ -106,12 +135,13 @@ class IntegrationServer:
106
135
 
107
136
  def __enter__(self) -> "IntegrationServer":
108
137
  self._start_apscheduler()
138
+ self._register_static_jobs()
109
139
  return self
110
140
 
111
141
  def __exit__(
112
142
  self,
113
- exc_type: Optional[type[BaseException]],
114
- exc_val: Optional[BaseException],
115
- exc_tb: Optional[TracebackType],
143
+ exc_type: type[BaseException] | None,
144
+ exc_val: BaseException | None,
145
+ exc_tb: TracebackType | None,
116
146
  ) -> None:
117
147
  self._stop_apscheduler()
@@ -1,4 +1,5 @@
1
1
  import functools
2
+ import json
2
3
  import os
3
4
  import time
4
5
  import traceback
@@ -11,7 +12,10 @@ from opentelemetry import _logs, trace
11
12
  from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
12
13
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
13
14
  from opentelemetry.sdk._logs import Logger as OTELLogger
14
- from opentelemetry.sdk._logs import LoggerProvider, LogRecord
15
+ from opentelemetry.sdk._logs import (
16
+ LoggerProvider,
17
+ LogRecord,
18
+ )
15
19
  from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter
16
20
  from opentelemetry.sdk.resources import Attributes, Resource
17
21
  from opentelemetry.sdk.trace import TracerProvider
@@ -32,6 +36,11 @@ def _cast_attributes(attributes: dict[str, base_t.JsonValue]) -> Attributes:
32
36
  return cast(Attributes, attributes)
33
37
 
34
38
 
39
+ def one_line_formatter(record: LogRecord) -> str:
40
+ json_data = record.to_json()
41
+ return json.dumps(json.loads(json_data), separators=(",", ":")) + "\n"
42
+
43
+
35
44
  @functools.cache
36
45
  def get_otel_resource() -> Resource:
37
46
  attributes: dict[str, base_t.JsonValue] = {
@@ -60,7 +69,9 @@ def get_otel_tracer() -> Tracer:
60
69
  @functools.cache
61
70
  def get_otel_logger() -> OTELLogger:
62
71
  provider = LoggerProvider(resource=get_otel_resource())
63
- provider.add_log_record_processor(BatchLogRecordProcessor(ConsoleLogExporter()))
72
+ provider.add_log_record_processor(
73
+ BatchLogRecordProcessor(ConsoleLogExporter(formatter=one_line_formatter))
74
+ )
64
75
  if get_otel_enabled():
65
76
  provider.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter()))
66
77
  _logs.set_logger_provider(provider)
@@ -87,8 +98,27 @@ class Logger:
87
98
  def current_trace_id(self) -> int | None:
88
99
  return self.current_span.get_span_context().trace_id
89
100
 
90
- def _patch_attributes(self, attributes: Attributes | None) -> Attributes:
91
- return attributes or {}
101
+ def _patch_attributes(
102
+ self,
103
+ attributes: Attributes | None,
104
+ *,
105
+ message: str | None = None,
106
+ severity: LogSeverity | None = None,
107
+ ) -> Attributes:
108
+ patched_attributes = {**(attributes if attributes is not None else {})}
109
+ if message is not None:
110
+ patched_attributes["message"] = message
111
+ elif "body" in patched_attributes:
112
+ patched_attributes["message"] = patched_attributes["body"]
113
+
114
+ if severity is not None:
115
+ patched_attributes["status"] = severity.lower()
116
+ elif "severity_text" in patched_attributes and isinstance(
117
+ patched_attributes["severity_text"], str
118
+ ):
119
+ patched_attributes["status"] = patched_attributes["severity_text"].lower()
120
+
121
+ return patched_attributes
92
122
 
93
123
  def _emit_log(
94
124
  self, message: str, *, severity: LogSeverity, attributes: Attributes | None
@@ -98,7 +128,9 @@ class Logger:
98
128
  body=message,
99
129
  severity_text=severity,
100
130
  timestamp=time.time_ns(),
101
- attributes=self._patch_attributes(attributes),
131
+ attributes=self._patch_attributes(
132
+ message=message, severity=severity, attributes=attributes
133
+ ),
102
134
  span_id=self.current_span_id,
103
135
  trace_id=self.current_trace_id,
104
136
  trace_flags=DEFAULT_TRACE_OPTIONS,
@@ -131,8 +163,10 @@ class Logger:
131
163
  message: str | None = None,
132
164
  attributes: Attributes | None = None,
133
165
  ) -> None:
134
- traceback_str = "".join(traceback.format_tb(exception.__traceback__))
135
- patched_attributes = self._patch_attributes(attributes)
166
+ traceback_str = "".join(traceback.format_exception(exception))
167
+ patched_attributes = self._patch_attributes(
168
+ message=message, severity=LogSeverity.ERROR, attributes=attributes
169
+ )
136
170
  self.current_span.record_exception(
137
171
  exception=exception, attributes=patched_attributes
138
172
  )
@@ -163,9 +197,17 @@ class JobLogger(Logger):
163
197
  self.job_definition = job_definition
164
198
  super().__init__(base_span)
165
199
 
166
- def _patch_attributes(self, attributes: Attributes | None) -> Attributes:
200
+ def _patch_attributes(
201
+ self,
202
+ attributes: Attributes | None,
203
+ *,
204
+ message: str | None = None,
205
+ severity: LogSeverity | None = None,
206
+ ) -> Attributes:
167
207
  patched_attributes: dict[str, base_t.JsonValue] = {
168
- **(attributes if attributes is not None else {})
208
+ **super()._patch_attributes(
209
+ attributes=attributes, message=message, severity=severity
210
+ )
169
211
  }
170
212
  patched_attributes["profile.name"] = self.profile_metadata.name
171
213
  patched_attributes["profile.base_url"] = self.profile_metadata.base_url
@@ -177,7 +219,7 @@ class JobLogger(Logger):
177
219
  patched_attributes["job.definition.cron_spec"] = (
178
220
  self.job_definition.cron_spec
179
221
  )
180
- case job_definition_t.WebhookJobDefinition():
222
+ case job_definition_t.HttpJobDefinitionBase():
181
223
  pass
182
224
  case _:
183
225
  assert_never(self.job_definition)
@@ -196,3 +238,14 @@ class JobLogger(Logger):
196
238
  case _:
197
239
  assert_never(self.job_definition.executor)
198
240
  return _cast_attributes(patched_attributes)
241
+
242
+
243
+ @contextmanager
244
+ def push_scope_optional(
245
+ logger: Logger | None, scope_name: str, *, attributes: Attributes | None = None
246
+ ) -> Generator[None, None, None]:
247
+ if logger is None:
248
+ yield
249
+ else:
250
+ with logger.push_scope(scope_name, attributes=attributes):
251
+ yield
@@ -1,146 +1,71 @@
1
- import hmac
2
- import typing
3
- from dataclasses import dataclass
1
+ import base64
4
2
 
5
3
  import flask
6
- import simplejson
7
4
  from flask.typing import ResponseReturnValue
8
- from flask.wrappers import Response
9
5
  from opentelemetry.trace import get_current_span
10
6
  from uncountable.core.environment import (
11
- get_local_admin_server_port,
7
+ get_http_server_port,
12
8
  get_server_env,
13
- get_webhook_server_port,
14
- )
15
- from uncountable.integration.queue_runner.command_server.command_client import (
16
- send_job_queue_message,
17
- )
18
- from uncountable.integration.queue_runner.command_server.types import (
19
- CommandServerException,
20
9
  )
10
+ from uncountable.integration.executors.script_executor import resolve_script_executor
11
+ from uncountable.integration.http_server import GenericHttpRequest, HttpException
12
+ from uncountable.integration.job import CustomHttpJob, WebhookJob
21
13
  from uncountable.integration.scan_profiles import load_profiles
22
- from uncountable.integration.secret_retrieval.retrieve_secret import retrieve_secret
23
14
  from uncountable.integration.telemetry import Logger
24
- from uncountable.types import base_t, job_definition_t, queued_job_t, webhook_job_t
25
-
26
- from pkgs.argument_parser import CachedParser
15
+ from uncountable.types import job_definition_t
27
16
 
28
17
  app = flask.Flask(__name__)
29
18
 
30
19
 
31
- @dataclass(kw_only=True)
32
- class WebhookResponse:
33
- pass
34
-
35
-
36
- webhook_payload_parser = CachedParser(webhook_job_t.WebhookEventBody)
37
-
38
-
39
- class WebhookException(BaseException):
40
- error_code: int
41
- message: str
42
-
43
- def __init__(self, *, error_code: int, message: str) -> None:
44
- self.error_code = error_code
45
- self.message = message
46
-
47
- @staticmethod
48
- def payload_failed_signature() -> "WebhookException":
49
- return WebhookException(
50
- error_code=401, message="webhook payload did not match signature"
51
- )
52
-
53
- @staticmethod
54
- def no_signature_passed() -> "WebhookException":
55
- return WebhookException(error_code=400, message="missing signature")
56
-
57
- @staticmethod
58
- def body_parse_error() -> "WebhookException":
59
- return WebhookException(error_code=400, message="body parse error")
60
-
61
- @staticmethod
62
- def unknown_error() -> "WebhookException":
63
- return WebhookException(error_code=500, message="internal server error")
64
-
65
- def __str__(self) -> str:
66
- return f"[{self.error_code}]: {self.message}"
67
-
68
- def make_error_response(self) -> Response:
69
- return Response(
70
- status=self.error_code, response={"error": {"message": str(self)}}
71
- )
72
-
73
-
74
- def _parse_webhook_payload(
75
- *, raw_request_body: bytes, signature_key: str, passed_signature: str
76
- ) -> base_t.JsonValue:
77
- request_body_signature = hmac.new(
78
- signature_key.encode("utf-8"), msg=raw_request_body, digestmod="sha256"
79
- ).hexdigest()
80
-
81
- if request_body_signature != passed_signature:
82
- raise WebhookException.payload_failed_signature()
83
-
84
- try:
85
- request_body = simplejson.loads(raw_request_body.decode())
86
- return typing.cast(base_t.JsonValue, request_body)
87
- except (simplejson.JSONDecodeError, ValueError) as e:
88
- raise WebhookException.body_parse_error() from e
89
-
90
-
91
20
  def register_route(
92
21
  *,
93
22
  server_logger: Logger,
94
23
  profile_meta: job_definition_t.ProfileMetadata,
95
- job: job_definition_t.WebhookJobDefinition,
24
+ job: job_definition_t.HttpJobDefinitionBase,
96
25
  ) -> None:
97
26
  route = f"/{profile_meta.name}/{job.id}"
98
27
 
99
- def handle_webhook() -> ResponseReturnValue:
28
+ def handle_request() -> ResponseReturnValue:
100
29
  with server_logger.push_scope(route):
101
30
  try:
102
- signature_key = retrieve_secret(
103
- profile_metadata=profile_meta,
104
- secret_retrieval=job.signature_key_secret,
31
+ if not isinstance(job.executor, job_definition_t.JobExecutorScript):
32
+ raise HttpException.configuration_error(
33
+ message="[internal] http job must use a script executor"
34
+ )
35
+ job_instance = resolve_script_executor(
36
+ executor=job.executor, profile_metadata=profile_meta
105
37
  )
106
-
107
- passed_signature = flask.request.headers.get(
108
- "Uncountable-Webhook-Signature"
38
+ if not isinstance(job_instance, (CustomHttpJob, WebhookJob)):
39
+ raise HttpException.configuration_error(
40
+ message="[internal] http job must descend from CustomHttpJob"
41
+ )
42
+ http_request = GenericHttpRequest(
43
+ body_base64=base64.b64encode(flask.request.get_data()).decode(),
44
+ headers=dict(flask.request.headers),
109
45
  )
110
- if passed_signature is None:
111
- raise WebhookException.no_signature_passed()
112
-
113
- webhook_payload = _parse_webhook_payload(
114
- raw_request_body=flask.request.data,
115
- signature_key=signature_key,
116
- passed_signature=passed_signature,
46
+ job_instance.validate_request(
47
+ request=http_request, job_definition=job, profile_meta=profile_meta
48
+ )
49
+ http_response = job_instance.handle_request(
50
+ request=http_request, job_definition=job, profile_meta=profile_meta
117
51
  )
118
52
 
119
- try:
120
- send_job_queue_message(
121
- job_ref_name=job.id,
122
- payload=queued_job_t.QueuedJobPayload(
123
- invocation_context=queued_job_t.InvocationContextWebhook(
124
- webhook_payload=webhook_payload
125
- )
126
- ),
127
- port=get_local_admin_server_port(),
128
- )
129
- except CommandServerException as e:
130
- raise WebhookException.unknown_error() from e
131
-
132
- return flask.jsonify(WebhookResponse())
133
- except WebhookException as e:
53
+ return flask.make_response(
54
+ http_response.response,
55
+ http_response.status_code,
56
+ http_response.headers,
57
+ )
58
+ except HttpException as e:
134
59
  server_logger.log_exception(e)
135
60
  return e.make_error_response()
136
61
  except Exception as e:
137
62
  server_logger.log_exception(e)
138
- return WebhookException.unknown_error().make_error_response()
63
+ return HttpException.unknown_error().make_error_response()
139
64
 
140
65
  app.add_url_rule(
141
66
  route,
142
- endpoint=f"handle_webhook_{job.id}",
143
- view_func=handle_webhook,
67
+ endpoint=f"handle_request_{job.id}",
68
+ view_func=handle_request,
144
69
  methods=["POST"],
145
70
  )
146
71
 
@@ -148,11 +73,13 @@ def register_route(
148
73
 
149
74
 
150
75
  def main() -> None:
76
+ app.add_url_rule("/health", "health", lambda: ("OK", 200))
77
+
151
78
  profiles = load_profiles()
152
79
  for profile_metadata in profiles:
153
80
  server_logger = Logger(get_current_span())
154
81
  for job in profile_metadata.jobs:
155
- if isinstance(job, job_definition_t.WebhookJobDefinition):
82
+ if isinstance(job, job_definition_t.HttpJobDefinitionBase):
156
83
  register_route(
157
84
  server_logger=server_logger, profile_meta=profile_metadata, job=job
158
85
  )
@@ -164,7 +91,7 @@ main()
164
91
  if __name__ == "__main__":
165
92
  app.run(
166
93
  host="0.0.0.0",
167
- port=get_webhook_server_port(),
94
+ port=get_http_server_port(),
168
95
  debug=get_server_env() == "playground",
169
96
  exclude_patterns=[],
170
97
  )
@@ -1,9 +1,10 @@
1
1
  # DO NOT MODIFY -- This file is generated by type_spec
2
- # flake8: noqa: F821
3
2
  # ruff: noqa: E402 Q003
4
3
  # fmt: off
5
4
  # isort: skip_file
5
+ from .api.notebooks import add_notebook_content as add_notebook_content_t
6
6
  from .api.recipes import add_recipe_to_project as add_recipe_to_project_t
7
+ from .api.recipes import add_time_series_data as add_time_series_data_t
7
8
  from .api.recipes import archive_recipes as archive_recipes_t
8
9
  from .api.equipment import associate_equipment_input as associate_equipment_input_t
9
10
  from .api.recipes import associate_recipe_as_input as associate_recipe_as_input_t
@@ -16,24 +17,34 @@ from . import calculations_t as calculations_t
16
17
  from . import chemical_structure_t as chemical_structure_t
17
18
  from .api.recipes import clear_recipe_outputs as clear_recipe_outputs_t
18
19
  from . import client_config_t as client_config_t
20
+ from .api.uploader import complete_async_parse as complete_async_parse_t
21
+ from .api.runsheet import complete_async_upload as complete_async_upload_t
19
22
  from .api.chemical import convert_chemical_formats as convert_chemical_formats_t
20
23
  from .api.entity import create_entities as create_entities_t
21
24
  from .api.entity import create_entity as create_entity_t
22
25
  from .api.inputs import create_inputs as create_inputs_t
26
+ from .api.recipes import create_mix_order as create_mix_order_t
27
+ from .api.entity import create_or_update_entity as create_or_update_entity_t
23
28
  from .api.recipes import create_recipe as create_recipe_t
24
29
  from .api.recipe_links import create_recipe_link as create_recipe_link_t
25
30
  from .api.recipes import create_recipes as create_recipes_t
26
31
  from . import curves_t as curves_t
32
+ from . import data_t as data_t
27
33
  from .api.recipes import disassociate_recipe_as_input as disassociate_recipe_as_input_t
34
+ from .api.files import download_file as download_file_t
28
35
  from .api.recipes import edit_recipe_inputs as edit_recipe_inputs_t
29
36
  from . import entity_t as entity_t
30
37
  from .api.batch import execute_batch as execute_batch_t
31
38
  from .api.batch import execute_batch_load_async as execute_batch_load_async_t
32
39
  from . import experiment_groups_t as experiment_groups_t
40
+ from .api.entity import export_entities as export_entities_t
41
+ from . import exports_t as exports_t
42
+ from .api.listing import fetch_listing as fetch_listing_t
33
43
  from . import field_values_t as field_values_t
34
44
  from . import fields_t as fields_t
35
45
  from . import generic_upload_t as generic_upload_t
36
46
  from .api.recipes import get_column_calculation_values as get_column_calculation_values_t
47
+ from .api.user import get_current_user_info as get_current_user_info_t
37
48
  from .api.recipes import get_curve as get_curve_t
38
49
  from .api.entity import get_entities_data as get_entities_data_t
39
50
  from .api.inputs import get_input_data as get_input_data_t
@@ -42,6 +53,7 @@ from .api.inputs import get_input_names as get_input_names_t
42
53
  from .api.inputs import get_inputs_data as get_inputs_data_t
43
54
  from .api.outputs import get_output_data as get_output_data_t
44
55
  from .api.outputs import get_output_names as get_output_names_t
56
+ from .api.outputs import get_output_organization as get_output_organization_t
45
57
  from .api.project import get_projects as get_projects_t
46
58
  from .api.project import get_projects_data as get_projects_data_t
47
59
  from .api.recipes import get_recipe_calculations as get_recipe_calculations_t
@@ -56,18 +68,27 @@ from . import identifier_t as identifier_t
56
68
  from . import input_attributes_t as input_attributes_t
57
69
  from . import inputs_t as inputs_t
58
70
  from . import integration_server_t as integration_server_t
71
+ from . import integration_session_t as integration_session_t
72
+ from . import integrations_t as integrations_t
59
73
  from .api.uploader import invoke_uploader as invoke_uploader_t
60
74
  from . import job_definition_t as job_definition_t
75
+ from .api.entity import list_aggregate as list_aggregate_t
61
76
  from .api.entity import list_entities as list_entities_t
62
77
  from .api.id_source import list_id_source as list_id_source_t
78
+ from . import listing_t as listing_t
63
79
  from .api.entity import lock_entity as lock_entity_t
64
80
  from .api.recipes import lock_recipes as lock_recipes_t
81
+ from .api.entity import lookup_entity as lookup_entity_t
65
82
  from .api.id_source import match_id_source as match_id_source_t
83
+ from . import notices_t as notices_t
84
+ from . import notifications_t as notifications_t
66
85
  from . import outputs_t as outputs_t
67
86
  from . import overrides_t as overrides_t
68
87
  from . import permissions_t as permissions_t
69
88
  from . import phases_t as phases_t
70
89
  from . import post_base_t as post_base_t
90
+ from .api.integrations import publish_realtime_data as publish_realtime_data_t
91
+ from .api.integrations import push_notification as push_notification_t
71
92
  from . import queued_job_t as queued_job_t
72
93
  from . import recipe_identifiers_t as recipe_identifiers_t
73
94
  from . import recipe_inputs_t as recipe_inputs_t
@@ -77,6 +98,7 @@ from . import recipe_output_metadata_t as recipe_output_metadata_t
77
98
  from . import recipe_tags_t as recipe_tags_t
78
99
  from . import recipe_workflow_steps_t as recipe_workflow_steps_t
79
100
  from . import recipes_t as recipes_t
101
+ from .api.integrations import register_sockets_token as register_sockets_token_t
80
102
  from .api.recipes import remove_recipe_from_project as remove_recipe_from_project_t
81
103
  from .api.recipe_links import remove_recipe_link as remove_recipe_link_t
82
104
  from .api.entity import resolve_entity_ids as resolve_entity_ids_t
@@ -85,6 +107,7 @@ from . import response_t as response_t
85
107
  from .api.triggers import run_trigger as run_trigger_t
86
108
  from . import secret_retrieval_t as secret_retrieval_t
87
109
  from .api.permissions import set_core_permissions as set_core_permissions_t
110
+ from .api.entity import set_entity_field_values as set_entity_field_values_t
88
111
  from .api.inputs import set_input_attribute_values as set_input_attribute_values_t
89
112
  from .api.inputs import set_input_category as set_input_category_t
90
113
  from .api.inputs import set_input_subcategories as set_input_subcategories_t
@@ -95,13 +118,18 @@ from .api.recipes import set_recipe_output_annotations as set_recipe_output_anno
95
118
  from .api.recipes import set_recipe_output_file as set_recipe_output_file_t
96
119
  from .api.recipes import set_recipe_outputs as set_recipe_outputs_t
97
120
  from .api.recipes import set_recipe_tags as set_recipe_tags_t
121
+ from .api.recipes import set_recipe_total as set_recipe_total_t
98
122
  from .api.entity import set_values as set_values_t
123
+ from . import sockets_t as sockets_t
124
+ from . import structured_filters_t as structured_filters_t
99
125
  from .api.entity import transition_entity_phase as transition_entity_phase_t
100
126
  from .api.recipes import unarchive_recipes as unarchive_recipes_t
101
127
  from . import units_t as units_t
102
128
  from .api.entity import unlock_entity as unlock_entity_t
103
129
  from .api.recipes import unlock_recipes as unlock_recipes_t
104
130
  from .api.material_families import update_entity_material_families as update_entity_material_families_t
131
+ from . import uploader_t as uploader_t
132
+ from .api.condition_parameters import upsert_condition_match as upsert_condition_match_t
105
133
  from .api.field_options import upsert_field_options as upsert_field_options_t
106
134
  from . import users_t as users_t
107
135
  from . import webhook_job_t as webhook_job_t
@@ -109,7 +137,9 @@ from . import workflows_t as workflows_t
109
137
 
110
138
 
111
139
  __all__: list[str] = [
140
+ "add_notebook_content_t",
112
141
  "add_recipe_to_project_t",
142
+ "add_time_series_data_t",
113
143
  "archive_recipes_t",
114
144
  "associate_equipment_input_t",
115
145
  "associate_recipe_as_input_t",
@@ -122,24 +152,34 @@ __all__: list[str] = [
122
152
  "chemical_structure_t",
123
153
  "clear_recipe_outputs_t",
124
154
  "client_config_t",
155
+ "complete_async_parse_t",
156
+ "complete_async_upload_t",
125
157
  "convert_chemical_formats_t",
126
158
  "create_entities_t",
127
159
  "create_entity_t",
128
160
  "create_inputs_t",
161
+ "create_mix_order_t",
162
+ "create_or_update_entity_t",
129
163
  "create_recipe_t",
130
164
  "create_recipe_link_t",
131
165
  "create_recipes_t",
132
166
  "curves_t",
167
+ "data_t",
133
168
  "disassociate_recipe_as_input_t",
169
+ "download_file_t",
134
170
  "edit_recipe_inputs_t",
135
171
  "entity_t",
136
172
  "execute_batch_t",
137
173
  "execute_batch_load_async_t",
138
174
  "experiment_groups_t",
175
+ "export_entities_t",
176
+ "exports_t",
177
+ "fetch_listing_t",
139
178
  "field_values_t",
140
179
  "fields_t",
141
180
  "generic_upload_t",
142
181
  "get_column_calculation_values_t",
182
+ "get_current_user_info_t",
143
183
  "get_curve_t",
144
184
  "get_entities_data_t",
145
185
  "get_input_data_t",
@@ -148,6 +188,7 @@ __all__: list[str] = [
148
188
  "get_inputs_data_t",
149
189
  "get_output_data_t",
150
190
  "get_output_names_t",
191
+ "get_output_organization_t",
151
192
  "get_projects_t",
152
193
  "get_projects_data_t",
153
194
  "get_recipe_calculations_t",
@@ -162,18 +203,27 @@ __all__: list[str] = [
162
203
  "input_attributes_t",
163
204
  "inputs_t",
164
205
  "integration_server_t",
206
+ "integration_session_t",
207
+ "integrations_t",
165
208
  "invoke_uploader_t",
166
209
  "job_definition_t",
210
+ "list_aggregate_t",
167
211
  "list_entities_t",
168
212
  "list_id_source_t",
213
+ "listing_t",
169
214
  "lock_entity_t",
170
215
  "lock_recipes_t",
216
+ "lookup_entity_t",
171
217
  "match_id_source_t",
218
+ "notices_t",
219
+ "notifications_t",
172
220
  "outputs_t",
173
221
  "overrides_t",
174
222
  "permissions_t",
175
223
  "phases_t",
176
224
  "post_base_t",
225
+ "publish_realtime_data_t",
226
+ "push_notification_t",
177
227
  "queued_job_t",
178
228
  "recipe_identifiers_t",
179
229
  "recipe_inputs_t",
@@ -183,6 +233,7 @@ __all__: list[str] = [
183
233
  "recipe_tags_t",
184
234
  "recipe_workflow_steps_t",
185
235
  "recipes_t",
236
+ "register_sockets_token_t",
186
237
  "remove_recipe_from_project_t",
187
238
  "remove_recipe_link_t",
188
239
  "resolve_entity_ids_t",
@@ -191,6 +242,7 @@ __all__: list[str] = [
191
242
  "run_trigger_t",
192
243
  "secret_retrieval_t",
193
244
  "set_core_permissions_t",
245
+ "set_entity_field_values_t",
194
246
  "set_input_attribute_values_t",
195
247
  "set_input_category_t",
196
248
  "set_input_subcategories_t",
@@ -201,13 +253,18 @@ __all__: list[str] = [
201
253
  "set_recipe_output_file_t",
202
254
  "set_recipe_outputs_t",
203
255
  "set_recipe_tags_t",
256
+ "set_recipe_total_t",
204
257
  "set_values_t",
258
+ "sockets_t",
259
+ "structured_filters_t",
205
260
  "transition_entity_phase_t",
206
261
  "unarchive_recipes_t",
207
262
  "units_t",
208
263
  "unlock_entity_t",
209
264
  "unlock_recipes_t",
210
265
  "update_entity_material_families_t",
266
+ "uploader_t",
267
+ "upsert_condition_match_t",
211
268
  "upsert_field_options_t",
212
269
  "users_t",
213
270
  "webhook_job_t",