abstra 3.23.3__py3-none-any.whl → 3.23.5__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.
- {abstra-3.23.3.dist-info → abstra-3.23.5.dist-info}/METADATA +1 -1
- {abstra-3.23.3.dist-info → abstra-3.23.5.dist-info}/RECORD +191 -192
- abstra_internals/contracts_generated.py +660 -652
- abstra_internals/controllers/execution/consumer.py +27 -39
- abstra_internals/controllers/execution/execution.py +7 -4
- abstra_internals/controllers/execution/execution_client.py +2 -11
- abstra_internals/controllers/execution/execution_client_form.py +10 -11
- abstra_internals/controllers/execution/execution_client_hook.py +3 -9
- abstra_internals/controllers/execution/execution_stdio.py +3 -14
- abstra_internals/controllers/execution/execution_test.py +5 -9
- abstra_internals/controllers/execution/worker_process.py +8 -33
- abstra_internals/controllers/main.py +18 -26
- abstra_internals/controllers/workflows.py +33 -0
- abstra_internals/entities/execution_context.py +1 -0
- abstra_internals/entities/execution_test.py +6 -3
- abstra_internals/entities/forms/widgets/file_upload.py +1 -1
- abstra_internals/interface/cli/editor.py +6 -3
- abstra_internals/interface/sdk/forms/deprecated/page_test.py +6 -8
- abstra_internals/interface/sdk/tables/api_test.py +1 -7
- abstra_internals/logs_watcher.py +13 -6
- abstra_internals/repositories/consumer.py +1 -3
- abstra_internals/repositories/factory.py +7 -6
- abstra_internals/repositories/producer.py +7 -41
- abstra_internals/repositories/project/project_test.py +1 -6
- abstra_internals/repositories/tasks.py +8 -4
- abstra_internals/server/blueprints/player.py +39 -24
- abstra_internals/server/guards/role_guard_test.py +1 -6
- abstra_internals/server/routes/forms.py +10 -3
- abstra_internals/server/routes/mcp.py +2 -1
- abstra_internals/server/routes/stdio.py +3 -3
- abstra_internals/stdio_patcher.py +4 -4
- abstra_internals/tasks_watcher.py +59 -0
- abstra_internals/utils/ai.py +4 -9
- abstra_statics/dist/assets/AbstraButton.vue_vue_type_script_setup_true_lang.ad9e23f0.js +2 -0
- abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.ee29465c.js +2 -0
- abstra_statics/dist/assets/ApiKeys.958a2342.js +2 -0
- abstra_statics/dist/assets/App.6c9e1d73.js +2 -0
- abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.46bbdba5.js +2 -0
- abstra_statics/dist/assets/{BaseLayout.670b8441.js → BaseLayout.d4459d3a.js} +2 -2
- abstra_statics/dist/assets/{Billing.80a50079.js → Billing.920e2b19.js} +2 -2
- abstra_statics/dist/assets/{Breadcrumb.1d42c80c.js → Breadcrumb.2d9d636f.js} +2 -2
- abstra_statics/dist/assets/{Builds.4641dcf9.js → Builds.11e86dae.js} +2 -2
- abstra_statics/dist/assets/{Card.774e5d67.js → Card.0b4293e1.js} +2 -2
- abstra_statics/dist/assets/{CircularLoading.02fd7b7b.js → CircularLoading.e2f7dd80.js} +2 -2
- abstra_statics/dist/assets/CloseCircleOutlined.196a309a.js +2 -0
- abstra_statics/dist/assets/{ConnectorsView.746c7417.js → ConnectorsView.cda5c60f.js} +2 -2
- abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.4d7bb007.js +2 -0
- abstra_statics/dist/assets/{ContentLayout.4fc85c66.js → ContentLayout.69b8ab09.js} +2 -2
- abstra_statics/dist/assets/{CrudView.7f2cbde4.js → CrudView.56ebce1a.js} +2 -2
- abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.4eb7d335.js +2 -0
- abstra_statics/dist/assets/{EditorLogin.45764526.js → EditorLogin.a10e3a50.js} +2 -2
- abstra_statics/dist/assets/{EditorsView.e9452d34.js → EditorsView.1925d1b5.js} +2 -2
- abstra_statics/dist/assets/EnvVars.67eed38a.js +2 -0
- abstra_statics/dist/assets/Error.66a8e630.js +2 -0
- abstra_statics/dist/assets/ExclamationCircleOutlined.3ef65f87.js +2 -0
- abstra_statics/dist/assets/{Files.8bcd4d1f.js → Files.e978e721.js} +2 -2
- abstra_statics/dist/assets/{Form.288a7191.js → Form.ba1aeab0.js} +2 -2
- abstra_statics/dist/assets/{FormRunner.f721431d.js → FormRunner.1a6e12cb.js} +2 -2
- abstra_statics/dist/assets/Home.2871cae9.js +2 -0
- abstra_statics/dist/assets/Home.de257b98.js +2 -0
- abstra_statics/dist/assets/{Live.2bbd1666.js → Live.cf58dd01.js} +2 -2
- abstra_statics/dist/assets/LoadingContainer.e6a98e2f.js +2 -0
- abstra_statics/dist/assets/LoadingOutlined.398021fd.js +2 -0
- abstra_statics/dist/assets/Login.604d7bbd.js +2 -0
- abstra_statics/dist/assets/{Login.186f24a9.js → Login.605b23e8.js} +2 -2
- abstra_statics/dist/assets/{Login.vue_vue_type_script_setup_true_lang.fab46c0c.js → Login.vue_vue_type_script_setup_true_lang.a8986f39.js} +2 -2
- abstra_statics/dist/assets/Logo.0fee0801.js +2 -0
- abstra_statics/dist/assets/Logs.e15923a0.js +2 -0
- abstra_statics/dist/assets/LogsController.35cad159.js +2 -0
- abstra_statics/dist/assets/Main.17024018.js +2 -0
- abstra_statics/dist/assets/{MockForm.9b6a244c.js → MockForm.603b1cbb.js} +2 -2
- abstra_statics/dist/assets/Navbar.d4751f52.js +2 -0
- abstra_statics/dist/assets/{NewEditor.1d7ddf86.js → NewEditor.bd4c258c.js} +5 -5
- abstra_statics/dist/assets/OidcLoginCallback.14f293b0.js +2 -0
- abstra_statics/dist/assets/OidcLogoutCallback.cb05eca4.js +2 -0
- abstra_statics/dist/assets/{OmniChat.5e68be1c.js → OmniChat.6d6fed9c.js} +2 -2
- abstra_statics/dist/assets/{OnboardingView.8733d184.js → OnboardingView.546afa14.js} +2 -2
- abstra_statics/dist/assets/{Organization.bcb76b94.js → Organization.a7309bfd.js} +2 -2
- abstra_statics/dist/assets/Organizations.91c269e6.js +2 -0
- abstra_statics/dist/assets/{PhArrowCounterClockwise.vue.d273955f.js → PhArrowCounterClockwise.vue.2706c012.js} +2 -2
- abstra_statics/dist/assets/{PhArrowSquareOut.vue.0d20b8e9.js → PhArrowSquareOut.vue.06e1e087.js} +2 -2
- abstra_statics/dist/assets/{PhBookBookmark.vue.8611e54f.js → PhBookBookmark.vue.b9569168.js} +2 -2
- abstra_statics/dist/assets/{PhChats.vue.3dd1c2d7.js → PhChats.vue.bb8ced1d.js} +2 -2
- abstra_statics/dist/assets/{PhClockCounterClockwise.vue.853bd5ec.js → PhClockCounterClockwise.vue.4252e6be.js} +2 -2
- abstra_statics/dist/assets/{PhCopy.vue.b1c2f21f.js → PhCopy.vue.59c3a353.js} +2 -2
- abstra_statics/dist/assets/{PhCopySimple.vue.ffc7ec94.js → PhCopySimple.vue.1c5a0760.js} +2 -2
- abstra_statics/dist/assets/{PhCube.vue.b5e79047.js → PhCube.vue.7728ef5a.js} +2 -2
- abstra_statics/dist/assets/{PhDotsThreeVertical.vue.89b627bd.js → PhDotsThreeVertical.vue.275748da.js} +2 -2
- abstra_statics/dist/assets/{PhDownloadSimple.vue.2c2eeb2c.js → PhDownloadSimple.vue.ed1fb44c.js} +2 -2
- abstra_statics/dist/assets/{PhFolderPlus.vue.e199ba17.js → PhFolderPlus.vue.aa7bc872.js} +2 -2
- abstra_statics/dist/assets/{PhGear.vue.fbac4098.js → PhGear.vue.ab14d56a.js} +2 -2
- abstra_statics/dist/assets/{PhKey.vue.1850e466.js → PhKey.vue.c0768b68.js} +2 -2
- abstra_statics/dist/assets/{PhPencil.vue.c3c70f7d.js → PhPencil.vue.aaf907d1.js} +2 -2
- abstra_statics/dist/assets/{PhPencilSimple.vue.ee9d03fc.js → PhPencilSimple.vue.2580bcf9.js} +2 -2
- abstra_statics/dist/assets/{PhPencilSimpleLine.vue.6eedd1e6.js → PhPencilSimpleLine.vue.2c2058ce.js} +2 -2
- abstra_statics/dist/assets/{PhRocket.vue.56a9b767.js → PhRocket.vue.67935a85.js} +2 -2
- abstra_statics/dist/assets/{PhSignOut.vue.ee548b4c.js → PhSignOut.vue.87084f39.js} +2 -2
- abstra_statics/dist/assets/{PhSparkle.vue.dc906cc7.js → PhSparkle.vue.707b3721.js} +2 -2
- abstra_statics/dist/assets/{PhUserList.vue.6d0e7bc8.js → PhUserList.vue.907c1a2b.js} +2 -2
- abstra_statics/dist/assets/{PhUsersThree.vue.528ec19f.js → PhUsersThree.vue.2c3539c7.js} +2 -2
- abstra_statics/dist/assets/{PhWebhooksLogo.vue.49b66d69.js → PhWebhooksLogo.vue.1134426a.js} +2 -2
- abstra_statics/dist/assets/{PlayerConfigProvider.ead35c7b.js → PlayerConfigProvider.bee6a260.js} +2 -2
- abstra_statics/dist/assets/{PlayerNavbar.523eb145.js → PlayerNavbar.b15bb1f3.js} +2 -2
- abstra_statics/dist/assets/{Project.d667be48.js → Project.c5ac4ebe.js} +2 -2
- abstra_statics/dist/assets/{ProjectLogin.ed25cb39.js → ProjectLogin.2905a0d6.js} +2 -2
- abstra_statics/dist/assets/{ProjectSettings.78ebb6ae.js → ProjectSettings.e40cc19c.js} +2 -2
- abstra_statics/dist/assets/{ProjectsView.0a9b802f.js → ProjectsView.3df99f87.js} +2 -2
- abstra_statics/dist/assets/{SaveButton.7c95c711.js → SaveButton.5a81e6f5.js} +2 -2
- abstra_statics/dist/assets/{Sidebar.88ff0297.js → Sidebar.f23acdbf.js} +2 -2
- abstra_statics/dist/assets/{Sql.cbcb65af.js → Sql.ea1ee70d.js} +5 -5
- abstra_statics/dist/assets/Steps.ad9ac163.js +2 -0
- abstra_statics/dist/assets/{TableEditor.8166dbd3.js → TableEditor.3150beb1.js} +2 -2
- abstra_statics/dist/assets/Tables.ad8c6f7a.js +2 -0
- abstra_statics/dist/assets/{TablesDiagram.67a07e98.js → TablesDiagram.5d3625d1.js} +2 -2
- abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.58bcff9a.js +2 -0
- abstra_statics/dist/assets/{Tasks.d7e74b8f.js → Tasks.d5e7bdf2.js} +2 -2
- abstra_statics/dist/assets/{UploadOutlined.6515c426.js → UploadOutlined.5f3b92b7.js} +2 -2
- abstra_statics/dist/assets/{View.2cf966b9.js → View.63e7c09c.js} +2 -2
- abstra_statics/dist/assets/{View.vue_vue_type_script_setup_true_lang.17304c45.js → View.vue_vue_type_script_setup_true_lang.a36086e8.js} +2 -2
- abstra_statics/dist/assets/{Watermark.4b6da85c.js → Watermark.a7a7af90.js} +2 -2
- abstra_statics/dist/assets/{WebEditor.969d0862.js → WebEditor.37194274.js} +2 -2
- abstra_statics/dist/assets/{WidgetPreview.6cc6a025.js → WidgetPreview.5d662c77.js} +2 -2
- abstra_statics/dist/assets/ant-design.9bb31d85.js +2 -0
- abstra_statics/dist/assets/apiKey.4f2a122c.js +2 -0
- abstra_statics/dist/assets/asyncComputed.06839806.js +2 -0
- abstra_statics/dist/assets/{build.c819a477.js → build.995e6997.js} +2 -2
- abstra_statics/dist/assets/colorHelpers.eb706cff.js +2 -0
- abstra_statics/dist/assets/{console.072f10aa.js → console.a07258d6.js} +2 -2
- abstra_statics/dist/assets/constants.d30e64e7.js +2 -0
- abstra_statics/dist/assets/{cssMode.b24669f0.js → cssMode.5c9f19b3.js} +2 -2
- abstra_statics/dist/assets/datetime.49213c3c.js +2 -0
- abstra_statics/dist/assets/dayjs.9d20dd08.js +2 -0
- abstra_statics/dist/assets/editor.a151d549.js +2 -0
- abstra_statics/dist/assets/editor.main.f809eb34.js +2 -0
- abstra_statics/dist/assets/fetch.ce34efab.js +2 -0
- abstra_statics/dist/assets/{files.ff551888.js → files.5dcc78be.js} +2 -2
- abstra_statics/dist/assets/{folder.92a70293.js → folder.777c3209.js} +2 -2
- abstra_statics/dist/assets/{freemarker2.c3563cfb.js → freemarker2.385a198f.js} +2 -2
- abstra_statics/dist/assets/{handlebars.bedd8360.js → handlebars.806a8b22.js} +3 -3
- abstra_statics/dist/assets/{html.3cfc37f8.js → html.8df330b0.js} +2 -2
- abstra_statics/dist/assets/{htmlMode.f9c29f9d.js → htmlMode.08713341.js} +2 -2
- abstra_statics/dist/assets/{index.5429e60c.js → index.11470ce1.js} +5 -5
- abstra_statics/dist/assets/index.15909af6.js +2 -0
- abstra_statics/dist/assets/{index.dfc9f500.js → index.227be58a.js} +2 -2
- abstra_statics/dist/assets/{index.9028af84.js → index.440bd76c.js} +2 -2
- abstra_statics/dist/assets/{index.138ad220.js → index.49f6d313.js} +2 -2
- abstra_statics/dist/assets/index.6f004291.js +2 -0
- abstra_statics/dist/assets/{index.a9e83a57.js → index.ad15e486.js} +2 -2
- abstra_statics/dist/assets/{index.26a34a52.js → index.cf5ef754.js} +2 -2
- abstra_statics/dist/assets/{index.b69b3146.js → index.f219d050.js} +2 -2
- abstra_statics/dist/assets/{index.e6b397aa.js → index.fdbbf978.js} +2 -2
- abstra_statics/dist/assets/{javascript.a4a24e63.js → javascript.7e683eb3.js} +2 -2
- abstra_statics/dist/assets/{jsonMode.4df0cb48.js → jsonMode.c71ca5e7.js} +2 -2
- abstra_statics/dist/assets/{jwt-decode.esm.b204fb7d.js → jwt-decode.esm.bcf2db26.js} +7 -7
- abstra_statics/dist/assets/linters.1f3c2bdc.js +2 -0
- abstra_statics/dist/assets/{liquid.6539d262.js → liquid.c89ab4d8.js} +3 -3
- abstra_statics/dist/assets/{member.c5283d66.js → member.39a2b810.js} +2 -2
- abstra_statics/dist/assets/{metadata.e6e36ac2.js → metadata.77edb2ef.js} +2 -2
- abstra_statics/dist/assets/{omniChatStore.0470e5b1.js → omniChatStore.13045a23.js} +2 -2
- abstra_statics/dist/assets/{organization.04cff158.js → organization.017d0932.js} +2 -2
- abstra_statics/dist/assets/player.490b5a36.js +2 -0
- abstra_statics/dist/assets/{plotly.min.e185d882.js → plotly.min.ebdb6341.js} +2 -2
- abstra_statics/dist/assets/polling.b53cbe3d.js +2 -0
- abstra_statics/dist/assets/{project.c198d794.js → project.6106c0ae.js} +2 -2
- abstra_statics/dist/assets/{python.5c45007e.js → python.fe5f6c61.js} +3 -3
- abstra_statics/dist/assets/{razor.dbd79c37.js → razor.63a60978.js} +2 -2
- abstra_statics/dist/assets/{record.316e025a.js → record.6b6d4da9.js} +2 -2
- abstra_statics/dist/assets/{redirect.d1904049.js → redirect.b8ba406b.js} +2 -2
- abstra_statics/dist/assets/{repository.ae6e826f.js → repository.54d4a909.js} +2 -2
- abstra_statics/dist/assets/{repository.babdc912.js → repository.f2f5d880.js} +2 -2
- abstra_statics/dist/assets/{router.29d47df0.js → router.e5052323.js} +3 -3
- abstra_statics/dist/assets/{string.4a80abd5.js → string.2f1abff3.js} +2 -2
- abstra_statics/dist/assets/{tables.e80a0328.js → tables.ecf1f359.js} +2 -2
- abstra_statics/dist/assets/{tasksController.307be8dc.js → tasksController.64f43cce.js} +3 -3
- abstra_statics/dist/assets/{toggleHighContrast.4184537f.js → toggleHighContrast.a33c9afc.js} +7 -7
- abstra_statics/dist/assets/{tsMode.12c94eed.js → tsMode.bbb62deb.js} +2 -2
- abstra_statics/dist/assets/{typescript.e087b68e.js → typescript.f3be1a16.js} +3 -3
- abstra_statics/dist/assets/url.79c72bde.js +2 -0
- abstra_statics/dist/assets/userStore.5dca7ff0.js +2 -0
- abstra_statics/dist/assets/uuid.3f5b8acd.js +2 -0
- abstra_statics/dist/assets/{vue-flow-background.402b0a32.js → vue-flow-background.939eca5f.js} +2 -2
- abstra_statics/dist/assets/{vue-quill.esm-bundler.10d49c97.js → vue-quill.esm-bundler.c86df351.js} +2 -2
- abstra_statics/dist/assets/workspaceStore.7283625b.js +2 -0
- abstra_statics/dist/assets/{xml.c4ef7130.js → xml.96392483.js} +3 -3
- abstra_statics/dist/assets/{yaml.5eb27541.js → yaml.96b91a1d.js} +3 -3
- abstra_statics/dist/console.html +15 -15
- abstra_statics/dist/editor.html +11 -11
- abstra_statics/dist/player.html +9 -9
- abstra_internals/utils/threading.py +0 -24
- abstra_internals/utils/websockets.py +0 -79
- abstra_statics/dist/assets/AbstraButton.vue_vue_type_script_setup_true_lang.c714d609.js +0 -2
- abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.37e7ad5b.js +0 -2
- abstra_statics/dist/assets/ApiKeys.94113392.js +0 -2
- abstra_statics/dist/assets/App.6c102d26.js +0 -2
- abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.26121baf.js +0 -2
- abstra_statics/dist/assets/CloseCircleOutlined.7ac8e1e8.js +0 -2
- abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.b85f86d8.js +0 -2
- abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.0b26fb89.js +0 -2
- abstra_statics/dist/assets/EnvVars.ccc24231.js +0 -2
- abstra_statics/dist/assets/Error.1bf571a6.js +0 -2
- abstra_statics/dist/assets/ExclamationCircleOutlined.f71fa58b.js +0 -2
- abstra_statics/dist/assets/Home.23f0cff8.js +0 -2
- abstra_statics/dist/assets/Home.5ce6aade.js +0 -2
- abstra_statics/dist/assets/LoadingContainer.3ea9ec8d.js +0 -2
- abstra_statics/dist/assets/LoadingOutlined.11688830.js +0 -2
- abstra_statics/dist/assets/Login.2a4c7e48.js +0 -2
- abstra_statics/dist/assets/Logo.cc2d4a95.js +0 -2
- abstra_statics/dist/assets/Logs.f57284a4.js +0 -2
- abstra_statics/dist/assets/LogsController.353f0002.js +0 -2
- abstra_statics/dist/assets/Main.635e443c.js +0 -2
- abstra_statics/dist/assets/Navbar.dacca218.js +0 -2
- abstra_statics/dist/assets/OidcLoginCallback.0dff6320.js +0 -2
- abstra_statics/dist/assets/OidcLogoutCallback.1f077fbb.js +0 -2
- abstra_statics/dist/assets/Organizations.3dbd99e1.js +0 -2
- abstra_statics/dist/assets/Steps.4c7bc2b0.js +0 -2
- abstra_statics/dist/assets/Tables.0da330b6.js +0 -2
- abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.2d88acf3.js +0 -2
- abstra_statics/dist/assets/ant-design.771baa34.js +0 -2
- abstra_statics/dist/assets/apiKey.8e591cfc.js +0 -2
- abstra_statics/dist/assets/asyncComputed.0f036eae.js +0 -2
- abstra_statics/dist/assets/colorHelpers.c4e8851c.js +0 -2
- abstra_statics/dist/assets/constants.6f822a9d.js +0 -2
- abstra_statics/dist/assets/datetime.7d5f4744.js +0 -2
- abstra_statics/dist/assets/dayjs.19defe89.js +0 -2
- abstra_statics/dist/assets/editor.6fa2da52.js +0 -2
- abstra_statics/dist/assets/editor.main.91c25311.js +0 -2
- abstra_statics/dist/assets/fetch.41281c28.js +0 -2
- abstra_statics/dist/assets/index.dccc1696.js +0 -2
- abstra_statics/dist/assets/index.e7bc0e19.js +0 -2
- abstra_statics/dist/assets/linters.5b679742.js +0 -2
- abstra_statics/dist/assets/player.c715ca22.js +0 -2
- abstra_statics/dist/assets/polling.5f85e241.js +0 -2
- abstra_statics/dist/assets/url.371275cf.js +0 -2
- abstra_statics/dist/assets/userStore.26658284.js +0 -2
- abstra_statics/dist/assets/uuid.ffd92735.js +0 -2
- abstra_statics/dist/assets/workspaceStore.9caf566c.js +0 -2
- {abstra-3.23.3.dist-info → abstra-3.23.5.dist-info}/WHEEL +0 -0
- {abstra-3.23.3.dist-info → abstra-3.23.5.dist-info}/entry_points.txt +0 -0
- {abstra-3.23.3.dist-info → abstra-3.23.5.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
from concurrent.futures import ThreadPoolExecutor
|
|
2
|
-
from multiprocessing.connection import Client
|
|
3
2
|
from uuid import uuid4
|
|
4
3
|
|
|
5
4
|
from abstra_internals.controllers.execution.worker_process import process_main
|
|
6
5
|
from abstra_internals.controllers.main import MainController
|
|
7
6
|
from abstra_internals.environment import (
|
|
8
7
|
EXECUTION_QUEUE_CONCURRENCY,
|
|
9
|
-
HOST,
|
|
10
8
|
PROCESS_TIMEOUT_SECONDS,
|
|
11
9
|
)
|
|
12
10
|
from abstra_internals.logger import AbstraLogger
|
|
@@ -35,36 +33,38 @@ class ConsumerController:
|
|
|
35
33
|
f"[ConsumerController] Starting loop with {self.concurrency} threads"
|
|
36
34
|
)
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
thread_pool = ThreadPoolExecutor(
|
|
39
37
|
max_workers=self.concurrency,
|
|
40
38
|
thread_name_prefix="ExecutionConsumer",
|
|
41
|
-
)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
42
|
for msg in self.consumer.iter():
|
|
43
43
|
AbstraLogger.debug(
|
|
44
44
|
f"[ConsumerController] Submitting message [{msg.delivery_tag}] for processing"
|
|
45
45
|
)
|
|
46
|
-
|
|
46
|
+
thread_pool.submit(
|
|
47
47
|
self.run_subprocess,
|
|
48
48
|
msg=msg,
|
|
49
49
|
)
|
|
50
|
+
finally:
|
|
51
|
+
AbstraLogger.warning(
|
|
52
|
+
"[ConsumerController] Consumer main loop exited, waiting for threads to finish"
|
|
53
|
+
)
|
|
54
|
+
thread_pool.shutdown(wait=True)
|
|
55
|
+
AbstraLogger.warning("[ConsumerController] All threads finished")
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
# If the loop exits and there are running executions, gracefull shutdown has failed
|
|
57
|
-
self.main_controller.fail_app_executions(
|
|
58
|
-
app_id=self.app_id,
|
|
59
|
-
reason="Failed to set status",
|
|
60
|
-
)
|
|
57
|
+
# If the loop exits and there are running executions, gracefull shutdown has failed
|
|
58
|
+
self.main_controller.fail_app_executions(
|
|
59
|
+
app_id=self.app_id,
|
|
60
|
+
reason="Failed to set status",
|
|
61
|
+
)
|
|
61
62
|
|
|
62
|
-
def run_subprocess(self, msg: QueueMessage)
|
|
63
|
-
connection = None
|
|
63
|
+
def run_subprocess(self, msg: QueueMessage):
|
|
64
64
|
worker_id = str(uuid4())
|
|
65
|
-
|
|
66
|
-
head_id = worker_id.split("-")[0]
|
|
65
|
+
head_id = worker_id.split("-")[0]
|
|
67
66
|
|
|
67
|
+
try:
|
|
68
68
|
mp_context = self.main_controller.repositories.mp_context.get_context()
|
|
69
69
|
stage = self.main_controller.get_stage(msg.preexecution.stage_id)
|
|
70
70
|
|
|
@@ -74,11 +74,13 @@ class ConsumerController:
|
|
|
74
74
|
)
|
|
75
75
|
self.consumer.threadsafe_ack(msg)
|
|
76
76
|
return
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
)
|
|
77
|
+
|
|
78
|
+
local_queue = None
|
|
79
|
+
if isinstance(
|
|
80
|
+
self.main_controller.repositories.producer, LocalProducerRepository
|
|
81
|
+
):
|
|
82
|
+
local_queue = self.main_controller.repositories.producer.queue
|
|
83
|
+
|
|
82
84
|
p = mp_context.Process(
|
|
83
85
|
target=process_main,
|
|
84
86
|
name=f"Worker-{head_id}",
|
|
@@ -89,13 +91,7 @@ class ConsumerController:
|
|
|
89
91
|
root_path=Settings.root_path,
|
|
90
92
|
server_port=Settings.server_port,
|
|
91
93
|
request=msg.preexecution.context,
|
|
92
|
-
|
|
93
|
-
local_queue=self.main_controller.repositories.producer.queue
|
|
94
|
-
if isinstance(
|
|
95
|
-
self.main_controller.repositories.producer,
|
|
96
|
-
LocalProducerRepository,
|
|
97
|
-
)
|
|
98
|
-
else None,
|
|
94
|
+
local_queue=local_queue,
|
|
99
95
|
),
|
|
100
96
|
)
|
|
101
97
|
|
|
@@ -105,7 +101,6 @@ class ConsumerController:
|
|
|
105
101
|
# If timeout is reached, the process is still alive
|
|
106
102
|
if p.is_alive():
|
|
107
103
|
p.terminate()
|
|
108
|
-
p.join() # Wait for termination to complete
|
|
109
104
|
raise NonCleanExit(
|
|
110
105
|
f"Run took too long to complete ({PROCESS_TIMEOUT_SECONDS} secs) and was terminated. Please make sure all your requests calls have a timeout set."
|
|
111
106
|
)
|
|
@@ -133,13 +128,6 @@ class ConsumerController:
|
|
|
133
128
|
)
|
|
134
129
|
|
|
135
130
|
self.consumer.threadsafe_nack(msg)
|
|
136
|
-
finally:
|
|
137
|
-
# Always close the connection when done
|
|
138
|
-
if connection is not None:
|
|
139
|
-
try:
|
|
140
|
-
connection.close()
|
|
141
|
-
except Exception:
|
|
142
|
-
pass # Ignore errors when closing
|
|
143
131
|
|
|
144
132
|
AbstraLogger.warning(
|
|
145
133
|
f"[ConsumerController] Message [{msg.delivery_tag}] has been negatively acknowledged"
|
|
@@ -4,7 +4,10 @@ import traceback
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Literal, Optional, Tuple
|
|
6
6
|
|
|
7
|
-
from abstra_internals.controllers.execution.execution_client import
|
|
7
|
+
from abstra_internals.controllers.execution.execution_client import (
|
|
8
|
+
ExecutionClient,
|
|
9
|
+
HeadlessClient,
|
|
10
|
+
)
|
|
8
11
|
from abstra_internals.controllers.execution.execution_client_form import ClientAbandoned
|
|
9
12
|
from abstra_internals.controllers.sdk.sdk_context import SDKContext
|
|
10
13
|
from abstra_internals.entities.execution import Execution
|
|
@@ -27,12 +30,12 @@ class ExecutionController:
|
|
|
27
30
|
*,
|
|
28
31
|
repositories: Repositories,
|
|
29
32
|
stage: StageWithFile,
|
|
30
|
-
client: ExecutionClient,
|
|
31
|
-
context: ClientContext,
|
|
33
|
+
client: Optional[ExecutionClient] = None,
|
|
34
|
+
context: Optional[ClientContext] = None,
|
|
32
35
|
) -> None:
|
|
33
36
|
self.repositories = repositories
|
|
34
37
|
self.stage = stage
|
|
35
|
-
self.client = client
|
|
38
|
+
self.client = client or HeadlessClient()
|
|
36
39
|
self.context = context
|
|
37
40
|
|
|
38
41
|
def run(self):
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import abc
|
|
2
|
-
from multiprocessing.connection import Connection
|
|
3
|
-
|
|
4
|
-
from abstra_internals.entities.execution_context import ClientContext
|
|
5
2
|
|
|
6
3
|
|
|
7
4
|
class ExecutionClient(abc.ABC):
|
|
@@ -19,17 +16,11 @@ class ExecutionClient(abc.ABC):
|
|
|
19
16
|
|
|
20
17
|
|
|
21
18
|
class HeadlessClient(ExecutionClient):
|
|
22
|
-
context: ClientContext
|
|
23
|
-
|
|
24
|
-
def __init__(self, context: ClientContext, conn: Connection):
|
|
25
|
-
self.context = context
|
|
26
|
-
self._conn = conn
|
|
27
|
-
|
|
28
19
|
def handle_failure(self, e: Exception) -> None:
|
|
29
|
-
|
|
20
|
+
pass
|
|
30
21
|
|
|
31
22
|
def handle_success(self) -> None:
|
|
32
|
-
|
|
23
|
+
pass
|
|
33
24
|
|
|
34
25
|
def handle_start(self, execution_id: str):
|
|
35
26
|
pass
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
from multiprocessing.connection import Connection
|
|
2
1
|
from typing import Dict, Optional
|
|
3
2
|
|
|
3
|
+
import flask_sock
|
|
4
|
+
|
|
4
5
|
from abstra_internals.controllers.execution.execution_client import ExecutionClient
|
|
5
6
|
from abstra_internals.entities.execution_context import FormContext
|
|
6
7
|
from abstra_internals.entities.forms.form_entity import ButtonAction, RenderedForm
|
|
@@ -17,38 +18,38 @@ class FormClient(ExecutionClient):
|
|
|
17
18
|
|
|
18
19
|
def __init__(
|
|
19
20
|
self,
|
|
20
|
-
|
|
21
|
+
ws: flask_sock.Server,
|
|
21
22
|
context: FormContext,
|
|
22
23
|
production_mode: bool,
|
|
23
24
|
) -> None:
|
|
24
25
|
self.context = context
|
|
25
26
|
self._production_mode = production_mode
|
|
26
|
-
self.
|
|
27
|
+
self._ws = ws
|
|
27
28
|
|
|
28
29
|
# WEBSOCKET
|
|
29
30
|
|
|
30
31
|
def _receive_message(self):
|
|
31
32
|
try:
|
|
32
|
-
str_data = self.
|
|
33
|
+
str_data = self._ws.receive()
|
|
33
34
|
deserialized = deserialize(str_data)
|
|
34
35
|
if not isinstance(deserialized, dict):
|
|
35
36
|
raise ValueError("Invalid message format")
|
|
36
37
|
return deserialized
|
|
37
|
-
except
|
|
38
|
+
except flask_sock.ConnectionClosed:
|
|
38
39
|
raise ClientAbandoned()
|
|
39
40
|
|
|
40
41
|
def _send(self, msg: forms_contract.Message) -> None:
|
|
41
42
|
str_data = serialize(msg.to_json())
|
|
42
43
|
try:
|
|
43
|
-
self.
|
|
44
|
-
except
|
|
44
|
+
self._ws.send(str_data)
|
|
45
|
+
except flask_sock.ConnectionClosed:
|
|
45
46
|
pass
|
|
46
47
|
|
|
47
48
|
def _user_code_send(self, msg: forms_contract.Message) -> None:
|
|
48
49
|
str_data = serialize(msg.to_json())
|
|
49
50
|
try:
|
|
50
|
-
self.
|
|
51
|
-
except
|
|
51
|
+
self._ws.send(str_data)
|
|
52
|
+
except flask_sock.ConnectionClosed:
|
|
52
53
|
raise ClientAbandoned()
|
|
53
54
|
|
|
54
55
|
def _wait_for_message(
|
|
@@ -161,14 +162,12 @@ class FormClient(ExecutionClient):
|
|
|
161
162
|
self._send(
|
|
162
163
|
forms_contract.ExecutionEndedMessage(close_dto, self._production_mode)
|
|
163
164
|
)
|
|
164
|
-
self._conn.close()
|
|
165
165
|
|
|
166
166
|
def handle_success(self):
|
|
167
167
|
close_dto = forms_contract.CloseDTO(exit_status="SUCCESS")
|
|
168
168
|
self._send(
|
|
169
169
|
forms_contract.ExecutionEndedMessage(close_dto, self._production_mode)
|
|
170
170
|
)
|
|
171
|
-
self._conn.close()
|
|
172
171
|
|
|
173
172
|
## Testing
|
|
174
173
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from multiprocessing.connection import Connection
|
|
2
1
|
from typing import Dict
|
|
3
2
|
|
|
4
3
|
from abstra_internals.controllers.execution.execution_client import ExecutionClient
|
|
@@ -7,23 +6,18 @@ from abstra_internals.entities.execution_context import HookContext, Request, Re
|
|
|
7
6
|
|
|
8
7
|
class HookClient(ExecutionClient):
|
|
9
8
|
context: HookContext
|
|
10
|
-
conn: Connection
|
|
11
|
-
response: Response
|
|
12
9
|
|
|
13
|
-
def __init__(self, context: HookContext
|
|
10
|
+
def __init__(self, context: HookContext) -> None:
|
|
14
11
|
self.context = context
|
|
15
|
-
self.conn = conn
|
|
16
|
-
self.response = Response(headers={}, status=200, body="")
|
|
17
12
|
|
|
18
13
|
def handle_failure(self, e: Exception) -> None:
|
|
19
14
|
self.set_response(500, "An exception occurred during execution.", {})
|
|
20
|
-
self.conn.send(self.response)
|
|
21
15
|
|
|
22
16
|
def handle_success(self) -> None:
|
|
23
|
-
|
|
17
|
+
pass
|
|
24
18
|
|
|
25
19
|
def set_response(self, status: int, body: str, headers: Dict[str, str]) -> None:
|
|
26
|
-
self.response = Response(
|
|
20
|
+
self.context.response = Response(
|
|
27
21
|
status=status,
|
|
28
22
|
body=body,
|
|
29
23
|
headers=headers,
|
|
@@ -13,10 +13,9 @@ from abstra_internals.env_masker import GLOBAL_MASKER
|
|
|
13
13
|
from abstra_internals.environment import IS_PRODUCTION
|
|
14
14
|
from abstra_internals.interface.sdk.user_exceptions import ExecutionNotFound
|
|
15
15
|
from abstra_internals.logger import AbstraLogger
|
|
16
|
-
from abstra_internals.utils import serialize
|
|
17
16
|
|
|
18
17
|
|
|
19
|
-
class
|
|
18
|
+
class BroadcastController:
|
|
20
19
|
listeners: List[flask_sock.Server] = []
|
|
21
20
|
_lock = threading.Lock()
|
|
22
21
|
|
|
@@ -32,22 +31,12 @@ class StdioController:
|
|
|
32
31
|
cls.listeners.remove(listener)
|
|
33
32
|
|
|
34
33
|
@classmethod
|
|
35
|
-
def broadcast(
|
|
36
|
-
cls,
|
|
37
|
-
*,
|
|
38
|
-
type: Literal["stdout", "stderr"],
|
|
39
|
-
execution_id: str,
|
|
40
|
-
stage_id: str,
|
|
41
|
-
log: str,
|
|
42
|
-
):
|
|
43
|
-
msg = serialize(
|
|
44
|
-
dict(type=type, log=log, execution_id=execution_id, stage_id=stage_id)
|
|
45
|
-
)
|
|
34
|
+
def broadcast(cls, *, msg: str):
|
|
46
35
|
for listener in cls.listeners:
|
|
47
36
|
try:
|
|
48
37
|
listener.send(msg)
|
|
49
38
|
except Exception:
|
|
50
|
-
|
|
39
|
+
BroadcastController.unregister(listener)
|
|
51
40
|
|
|
52
41
|
def __init__(
|
|
53
42
|
self,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from multiprocessing import Pipe
|
|
2
1
|
from pathlib import Path
|
|
3
2
|
|
|
4
3
|
from abstra_internals.controllers.execution.execution import ExecutionController
|
|
@@ -25,7 +24,8 @@ class ExecutionControllerTest(BaseTest):
|
|
|
25
24
|
headers={"auth": "some_secret_token"},
|
|
26
25
|
query_params={"c": "3"},
|
|
27
26
|
method="GET",
|
|
28
|
-
)
|
|
27
|
+
),
|
|
28
|
+
response=Response(headers={}, status=200, body=""),
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
self.project = self.repositories.project.load(include_disabled_stages=False)
|
|
@@ -39,8 +39,7 @@ class ExecutionControllerTest(BaseTest):
|
|
|
39
39
|
self.project.add_stage(self.stage)
|
|
40
40
|
self.repositories.project.save(self.project)
|
|
41
41
|
|
|
42
|
-
self.
|
|
43
|
-
self.hook_client = HookClient(self.context, conn=child_conn)
|
|
42
|
+
self.hook_client = HookClient(self.context)
|
|
44
43
|
|
|
45
44
|
def test_run_initial_returns_dto(self):
|
|
46
45
|
ExecutionController(
|
|
@@ -50,10 +49,7 @@ class ExecutionControllerTest(BaseTest):
|
|
|
50
49
|
client=self.hook_client,
|
|
51
50
|
).run()
|
|
52
51
|
|
|
53
|
-
|
|
54
|
-
assert isinstance(response, Response)
|
|
55
|
-
|
|
56
|
-
if not response:
|
|
52
|
+
if not self.context.response:
|
|
57
53
|
self.fail("Response was not set")
|
|
58
54
|
|
|
59
|
-
self.assertEqual(response.status, 200)
|
|
55
|
+
self.assertEqual(self.context.response.status, 200)
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import threading
|
|
2
2
|
import traceback
|
|
3
3
|
from multiprocessing import Queue
|
|
4
|
-
from multiprocessing.connection import Connection
|
|
5
4
|
from typing import Optional
|
|
6
5
|
|
|
7
6
|
from abstra_internals.controllers.execution.execution import ExecutionController
|
|
8
|
-
from abstra_internals.controllers.execution.execution_client import (
|
|
9
|
-
ExecutionClient,
|
|
10
|
-
HeadlessClient,
|
|
11
|
-
)
|
|
12
|
-
from abstra_internals.controllers.execution.execution_client_form import FormClient
|
|
13
|
-
from abstra_internals.controllers.execution.execution_client_hook import HookClient
|
|
14
7
|
from abstra_internals.controllers.main import MainController
|
|
15
8
|
from abstra_internals.entities.execution import ClientContext
|
|
16
|
-
from abstra_internals.
|
|
17
|
-
|
|
9
|
+
from abstra_internals.environment import (
|
|
10
|
+
IS_PRODUCTION,
|
|
11
|
+
set_SERVER_UUID,
|
|
12
|
+
set_WORKER_UUID,
|
|
13
|
+
)
|
|
18
14
|
from abstra_internals.logger import AbstraLogger
|
|
19
15
|
from abstra_internals.repositories.factory import (
|
|
20
16
|
build_editor_repositories,
|
|
@@ -25,24 +21,6 @@ from abstra_internals.settings import Settings
|
|
|
25
21
|
from abstra_internals.stdio_patcher import StdioPatcher
|
|
26
22
|
|
|
27
23
|
|
|
28
|
-
def make_client_from_context(
|
|
29
|
-
ctx: ClientContext, connection: Connection
|
|
30
|
-
) -> ExecutionClient:
|
|
31
|
-
if isinstance(ctx, FormContext):
|
|
32
|
-
return FormClient(
|
|
33
|
-
context=ctx,
|
|
34
|
-
conn=connection,
|
|
35
|
-
production_mode=IS_PRODUCTION,
|
|
36
|
-
)
|
|
37
|
-
elif isinstance(ctx, HookContext):
|
|
38
|
-
return HookClient(
|
|
39
|
-
context=ctx,
|
|
40
|
-
conn=connection,
|
|
41
|
-
)
|
|
42
|
-
else:
|
|
43
|
-
return HeadlessClient(context=ctx, conn=connection)
|
|
44
|
-
|
|
45
|
-
|
|
46
24
|
def process_main(
|
|
47
25
|
*,
|
|
48
26
|
root_path: str,
|
|
@@ -50,8 +28,7 @@ def process_main(
|
|
|
50
28
|
worker_id: str,
|
|
51
29
|
app_id: str,
|
|
52
30
|
stage: StageWithFile,
|
|
53
|
-
|
|
54
|
-
request: ClientContext,
|
|
31
|
+
request: Optional[ClientContext] = None,
|
|
55
32
|
local_queue: Optional[Queue] = None,
|
|
56
33
|
):
|
|
57
34
|
thread = threading.current_thread()
|
|
@@ -66,7 +43,6 @@ def process_main(
|
|
|
66
43
|
controller = MainController(repositories)
|
|
67
44
|
else:
|
|
68
45
|
AbstraLogger.init("local")
|
|
69
|
-
assert local_queue is not None, "Local queue is not initialized"
|
|
70
46
|
repositories = build_editor_repositories(local_queue)
|
|
71
47
|
controller = MainController(repositories)
|
|
72
48
|
|
|
@@ -75,15 +51,14 @@ def process_main(
|
|
|
75
51
|
|
|
76
52
|
StdioPatcher.apply(controller)
|
|
77
53
|
|
|
78
|
-
|
|
54
|
+
ExecutionController(
|
|
79
55
|
repositories=controller.repositories,
|
|
80
56
|
stage=stage,
|
|
81
|
-
client=
|
|
57
|
+
client=None,
|
|
82
58
|
context=request,
|
|
83
59
|
).run()
|
|
84
60
|
|
|
85
61
|
AbstraLogger.debug("[ConsumerController Subprocess] Finished")
|
|
86
|
-
return execution_data
|
|
87
62
|
except Exception as e:
|
|
88
63
|
AbstraLogger.error(f"[ConsumerController Subprocess] Error: {e}")
|
|
89
64
|
AbstraLogger.capture_exception(e)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import pkgutil
|
|
3
3
|
import webbrowser
|
|
4
|
-
from multiprocessing import Pipe
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
from shutil import move
|
|
7
6
|
from tempfile import mkdtemp, mktemp
|
|
@@ -12,7 +11,6 @@ import flask
|
|
|
12
11
|
from abstra_internals.cloud_api import get_api_key_info, get_project_info
|
|
13
12
|
from abstra_internals.consts.filepaths import TEST_DATA_FILEPATH
|
|
14
13
|
from abstra_internals.controllers.execution.execution import ExecutionController
|
|
15
|
-
from abstra_internals.controllers.execution.execution_client import HeadlessClient
|
|
16
14
|
from abstra_internals.controllers.execution.execution_client_form import FormClient
|
|
17
15
|
from abstra_internals.controllers.execution.execution_client_hook import HookClient
|
|
18
16
|
from abstra_internals.credentials import (
|
|
@@ -69,7 +67,6 @@ from abstra_internals.utils.ai import AiWs
|
|
|
69
67
|
from abstra_internals.utils.diff import compute_updated_code_from_replacements
|
|
70
68
|
from abstra_internals.utils.file import module2path, path2module
|
|
71
69
|
from abstra_internals.utils.validate import validate_json
|
|
72
|
-
from abstra_internals.utils.websockets import bind_ws_with_connection
|
|
73
70
|
|
|
74
71
|
|
|
75
72
|
class UnknownNodeTypeError(Exception):
|
|
@@ -1684,13 +1681,12 @@ class MainController:
|
|
|
1684
1681
|
if not job:
|
|
1685
1682
|
raise Exception(f"Job with id {id} not found")
|
|
1686
1683
|
|
|
1687
|
-
|
|
1688
|
-
|
|
1684
|
+
print(f"Running job {job.id} ({job.title})")
|
|
1685
|
+
|
|
1689
1686
|
return ExecutionController(
|
|
1690
1687
|
repositories=self.repositories,
|
|
1691
1688
|
stage=job,
|
|
1692
|
-
context=
|
|
1693
|
-
client=HeadlessClient(context=ctx, conn=child_conn),
|
|
1689
|
+
context=JobContext(),
|
|
1694
1690
|
).run()
|
|
1695
1691
|
|
|
1696
1692
|
def debug_run_hook(self, id: str, request: Request):
|
|
@@ -1717,9 +1713,14 @@ class MainController:
|
|
|
1717
1713
|
|
|
1718
1714
|
context = HookContext(
|
|
1719
1715
|
request=request,
|
|
1716
|
+
response=Response(
|
|
1717
|
+
body="",
|
|
1718
|
+
headers={},
|
|
1719
|
+
status=200,
|
|
1720
|
+
),
|
|
1720
1721
|
)
|
|
1721
|
-
|
|
1722
|
-
client = HookClient(context=context
|
|
1722
|
+
|
|
1723
|
+
client = HookClient(context=context)
|
|
1723
1724
|
|
|
1724
1725
|
run_data = ExecutionController(
|
|
1725
1726
|
repositories=self.repositories,
|
|
@@ -1728,13 +1729,13 @@ class MainController:
|
|
|
1728
1729
|
context=context,
|
|
1729
1730
|
).run()
|
|
1730
1731
|
|
|
1731
|
-
response
|
|
1732
|
-
|
|
1732
|
+
if context.response is None or client.context.response is None:
|
|
1733
|
+
flask.abort(500)
|
|
1733
1734
|
|
|
1734
1735
|
return {
|
|
1735
|
-
"body": response.body,
|
|
1736
|
-
"status": response.status,
|
|
1737
|
-
"headers": response.headers,
|
|
1736
|
+
"body": client.context.response.body,
|
|
1737
|
+
"status": context.response.status,
|
|
1738
|
+
"headers": context.response.headers,
|
|
1738
1739
|
**run_data,
|
|
1739
1740
|
}
|
|
1740
1741
|
|
|
@@ -1764,14 +1765,10 @@ class MainController:
|
|
|
1764
1765
|
if not task_id:
|
|
1765
1766
|
raise Exception("Task ID is required for script execution")
|
|
1766
1767
|
|
|
1767
|
-
_, child_conn = Pipe()
|
|
1768
|
-
ctx = ScriptContext(task_id=task_id)
|
|
1769
|
-
|
|
1770
1768
|
return ExecutionController(
|
|
1771
1769
|
repositories=self.repositories,
|
|
1772
1770
|
stage=script,
|
|
1773
|
-
context=
|
|
1774
|
-
client=HeadlessClient(context=ctx, conn=child_conn),
|
|
1771
|
+
context=ScriptContext(task_id=task_id),
|
|
1775
1772
|
).run()
|
|
1776
1773
|
|
|
1777
1774
|
def debug_run_form_with_ai(self, id, prompt: str, url_params: Dict[str, str] = {}):
|
|
@@ -1796,10 +1793,8 @@ class MainController:
|
|
|
1796
1793
|
if not form:
|
|
1797
1794
|
raise Exception(f"Form with id {id} not found")
|
|
1798
1795
|
|
|
1799
|
-
parent_conn, child_conn = Pipe()
|
|
1800
|
-
bind_ws_with_connection(ws, parent_conn, block=False)
|
|
1801
1796
|
client = FormClient(
|
|
1802
|
-
|
|
1797
|
+
ws=ws, # type: ignore
|
|
1803
1798
|
context=context,
|
|
1804
1799
|
production_mode=False,
|
|
1805
1800
|
)
|
|
@@ -1823,13 +1818,10 @@ class MainController:
|
|
|
1823
1818
|
stage = self.create_job(title, str(tempfile), (0, 0))
|
|
1824
1819
|
tempfile.write_text(code)
|
|
1825
1820
|
|
|
1826
|
-
_, child_conn = Pipe()
|
|
1827
|
-
ctx = JobContext()
|
|
1828
1821
|
execution_result = ExecutionController(
|
|
1829
1822
|
repositories=self.repositories,
|
|
1830
1823
|
stage=stage,
|
|
1831
|
-
context=
|
|
1832
|
-
client=HeadlessClient(context=ctx, conn=child_conn),
|
|
1824
|
+
context=JobContext(),
|
|
1833
1825
|
).run()
|
|
1834
1826
|
|
|
1835
1827
|
self.delete_job(stage.id, remove_file=True)
|
|
@@ -225,6 +225,39 @@ class WorkflowController:
|
|
|
225
225
|
stage.workflow_transitions.append(transition)
|
|
226
226
|
self.repos.project.save(project)
|
|
227
227
|
|
|
228
|
+
def delete_transition(self, transition_id: str):
|
|
229
|
+
"""
|
|
230
|
+
Delete a transition from the workflow by its ID.
|
|
231
|
+
This method removes the specified transition from the workflow,
|
|
232
|
+
breaking the connection between two stages.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
transition_id (str): ID of the transition to delete.
|
|
236
|
+
|
|
237
|
+
Raises:
|
|
238
|
+
ValueError: If the transition with the given ID does not exist.
|
|
239
|
+
|
|
240
|
+
Copywritings:
|
|
241
|
+
Delete a transition from the workflow
|
|
242
|
+
Deleting a transition from the workflow...
|
|
243
|
+
"""
|
|
244
|
+
project = self.repos.project.load()
|
|
245
|
+
transition_found = False
|
|
246
|
+
|
|
247
|
+
for stage in project.workflow_stages:
|
|
248
|
+
for i, transition in enumerate(stage.workflow_transitions):
|
|
249
|
+
if transition.id == transition_id:
|
|
250
|
+
stage.workflow_transitions.pop(i)
|
|
251
|
+
transition_found = True
|
|
252
|
+
break
|
|
253
|
+
if transition_found:
|
|
254
|
+
break
|
|
255
|
+
|
|
256
|
+
if not transition_found:
|
|
257
|
+
raise ValueError(f"Transition {transition_id} does not exist.")
|
|
258
|
+
|
|
259
|
+
self.repos.project.save(project)
|
|
260
|
+
|
|
228
261
|
def update_workflow(self, workflow_state_dto: Dict):
|
|
229
262
|
"""
|
|
230
263
|
Update the entire workflow configuration with new stages and transitions.
|
|
@@ -43,6 +43,7 @@ class JobExecutionMock(ExecutionMock):
|
|
|
43
43
|
|
|
44
44
|
class HookContext(Serializable):
|
|
45
45
|
request: Request
|
|
46
|
+
response: Response
|
|
46
47
|
sent_tasks: List[str] = Field(default_factory=list)
|
|
47
48
|
legacy_thread_data: dict = Field(default_factory=dict)
|
|
48
49
|
mock_execution: HookExecutionMock = Field(default_factory=HookExecutionMock)
|
|
@@ -2,7 +2,7 @@ from datetime import datetime
|
|
|
2
2
|
from unittest import TestCase
|
|
3
3
|
|
|
4
4
|
from abstra_internals.entities.execution import Execution
|
|
5
|
-
from abstra_internals.entities.execution_context import HookContext, Request
|
|
5
|
+
from abstra_internals.entities.execution_context import HookContext, Request, Response
|
|
6
6
|
from abstra_internals.repositories.project.project import HookStage
|
|
7
7
|
from abstra_internals.utils import is_serializable
|
|
8
8
|
from abstra_internals.utils.datetime import from_utc_iso_string
|
|
@@ -24,7 +24,9 @@ class ExecutionTest(TestCase):
|
|
|
24
24
|
method="GET",
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
-
context = HookContext(
|
|
27
|
+
context = HookContext(
|
|
28
|
+
request=request, response=Response(headers={}, status=200, body="")
|
|
29
|
+
)
|
|
28
30
|
|
|
29
31
|
execution: Execution[HookContext] = Execution.create(
|
|
30
32
|
stage_id=mock_stage.id, context=context
|
|
@@ -55,7 +57,8 @@ class ExecutionTest(TestCase):
|
|
|
55
57
|
headers={"auth": "secret_token"},
|
|
56
58
|
query_params={"c": "3"},
|
|
57
59
|
method="GET",
|
|
58
|
-
)
|
|
60
|
+
),
|
|
61
|
+
response=Response(headers={}, status=200, body=""),
|
|
59
62
|
)
|
|
60
63
|
|
|
61
64
|
execution: Execution[HookContext] = Execution.create(
|
|
@@ -49,7 +49,7 @@ def download_to_path(url: str) -> pathlib.Path:
|
|
|
49
49
|
execution = SDKContextStore.get_execution()
|
|
50
50
|
save_dir = get_uploads_dir() / execution.id
|
|
51
51
|
save_dir.mkdir(parents=True, exist_ok=True)
|
|
52
|
-
save_path = save_dir /
|
|
52
|
+
save_path = save_dir / pathlib.Path(url).name
|
|
53
53
|
|
|
54
54
|
if url.startswith("http://") or url.startswith("https://"):
|
|
55
55
|
with save_path.open("wb") as f, requests.get(url, stream=True) as r:
|