From 97c11340c3175ba946ac6fc77481899fed508af5 Mon Sep 17 00:00:00 2001 From: pyranota <92104930+pyranota@users.noreply.github.com> Date: Tue, 4 Feb 2025 20:58:22 +0300 Subject: [PATCH 01/27] fix(python): clear env before installing/finding python (#5209) * fix(python): clear env before installing/finding python * add windows-specific variables --- .../windmill-worker/src/python_executor.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index a48e1cb396..b5cd49822e 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -308,12 +308,26 @@ impl PyVersion { let mut child_cmd = Command::new(uv_cmd); child_cmd + .env_clear() + .env("HOME", HOME_ENV.to_string()) + .env("PATH", PATH_ENV.to_string()) .args(["python", "install", v, "--python-preference=only-managed"]) // TODO: Do we need these? .envs([("UV_PYTHON_INSTALL_DIR", PY_INSTALL_DIR)]) .stdout(Stdio::piped()) .stderr(Stdio::piped()); + #[cfg(windows)] + { + child_cmd + .env("SystemRoot", SYSTEM_ROOT.as_str()) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "TMP", + std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ); + } + let child_process = start_child_process(child_cmd, "uv").await?; append_logs(&job_id, &w_id, logs, db).await; @@ -341,8 +355,23 @@ impl PyVersion { let uv_cmd = UV_PATH.as_str(); let mut child_cmd = Command::new(uv_cmd); + + #[cfg(windows)] + { + child_cmd + .env("SystemRoot", SYSTEM_ROOT.as_str()) + .env("USERPROFILE", crate::USERPROFILE_ENV.as_str()) + .env( + "TMP", + std::env::var("TMP").unwrap_or_else(|_| String::from("/tmp")), + ); + } + let output = child_cmd // .current_dir(job_dir) + .env_clear() + .env("HOME", HOME_ENV.to_string()) + .env("PATH", PATH_ENV.to_string()) .args([ "python", "find", From 62bfec029c00fd067b9906f546b3c9597a54c319 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 4 Feb 2025 22:11:05 +0100 Subject: [PATCH 02/27] fix: hide values of WHITELIST_ENVS --- backend/windmill-common/src/worker.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/windmill-common/src/worker.rs b/backend/windmill-common/src/worker.rs index 241679301f..3edee5be30 100644 --- a/backend/windmill-common/src/worker.rs +++ b/backend/windmill-common/src/worker.rs @@ -907,7 +907,7 @@ impl Default for WorkerConfigOpt { } } -#[derive(PartialEq, Debug, Clone)] +#[derive(PartialEq, Clone)] pub struct WorkerConfig { pub worker_tags: Vec, pub priority_tags_sorted: Vec, @@ -919,6 +919,13 @@ pub struct WorkerConfig { pub env_vars: HashMap, } +impl std::fmt::Debug for WorkerConfig { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "WorkerConfig {{ worker_tags: {:?}, priority_tags_sorted: {:?}, dedicated_worker: {:?}, init_bash: {:?}, cache_clear: {:?}, additional_python_paths: {:?}, pip_local_dependencies: {:?}, env_vars: {:?} }}", + self.worker_tags, self.priority_tags_sorted, self.dedicated_worker, self.init_bash, self.cache_clear, self.additional_python_paths, self.pip_local_dependencies, self.env_vars.iter().map(|(k, v)| format!("{}: {}{} ({} chars)", k, &v[..3.min(v.len())], "***", v.len())).collect::>().join(", ")) + } +} + #[derive(PartialEq, Debug, Clone)] pub struct PriorityTags { pub priority: u8, From 245c8719fc4bf9779be6c653f057d9ffb2724886 Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Tue, 4 Feb 2025 22:17:30 +0100 Subject: [PATCH 03/27] feat: more AI models (#5207) * feat: more AI models * nits * groq + openrouter * nit --- ...af9819d07b08cf7ca442ea7ec78a9b2b63b2.json} | 4 +- ...90d810a5e8ea6458d9f9fd484ced549ea82e.json} | 11 +- ...c61296a3ff7489ae12f52a19f9543173ac597.json | 40 +- ...b1e4b9022f5e841afd9eeb08a81688f6c0c8.json} | 6 +- ...0aafd27ef61c940daba186e7e66f668c31ed.json} | 7 +- ...c9cc0a303d70980346e1f3c4096a8922d7d5.json} | 26 +- ...dcc40f463cbc52d94ed9315cf9a547d4c89f2.json | 40 +- ...085502a1e77e30e59c7310c78976f77a810a3.json | 34 ++ .../20250131153251_ai_models.down.sql | 5 + .../20250131153251_ai_models.up.sql | 24 + backend/windmill-api/openapi.yaml | 46 +- backend/windmill-api/src/ai.rs | 310 +++++++---- backend/windmill-api/src/workspaces.rs | 67 ++- backend/windmill-api/src/workspaces_export.rs | 7 +- cli/gen/core/OpenAPI.ts | 2 +- cli/gen/services.gen.ts | 446 +++++++++++++++- cli/gen/types.gen.ts | 457 ++++++++++++++-- cli/settings.ts | 36 +- frontend/src/lib/components/Dev.svelte | 15 +- frontend/src/lib/components/Editor.svelte | 5 +- .../src/lib/components/FlowBuilder.svelte | 3 +- .../copilot/CodeCompletionStatus.svelte | 2 +- .../src/lib/components/copilot/CronGen.svelte | 5 +- .../lib/components/copilot/IteratorGen.svelte | 4 +- .../lib/components/copilot/MetadataGen.svelte | 4 +- .../components/copilot/PredicateGen.svelte | 4 +- .../lib/components/copilot/RegexGen.svelte | 4 +- .../lib/components/copilot/ScriptFix.svelte | 6 +- .../lib/components/copilot/ScriptGen.svelte | 63 ++- .../components/copilot/StepInputGen.svelte | 4 +- .../components/copilot/StepInputsGen.svelte | 4 +- .../{TestAiKey.svelte => TestAIKey.svelte} | 20 +- .../src/lib/components/copilot/completion.ts | 7 +- frontend/src/lib/components/copilot/flow.ts | 20 +- frontend/src/lib/components/copilot/lib.ts | 501 +++++++++--------- .../src/lib/components/instanceSettings.ts | 59 ++- .../components/sidebar/WorkspaceMenu.svelte | 13 +- frontend/src/lib/stores.ts | 19 +- .../src/routes/(root)/(logged)/+layout.svelte | 34 +- .../user/(user)/create_workspace/+page.svelte | 50 +- .../(logged)/workspace_settings/+page.svelte | 190 +++++-- 41 files changed, 1884 insertions(+), 720 deletions(-) rename backend/.sqlx/{query-286fa00c088df146c08c1934556f06a4243c66787ab8759a8045e60effd2fb77.json => query-0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2.json} (59%) rename backend/.sqlx/{query-0230bd64d2b6719c3536a106e18a68c7b43b3a9a9efa92b5517132e9c0d8a25b.json => query-103ef3cf5cf4d25d780e4aefd5b290d810a5e8ea6458d9f9fd484ced549ea82e.json} (50%) rename backend/.sqlx/{query-6940ddc5ee8d2a1048213d46f52d964b199604ba66dedbe9f89cea727d726a2d.json => query-1bbef6baa5b8e2522d685df2979bb1e4b9022f5e841afd9eeb08a81688f6c0c8.json} (57%) rename backend/.sqlx/{query-1610c79b8d238e3a35d795f34a2a0100b933b7f199d41cc2f554d57c811d55f3.json => query-4edf05cbf35325d444de0e74ee070aafd27ef61c940daba186e7e66f668c31ed.json} (56%) rename backend/.sqlx/{query-0331a81262e2d3c1bcfaeb64617b11eb68ab4599d64e5e5af639a9ac5d791fd0.json => query-51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5.json} (76%) create mode 100644 backend/.sqlx/query-b186efe51e7bb1924efbd7e9b36085502a1e77e30e59c7310c78976f77a810a3.json create mode 100644 backend/migrations/20250131153251_ai_models.down.sql create mode 100644 backend/migrations/20250131153251_ai_models.up.sql rename frontend/src/lib/components/copilot/{TestAiKey.svelte => TestAIKey.svelte} (74%) diff --git a/backend/.sqlx/query-286fa00c088df146c08c1934556f06a4243c66787ab8759a8045e60effd2fb77.json b/backend/.sqlx/query-0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2.json similarity index 59% rename from backend/.sqlx/query-286fa00c088df146c08c1934556f06a4243c66787ab8759a8045e60effd2fb77.json rename to backend/.sqlx/query-0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2.json index 790948f378..a0f52168b9 100644 --- a/backend/.sqlx/query-286fa00c088df146c08c1934556f06a4243c66787ab8759a8045e60effd2fb77.json +++ b/backend/.sqlx/query-0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT value\n FROM resource\n WHERE path = $1 AND workspace_id = $2", + "query": "SELECT value\n FROM resource\n WHERE path = $1 AND workspace_id = $2", "describe": { "columns": [ { @@ -19,5 +19,5 @@ true ] }, - "hash": "286fa00c088df146c08c1934556f06a4243c66787ab8759a8045e60effd2fb77" + "hash": "0a9dd1addaf48eeb46eed59abb6daf9819d07b08cf7ca442ea7ec78a9b2b63b2" } diff --git a/backend/.sqlx/query-0230bd64d2b6719c3536a106e18a68c7b43b3a9a9efa92b5517132e9c0d8a25b.json b/backend/.sqlx/query-103ef3cf5cf4d25d780e4aefd5b290d810a5e8ea6458d9f9fd484ced549ea82e.json similarity index 50% rename from backend/.sqlx/query-0230bd64d2b6719c3536a106e18a68c7b43b3a9a9efa92b5517132e9c0d8a25b.json rename to backend/.sqlx/query-103ef3cf5cf4d25d780e4aefd5b290d810a5e8ea6458d9f9fd484ced549ea82e.json index e38ddcb726..15d772ab16 100644 --- a/backend/.sqlx/query-0230bd64d2b6719c3536a106e18a68c7b43b3a9a9efa92b5517132e9c0d8a25b.json +++ b/backend/.sqlx/query-103ef3cf5cf4d25d780e4aefd5b290d810a5e8ea6458d9f9fd484ced549ea82e.json @@ -1,21 +1,22 @@ { "db_name": "PostgreSQL", - "query": "SELECT ai_resource, code_completion_enabled FROM workspace_settings WHERE workspace_id = $1", + "query": "SELECT value, resource_type FROM resource WHERE path = $1 AND workspace_id = $2", "describe": { "columns": [ { "ordinal": 0, - "name": "ai_resource", + "name": "value", "type_info": "Jsonb" }, { "ordinal": 1, - "name": "code_completion_enabled", - "type_info": "Bool" + "name": "resource_type", + "type_info": "Varchar" } ], "parameters": { "Left": [ + "Text", "Text" ] }, @@ -24,5 +25,5 @@ false ] }, - "hash": "0230bd64d2b6719c3536a106e18a68c7b43b3a9a9efa92b5517132e9c0d8a25b" + "hash": "103ef3cf5cf4d25d780e4aefd5b290d810a5e8ea6458d9f9fd484ced549ea82e" } diff --git a/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json b/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json index b6fee2c5ff..03da2cee85 100644 --- a/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json +++ b/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json @@ -70,68 +70,73 @@ }, { "ordinal": 13, - "name": "code_completion_enabled", - "type_info": "Bool" - }, - { - "ordinal": 14, "name": "error_handler_extra_args", "type_info": "Json" }, { - "ordinal": 15, + "ordinal": 14, "name": "error_handler_muted_on_cancel", "type_info": "Bool" }, { - "ordinal": 16, + "ordinal": 15, "name": "large_file_storage", "type_info": "Jsonb" }, { - "ordinal": 17, + "ordinal": 16, "name": "git_sync", "type_info": "Jsonb" }, { - "ordinal": 18, + "ordinal": 17, "name": "default_app", "type_info": "Varchar" }, { - "ordinal": 19, + "ordinal": 18, "name": "auto_add", "type_info": "Bool" }, { - "ordinal": 20, + "ordinal": 19, "name": "automatic_billing", "type_info": "Bool" }, { - "ordinal": 21, + "ordinal": 20, "name": "default_scripts", "type_info": "Jsonb" }, { - "ordinal": 22, + "ordinal": 21, "name": "deploy_ui", "type_info": "Jsonb" }, { - "ordinal": 23, + "ordinal": 22, "name": "mute_critical_alerts", "type_info": "Bool" }, { - "ordinal": 24, + "ordinal": 23, "name": "color", "type_info": "Varchar" }, { - "ordinal": 25, + "ordinal": 24, "name": "operator_settings", "type_info": "Jsonb" + }, + { + "ordinal": 25, + "name": "ai_models", + "type_info": "VarcharArray" + }, + { + "ordinal": 26, + "name": "code_completion_model", + "type_info": "Varchar" } ], "parameters": { @@ -153,7 +158,6 @@ true, true, true, - false, true, false, true, @@ -165,6 +169,8 @@ true, true, true, + true, + false, true ] }, diff --git a/backend/.sqlx/query-6940ddc5ee8d2a1048213d46f52d964b199604ba66dedbe9f89cea727d726a2d.json b/backend/.sqlx/query-1bbef6baa5b8e2522d685df2979bb1e4b9022f5e841afd9eeb08a81688f6c0c8.json similarity index 57% rename from backend/.sqlx/query-6940ddc5ee8d2a1048213d46f52d964b199604ba66dedbe9f89cea727d726a2d.json rename to backend/.sqlx/query-1bbef6baa5b8e2522d685df2979bb1e4b9022f5e841afd9eeb08a81688f6c0c8.json index 4891854d48..4fcd1f0969 100644 --- a/backend/.sqlx/query-6940ddc5ee8d2a1048213d46f52d964b199604ba66dedbe9f89cea727d726a2d.json +++ b/backend/.sqlx/query-1bbef6baa5b8e2522d685df2979bb1e4b9022f5e841afd9eeb08a81688f6c0c8.json @@ -1,15 +1,15 @@ { "db_name": "PostgreSQL", - "query": "UPDATE workspace_settings SET ai_resource = NULL, code_completion_enabled = $1 WHERE workspace_id = $2", + "query": "UPDATE workspace_settings SET ai_resource = NULL, code_completion_model = $1, ai_models = '{}' WHERE workspace_id = $2", "describe": { "columns": [], "parameters": { "Left": [ - "Bool", + "Varchar", "Text" ] }, "nullable": [] }, - "hash": "6940ddc5ee8d2a1048213d46f52d964b199604ba66dedbe9f89cea727d726a2d" + "hash": "1bbef6baa5b8e2522d685df2979bb1e4b9022f5e841afd9eeb08a81688f6c0c8" } diff --git a/backend/.sqlx/query-1610c79b8d238e3a35d795f34a2a0100b933b7f199d41cc2f554d57c811d55f3.json b/backend/.sqlx/query-4edf05cbf35325d444de0e74ee070aafd27ef61c940daba186e7e66f668c31ed.json similarity index 56% rename from backend/.sqlx/query-1610c79b8d238e3a35d795f34a2a0100b933b7f199d41cc2f554d57c811d55f3.json rename to backend/.sqlx/query-4edf05cbf35325d444de0e74ee070aafd27ef61c940daba186e7e66f668c31ed.json index 9b94af8432..4eae8c22fb 100644 --- a/backend/.sqlx/query-1610c79b8d238e3a35d795f34a2a0100b933b7f199d41cc2f554d57c811d55f3.json +++ b/backend/.sqlx/query-4edf05cbf35325d444de0e74ee070aafd27ef61c940daba186e7e66f668c31ed.json @@ -1,16 +1,17 @@ { "db_name": "PostgreSQL", - "query": "UPDATE workspace_settings SET ai_resource = $1, code_completion_enabled = $2 WHERE workspace_id = $3", + "query": "UPDATE workspace_settings SET ai_resource = $1, code_completion_model = $2, ai_models = $3 WHERE workspace_id = $4", "describe": { "columns": [], "parameters": { "Left": [ "Jsonb", - "Bool", + "Varchar", + "VarcharArray", "Text" ] }, "nullable": [] }, - "hash": "1610c79b8d238e3a35d795f34a2a0100b933b7f199d41cc2f554d57c811d55f3" + "hash": "4edf05cbf35325d444de0e74ee070aafd27ef61c940daba186e7e66f668c31ed" } diff --git a/backend/.sqlx/query-0331a81262e2d3c1bcfaeb64617b11eb68ab4599d64e5e5af639a9ac5d791fd0.json b/backend/.sqlx/query-51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5.json similarity index 76% rename from backend/.sqlx/query-0331a81262e2d3c1bcfaeb64617b11eb68ab4599d64e5e5af639a9ac5d791fd0.json rename to backend/.sqlx/query-51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5.json index 5d0a70183e..813981bae2 100644 --- a/backend/.sqlx/query-0331a81262e2d3c1bcfaeb64617b11eb68ab4599d64e5e5af639a9ac5d791fd0.json +++ b/backend/.sqlx/query-51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n -- slack_team_id, \n -- slack_name, \n -- slack_command_script, \n -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,\n auto_invite_domain IS NOT NULL AS \"auto_invite_enabled!\",\n CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS \"auto_invite_as!\", \n CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS \"auto_invite_mode!\", \n webhook, \n deploy_to, \n error_handler, \n ai_resource, \n code_completion_enabled, \n error_handler_extra_args, \n error_handler_muted_on_cancel, \n large_file_storage, \n git_sync,\n default_app,\n default_scripts,\n workspace.name\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1", + "query": "SELECT\n -- slack_team_id, \n -- slack_name, \n -- slack_command_script, \n -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,\n auto_invite_domain IS NOT NULL AS \"auto_invite_enabled!\",\n CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS \"auto_invite_as!\", \n CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS \"auto_invite_mode!\", \n webhook, \n deploy_to, \n error_handler, \n ai_resource, \n ai_models,\n code_completion_model,\n error_handler_extra_args, \n error_handler_muted_on_cancel, \n large_file_storage, \n git_sync,\n default_app,\n default_scripts,\n workspace.name\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1", "describe": { "columns": [ { @@ -40,41 +40,46 @@ }, { "ordinal": 7, - "name": "code_completion_enabled", - "type_info": "Bool" + "name": "ai_models", + "type_info": "VarcharArray" }, { "ordinal": 8, + "name": "code_completion_model", + "type_info": "Varchar" + }, + { + "ordinal": 9, "name": "error_handler_extra_args", "type_info": "Json" }, { - "ordinal": 9, + "ordinal": 10, "name": "error_handler_muted_on_cancel", "type_info": "Bool" }, { - "ordinal": 10, + "ordinal": 11, "name": "large_file_storage", "type_info": "Jsonb" }, { - "ordinal": 11, + "ordinal": 12, "name": "git_sync", "type_info": "Jsonb" }, { - "ordinal": 12, + "ordinal": 13, "name": "default_app", "type_info": "Varchar" }, { - "ordinal": 13, + "ordinal": 14, "name": "default_scripts", "type_info": "Jsonb" }, { - "ordinal": 14, + "ordinal": 15, "name": "name", "type_info": "Varchar" } @@ -94,6 +99,7 @@ true, false, true, + true, false, true, true, @@ -102,5 +108,5 @@ false ] }, - "hash": "0331a81262e2d3c1bcfaeb64617b11eb68ab4599d64e5e5af639a9ac5d791fd0" + "hash": "51648e377d47815d0b15694572d5c9cc0a303d70980346e1f3c4096a8922d7d5" } diff --git a/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json b/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json index baf7cb42f5..920176991b 100644 --- a/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json +++ b/backend/.sqlx/query-55cb03040bc2a8c53dd7fbb42bbdcc40f463cbc52d94ed9315cf9a547d4c89f2.json @@ -70,68 +70,73 @@ }, { "ordinal": 13, - "name": "code_completion_enabled", - "type_info": "Bool" - }, - { - "ordinal": 14, "name": "error_handler_extra_args", "type_info": "Json" }, { - "ordinal": 15, + "ordinal": 14, "name": "error_handler_muted_on_cancel", "type_info": "Bool" }, { - "ordinal": 16, + "ordinal": 15, "name": "large_file_storage", "type_info": "Jsonb" }, { - "ordinal": 17, + "ordinal": 16, "name": "git_sync", "type_info": "Jsonb" }, { - "ordinal": 18, + "ordinal": 17, "name": "default_app", "type_info": "Varchar" }, { - "ordinal": 19, + "ordinal": 18, "name": "auto_add", "type_info": "Bool" }, { - "ordinal": 20, + "ordinal": 19, "name": "automatic_billing", "type_info": "Bool" }, { - "ordinal": 21, + "ordinal": 20, "name": "default_scripts", "type_info": "Jsonb" }, { - "ordinal": 22, + "ordinal": 21, "name": "deploy_ui", "type_info": "Jsonb" }, { - "ordinal": 23, + "ordinal": 22, "name": "mute_critical_alerts", "type_info": "Bool" }, { - "ordinal": 24, + "ordinal": 23, "name": "color", "type_info": "Varchar" }, { - "ordinal": 25, + "ordinal": 24, "name": "operator_settings", "type_info": "Jsonb" + }, + { + "ordinal": 25, + "name": "ai_models", + "type_info": "VarcharArray" + }, + { + "ordinal": 26, + "name": "code_completion_model", + "type_info": "Varchar" } ], "parameters": { @@ -153,7 +158,6 @@ true, true, true, - false, true, false, true, @@ -165,6 +169,8 @@ true, true, true, + true, + false, true ] }, diff --git a/backend/.sqlx/query-b186efe51e7bb1924efbd7e9b36085502a1e77e30e59c7310c78976f77a810a3.json b/backend/.sqlx/query-b186efe51e7bb1924efbd7e9b36085502a1e77e30e59c7310c78976f77a810a3.json new file mode 100644 index 0000000000..d856ab0109 --- /dev/null +++ b/backend/.sqlx/query-b186efe51e7bb1924efbd7e9b36085502a1e77e30e59c7310c78976f77a810a3.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT ai_resource, code_completion_model, ai_models FROM workspace_settings WHERE workspace_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "ai_resource", + "type_info": "Jsonb" + }, + { + "ordinal": 1, + "name": "code_completion_model", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "ai_models", + "type_info": "VarcharArray" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + true, + true, + false + ] + }, + "hash": "b186efe51e7bb1924efbd7e9b36085502a1e77e30e59c7310c78976f77a810a3" +} diff --git a/backend/migrations/20250131153251_ai_models.down.sql b/backend/migrations/20250131153251_ai_models.down.sql new file mode 100644 index 0000000000..76eaf5ba6b --- /dev/null +++ b/backend/migrations/20250131153251_ai_models.down.sql @@ -0,0 +1,5 @@ +ALTER TABLE workspace_settings ADD COLUMN code_completion_enabled BOOLEAN DEFAULT FALSE NOT NULL; + +UPDATE workspace_settings SET code_completion_enabled = TRUE WHERE code_completion_model IS NOT NULL; + +ALTER TABLE workspace_settings DROP COLUMN ai_models, DROP COLUMN code_completion_model; \ No newline at end of file diff --git a/backend/migrations/20250131153251_ai_models.up.sql b/backend/migrations/20250131153251_ai_models.up.sql new file mode 100644 index 0000000000..7145e0bb98 --- /dev/null +++ b/backend/migrations/20250131153251_ai_models.up.sql @@ -0,0 +1,24 @@ +ALTER TABLE workspace_settings + ADD COLUMN ai_models varchar(255)[] DEFAULT '{}' NOT NULL, + ADD COLUMN code_completion_model varchar(255); + +UPDATE workspace_settings +SET ai_models = CASE + WHEN ai_resource->>'provider' = 'openai' THEN ARRAY['gpt-4o'] + WHEN ai_resource->>'provider' = 'anthropic' THEN ARRAY['claude-3-5-sonnet-latest'] + WHEN ai_resource->>'provider' = 'mistral' THEN ARRAY['codestral-latest'] + ELSE ai_models +END +WHERE ai_resource->>'path' IS NOT NULL; + +UPDATE workspace_settings +SET code_completion_model = CASE + WHEN ai_resource->>'provider' = 'openai' THEN 'gpt-4o' + WHEN ai_resource->>'provider' = 'anthropic' THEN 'claude-3-5-sonnet-latest' + WHEN ai_resource->>'provider' = 'mistral' THEN 'codestral-latest' + ELSE code_completion_model +END +WHERE code_completion_enabled IS TRUE; + + +ALTER TABLE workspace_settings DROP COLUMN code_completion_enabled; \ No newline at end of file diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 1b6184e662..9df3cda043 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1745,9 +1745,13 @@ paths: deploy_to: type: string ai_resource: - $ref: "#/components/schemas/AiResource" - code_completion_enabled: - type: boolean + $ref: "#/components/schemas/AIResource" + code_completion_model: + type: string + ai_models: + type: array + items: + type: string error_handler: type: string error_handler_extra_args: @@ -1771,7 +1775,7 @@ paths: operator_settings: $ref: "#/components/schemas/OperatorSettings" required: - - code_completion_enabled + - ai_models - automatic_billing - error_handler_muted_on_cancel @@ -2084,12 +2088,16 @@ paths: schema: type: object required: - - code_completion_enabled + - ai_models properties: ai_resource: - $ref: "#/components/schemas/AiResource" - code_completion_enabled: - type: boolean + $ref: "#/components/schemas/AIResource" + code_completion_model: + type: string + ai_models: + type: array + items: + type: string responses: "200": description: status @@ -2116,15 +2124,18 @@ paths: type: object properties: ai_provider: - type: string + $ref: "#/components/schemas/AIProvider" exists_ai_resource: type: boolean - code_completion_enabled: - type: boolean + code_completion_model: + type: string + ai_models: + type: array + items: + type: string required: - - ai_provider - exists_ai_resource - - code_completion_enabled + - ai_models /w/{workspace}/workspaces/edit_error_handler: post: @@ -11546,16 +11557,21 @@ components: schemas: $ref: "../../openflow.openapi.yaml#/components/schemas" - AiResource: + AIProvider: + type: string + enum: [openai, anthropic, mistral, deepseek, groq, openrouter, customai] + + AIResource: type: object properties: path: type: string provider: - type: string + $ref: "#/components/schemas/AIProvider" required: - path - provider + Script: type: object properties: diff --git a/backend/windmill-api/src/ai.rs b/backend/windmill-api/src/ai.rs index 3b97ba18ce..ba055d27de 100644 --- a/backend/windmill-api/src/ai.rs +++ b/backend/windmill-api/src/ai.rs @@ -4,27 +4,21 @@ use crate::{ }; use anthropic::AnthropicCache; -use axum::{ - body::Bytes, - extract::{Path, Query}, - response::IntoResponse, - routing::post, - Extension, Router, -}; +use anyhow::Context; +use axum::{body::Bytes, extract::Path, response::IntoResponse, routing::post, Extension, Router}; +use http::HeaderMap; use lazy_static::lazy_static; -use mistral::MistralCache; -use openai::OpenaiCache; use quick_cache::sync::Cache; use reqwest::{Client, RequestBuilder}; -use serde::{Deserialize, Deserializer}; -use windmill_audit::audit_ee::audit_log; -use windmill_audit::ActionKind; -use windmill_common::error::{to_anyhow, Result}; - -use windmill_common::error::Error; - +use serde::{Deserialize, Serialize}; use serde_json::value::{RawValue, Value}; use std::collections::HashMap; +use windmill_audit::{audit_ee::audit_log, ActionKind}; +use windmill_common::error::{to_anyhow, Error, Result}; + +use mistral::MistralCache; +use openai::OpenaiCache; +use openai_api_compatible::OpenaiApiCompatibleCache; lazy_static::lazy_static! { static ref HTTP_CLIENT: Client = reqwest::ClientBuilder::new() @@ -33,16 +27,63 @@ lazy_static::lazy_static! { .build().unwrap(); } -trait AiRequest { - fn prepare_request(self, path: &str, body: Bytes) -> Result; +mod openai_api_compatible { + use super::*; + + #[derive(Deserialize, Clone, Debug)] + pub struct OpenaiApiCompatibleCache { + pub base_url: String, + pub api_key: Option, + } + + impl OpenaiApiCompatibleCache { + pub fn prepare_request(self, path: &str, body: Bytes) -> Result { + let url = format!("{}/{}", self.base_url, path); + + let mut request = HTTP_CLIENT + .post(url) + .header("content-type", "application/json") + .body(body); + + if let Some(api_key) = self.api_key { + request = request.header("Authorization", format!("Bearer {}", api_key)); + } + + Ok(request) + } + } + + pub async fn get_cached_value( + db: &DB, + w_id: &str, + resource: Value, + base_url: Option, + ) -> Result { + let mut resource: OpenaiApiCompatibleCache = if let Some(base_url) = base_url { + let api_key = match resource { + Value::Object(mut obj) => obj + .remove("api_key") + .map(|v| serde_json::from_value::(v.clone()).ok()) + .flatten(), + _ => None, + }; + OpenaiApiCompatibleCache { base_url, api_key } + } else { + serde_json::from_value(resource).with_context(|| "validating custom AI resource")? + }; + + if let Some(api_key) = resource.api_key { + resource.api_key = Some(get_variable_or_self(api_key, db, w_id).await?); + } + + Ok(KeyCache::OpenaiApiCompatible(resource)) + } } mod openai { use super::*; - use super::{get_variable_or_self, KeyCache}; - - const API_VERSION: &str = "2023-05-15"; + const API_VERSION: &str = "2024-10-21"; #[derive(Deserialize, Debug)] struct OpenaiResource { @@ -94,8 +135,8 @@ mod openai { } const BASE_URL: &str = "https://api.openai.com/v1"; - impl AiRequest for OpenaiCache { - fn prepare_request(self, openai_path: &str, mut body: Bytes) -> Result { + impl OpenaiCache { + pub fn prepare_request(self, openai_path: &str, mut body: Bytes) -> Result { let OpenaiCache { api_key, azure_base_path, organization_id, user } = self; if user.is_some() { tracing::debug!("Adding user to request body"); @@ -245,15 +286,9 @@ mod anthropic { const API_VERSION: &str = "2023-06-01"; - impl AnthropicCache { - pub fn new(api_key: String) -> Self { - Self { api_key } - } - } - const BASE_URL: &str = "https://api.anthropic.com"; - impl AiRequest for AnthropicCache { - fn prepare_request(self, anthropic_path: &str, body: Bytes) -> Result { + impl AnthropicCache { + pub fn prepare_request(self, anthropic_path: &str, body: Bytes) -> Result { let AnthropicCache { api_key } = self; let url = format!("{}/{}", BASE_URL, anthropic_path); let request = HTTP_CLIENT @@ -270,8 +305,7 @@ mod anthropic { let mut resource: AnthropicCache = serde_json::from_value(resource) .map_err(|e| Error::InternalErr(format!("validating anthropic resource {e:#}")))?; resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?; - let workspace_cache = AnthropicCache::new(resource.api_key); - Ok(KeyCache::Anthropic(workspace_cache)) + Ok(KeyCache::Anthropic(resource)) } } @@ -283,15 +317,9 @@ mod mistral { pub api_key: String, } - impl MistralCache { - pub fn new(api_key: String) -> Self { - Self { api_key } - } - } - const BASE_URL: &str = "https://api.mistral.ai"; - impl AiRequest for MistralCache { - fn prepare_request(self, mistral_path: &str, body: Bytes) -> Result { + impl MistralCache { + pub fn prepare_request(self, mistral_path: &str, body: Bytes) -> Result { let MistralCache { api_key } = self; let url = format!("{}/{}", BASE_URL, mistral_path); @@ -309,9 +337,7 @@ mod mistral { let mut resource: MistralCache = serde_json::from_value(resource) .map_err(|e| Error::InternalErr(format!("validating mistral resource {e:#}")))?; resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?; - - let workspace_cache = MistralCache::new(resource.api_key); - Ok(KeyCache::Mistral(workspace_cache)) + Ok(KeyCache::Mistral(resource)) } } @@ -320,16 +346,17 @@ pub enum KeyCache { Openai(OpenaiCache), Anthropic(AnthropicCache), Mistral(MistralCache), + OpenaiApiCompatible(OpenaiApiCompatibleCache), } #[derive(Clone, Debug)] -pub struct AiCache { +pub struct AICache { pub path: String, pub cached_key: KeyCache, pub expires_at: std::time::Instant, } -impl AiCache { +impl AICache { pub fn new(path: String, cached_key: KeyCache) -> Self { Self { path, @@ -343,35 +370,57 @@ impl AiCache { } lazy_static! { - pub static ref AI_KEY_CACHE: Cache = Cache::new(500); + pub static ref AI_KEY_CACHE: Cache = Cache::new(500); } -#[derive(Deserialize, Debug)] -struct ProxyQueryParams { - no_cache: Option, +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "lowercase")] +pub enum AIProvider { + OpenAI, + Anthropic, + Mistral, + DeepSeek, + Groq, + OpenRouter, + CustomAI, } -#[derive(Deserialize, Debug)] -pub struct AiResource { - pub path: String, - #[serde(deserialize_with = "check_if_valid_ai_provider")] - pub provider: String, -} - -fn check_if_valid_ai_provider<'de, D>(provider: D) -> std::result::Result -where - D: Deserializer<'de>, -{ - let provider = String::deserialize(provider)?; - match provider.as_str() { - "anthropic" | "openai" | "mistral" => Ok(provider), - _ => Err(serde::de::Error::custom( - "Only the following Ai providers are supported: openai, anthropic and mistral" - .to_string(), - )), +impl AIProvider { + pub fn get_openai_compatible_base_url(&self) -> Result> { + match self { + AIProvider::DeepSeek => Ok(Some("https://api.deepseek.com/v1".to_string())), + AIProvider::Groq => Ok(Some("https://api.groq.com/openai/v1".to_string())), + AIProvider::OpenRouter => Ok(Some("https://openrouter.ai/api/v1".to_string())), + AIProvider::CustomAI => Ok(None), + _ => Err(Error::BadRequest( + "Please use the specific provider instead of the OpenAI compatible one".to_string(), + )), + } } } +impl TryFrom<&str> for AIProvider { + type Error = Error; + fn try_from(s: &str) -> Result { + match s { + "openai" => Ok(AIProvider::OpenAI), + "anthropic" => Ok(AIProvider::Anthropic), + "mistral" => Ok(AIProvider::Mistral), + "groq" => Ok(AIProvider::Groq), + "openrouter" => Ok(AIProvider::OpenRouter), + "deepseek" => Ok(AIProvider::DeepSeek), + "customai" => Ok(AIProvider::CustomAI), + _ => Err(Error::BadRequest(format!("Invalid AI provider: {}", s))), + } + } +} + +#[derive(Deserialize, Debug)] +pub struct AIResource { + pub path: String, + pub provider: AIProvider, +} + pub fn workspaced_service() -> Router { let router = Router::new().route("/proxy/*ai", post(proxy)); @@ -382,74 +431,105 @@ async fn proxy( authed: ApiAuthed, Extension(db): Extension, Path((w_id, ai_path)): Path<(String, String)>, - Query(query_params): Query, + headers: HeaderMap, body: Bytes, ) -> impl IntoResponse { let workspace_cache = AI_KEY_CACHE.get(&w_id); + let forced_resource_path = headers + .get("X-Resource-Path") + .map(|v| v.to_str().unwrap_or("").to_string()); let ai_cache = match workspace_cache { - Some(cache) if !cache.is_expired() && !query_params.no_cache.unwrap_or(false) => { - cache.cached_key - } + Some(cache) if !cache.is_expired() && forced_resource_path.is_none() => cache.cached_key, _ => { - let ai_resource = sqlx::query_scalar!( - "SELECT ai_resource FROM workspace_settings WHERE workspace_id = $1", - &w_id - ) - .fetch_one(&db) - .await?; + let (resource, resource_path, ai_provider) = if let Some(resource_path) = + forced_resource_path + { + // guess the provider from the resource type + let record = sqlx::query!( + "SELECT value, resource_type FROM resource WHERE path = $1 AND workspace_id = $2", + &resource_path, + &w_id + ) + .fetch_optional(&db) + .await? + .ok_or_else(|| { + Error::NotFound(format!( + "Could not find the resource {}, update the resource path in the workspace settings", resource_path + )) + })?; - if ai_resource.is_none() { - return Err(Error::InternalErr("AI resource not configured".to_string())); - } + ( + record.value, + resource_path, + AIProvider::try_from(record.resource_type.as_str())?, + ) + } else { + let ai_resource = sqlx::query_scalar!( + "SELECT ai_resource FROM workspace_settings WHERE workspace_id = $1", + &w_id + ) + .fetch_one(&db) + .await?; - let ai_resource = serde_json::from_value::(ai_resource.unwrap()) - .map_err(|e| Error::BadRequest(e.to_string()))?; - let ai_resource_path = ai_resource.path; + if ai_resource.is_none() { + return Err(Error::InternalErr("AI resource not configured".to_string())); + } - let resource = sqlx::query_scalar!( - "SELECT value - FROM resource - WHERE path = $1 AND workspace_id = $2", - &ai_resource_path, - &w_id - ) - .fetch_optional(&db) - .await? - .ok_or_else(|| { - Error::InternalErr(format!( - "Could not find the {} resource at path {ai_resource_path}, update the resource path in the workspace settings", ai_resource.provider - )) - })?; + let ai_resource = serde_json::from_value::(ai_resource.unwrap()) + .map_err(|e| Error::BadRequest(e.to_string()))?; + + let resource = sqlx::query_scalar!( + "SELECT value + FROM resource + WHERE path = $1 AND workspace_id = $2", + &ai_resource.path, + &w_id + ) + .fetch_optional(&db) + .await? + .ok_or_else(|| { + Error::NotFound(format!( + "Could not find the {:?} resource at path {}, update the resource path in the workspace settings", ai_resource.provider, ai_resource.path + )) + })?; + + (resource, ai_resource.path, ai_resource.provider) + }; if resource.is_none() { return Err(Error::InternalErr(format!( - "{} resource missing value", - ai_resource.provider + "{:?} resource missing value", + ai_provider ))); } let resource = resource.unwrap(); - let ai_cache = match ai_resource.provider.as_str() { - "openai" => openai::get_cached_value(&db, &w_id, resource).await, - "anthropic" => anthropic::get_cached_value(&db, &w_id, resource).await, - "mistral" => mistral::get_cached_value(&db, &w_id, resource).await, - provider => { - return Err(Error::BadRequest(format!("{} is not supported", provider))) + let ai_cache = match ai_provider { + AIProvider::OpenAI => openai::get_cached_value(&db, &w_id, resource).await, + AIProvider::Anthropic => anthropic::get_cached_value(&db, &w_id, resource).await, + AIProvider::Mistral => mistral::get_cached_value(&db, &w_id, resource).await, + _ => { + openai_api_compatible::get_cached_value( + &db, + &w_id, + resource, + ai_provider.get_openai_compatible_base_url()?, + ) + .await } }; let ai_cache = ai_cache?; - AI_KEY_CACHE.insert( - w_id.clone(), - AiCache::new(ai_resource_path, ai_cache.clone()), - ); + AI_KEY_CACHE.insert(w_id.clone(), AICache::new(resource_path, ai_cache.clone())); ai_cache } }; - let (path, request) = match ai_cache { - KeyCache::Openai(cached) => ("openai_path", cached.prepare_request(&ai_path, body)), - KeyCache::Anthropic(cached) => ("anthropic_path", cached.prepare_request(&ai_path, body)), - KeyCache::Mistral(cached) => ("mistral_path", cached.prepare_request(&ai_path, body)), + + let request = match ai_cache { + KeyCache::Openai(cached) => cached.prepare_request(&ai_path, body), + KeyCache::Anthropic(cached) => cached.prepare_request(&ai_path, body), + KeyCache::Mistral(cached) => cached.prepare_request(&ai_path, body), + KeyCache::OpenaiApiCompatible(cached) => cached.prepare_request(&ai_path, body), }; let response = request?.send().await.map_err(to_anyhow)?; @@ -463,7 +543,7 @@ async fn proxy( ActionKind::Execute, &w_id, Some(&authed.email), - Some([(path, &format!("{:?}", ai_path)[..])].into()), + Some([("ai_resource_path", &format!("{:?}", ai_path)[..])].into()), ) .await?; tx.commit().await?; diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index 572319dc39..3b9fcc7049 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; -use crate::ai::{AiResource, AI_KEY_CACHE}; +use crate::ai::{AIProvider, AIResource, AI_KEY_CACHE}; use crate::db::ApiAuthed; use crate::users_ee::send_email_if_possible; use crate::utils::get_instance_username_or_create_pending; @@ -177,7 +177,9 @@ pub struct WorkspaceSettings { pub webhook: Option, pub deploy_to: Option, pub ai_resource: Option, - pub code_completion_enabled: bool, + pub ai_models: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub code_completion_model: Option, pub error_handler: Option, pub error_handler_extra_args: Option, pub error_handler_muted_on_cancel: Option, @@ -246,7 +248,8 @@ struct EditWebhook { #[derive(Deserialize)] struct EditCopilotConfig { ai_resource: Option, - code_completion_enabled: bool, + code_completion_model: Option, + ai_models: Vec, } #[derive(Deserialize, Serialize, Debug)] @@ -660,27 +663,37 @@ async fn edit_copilot_config( let mut tx = db.begin().await?; if let Some(ai_resource) = &eo.ai_resource { - let path = serde_json::from_value::(ai_resource.clone()) - .map_err(|e| Error::BadRequest(e.to_string()))? - .path; + let parsed_ai_resource = serde_json::from_value::(ai_resource.clone()) + .map_err(|e| Error::BadRequest(e.to_string()))?; + + #[cfg(not(feature = "enterprise"))] + { + if matches!(parsed_ai_resource.provider, AIProvider::CustomAI) { + return Err(Error::BadRequest( + "Custom AI is only available on EE".to_string(), + )); + } + } + sqlx::query!( - "UPDATE workspace_settings SET ai_resource = $1, code_completion_enabled = $2 WHERE workspace_id = $3", + "UPDATE workspace_settings SET ai_resource = $1, code_completion_model = $2, ai_models = $3 WHERE workspace_id = $4", ai_resource, - eo.code_completion_enabled, + eo.code_completion_model, + eo.ai_models.as_slice(), &w_id ) .execute(&mut *tx) .await?; if let Some(cached) = AI_KEY_CACHE.get(&w_id) { - if cached.path != path { + if cached.path != parsed_ai_resource.path { AI_KEY_CACHE.remove(&w_id); } } } else { sqlx::query!( - "UPDATE workspace_settings SET ai_resource = NULL, code_completion_enabled = $1 WHERE workspace_id = $2", - eo.code_completion_enabled, + "UPDATE workspace_settings SET ai_resource = NULL, code_completion_model = $1, ai_models = '{}' WHERE workspace_id = $2", + eo.code_completion_model, &w_id, ) .execute(&mut *tx) @@ -698,8 +711,8 @@ async fn edit_copilot_config( [ ("ai_resource", &format!("{:?}", eo.ai_resource)[..]), ( - "code_completion_enabled", - &format!("{:?}", eo.code_completion_enabled)[..], + "code_completion_model", + &format!("{:?}", eo.code_completion_model)[..], ), ] .into(), @@ -713,9 +726,12 @@ async fn edit_copilot_config( #[derive(Serialize)] struct CopilotInfo { - pub ai_provider: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub ai_provider: Option, pub exists_ai_resource: bool, - pub code_completion_enabled: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub code_completion_model: Option, + pub ai_models: Vec, } async fn get_copilot_info( Extension(db): Extension, @@ -723,33 +739,26 @@ async fn get_copilot_info( ) -> JsonResult { let mut tx = db.begin().await?; let record = sqlx::query!( - "SELECT ai_resource, code_completion_enabled FROM workspace_settings WHERE workspace_id = $1", + "SELECT ai_resource, code_completion_model, ai_models FROM workspace_settings WHERE workspace_id = $1", &w_id ) .fetch_one(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("getting ai_resource and code_completion_enabled: {e:#}")))?; + .map_err(|e| Error::InternalErr(format!("getting ai_resource and code_completion_model: {e:#}")))?; tx.commit().await?; let (ai_provider, exists_ai_resource) = if let Some(ai_resource) = record.ai_resource { - let ai_resource = serde_json::from_value::(ai_resource); - let exist = ai_resource.is_ok(); - ( - if exist { - ai_resource.unwrap().provider - } else { - "".to_string() - }, - exist, - ) + let ai_resource = serde_json::from_value::(ai_resource)?; + (Some(ai_resource.provider), true) } else { - ("".to_string(), false) + (None, false) }; Ok(Json(CopilotInfo { ai_provider, exists_ai_resource, - code_completion_enabled: record.code_completion_enabled, + code_completion_model: record.code_completion_model, + ai_models: record.ai_models, })) } diff --git a/backend/windmill-api/src/workspaces_export.rs b/backend/windmill-api/src/workspaces_export.rs index 0a417ff916..8e7783d8c0 100644 --- a/backend/windmill-api/src/workspaces_export.rs +++ b/backend/windmill-api/src/workspaces_export.rs @@ -241,7 +241,9 @@ struct SimplifiedSettings { error_handler_extra_args: Option, error_handler_muted_on_cancel: bool, ai_resource: Option, - code_completion_enabled: bool, + ai_models: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + code_completion_model: Option, large_file_storage: Option, git_sync: Option, default_app: Option, @@ -617,7 +619,8 @@ pub(crate) async fn tarball_workspace( deploy_to, error_handler, ai_resource, - code_completion_enabled, + ai_models, + code_completion_model, error_handler_extra_args, error_handler_muted_on_cancel, large_file_storage, diff --git a/cli/gen/core/OpenAPI.ts b/cli/gen/core/OpenAPI.ts index f9debf88b1..38bbd34aac 100644 --- a/cli/gen/core/OpenAPI.ts +++ b/cli/gen/core/OpenAPI.ts @@ -54,7 +54,7 @@ export const OpenAPI: OpenAPIConfig = { PASSWORD: undefined, TOKEN: getEnv("WM_TOKEN"), USERNAME: undefined, - VERSION: '1.447.5', + VERSION: '1.454.1', WITH_CREDENTIALS: true, interceptors: { request: new Interceptors(), diff --git a/cli/gen/services.gen.ts b/cli/gen/services.gen.ts index 49b5e1482c..66481a2857 100644 --- a/cli/gen/services.gen.ts +++ b/cli/gen/services.gen.ts @@ -3,7 +3,7 @@ import type { CancelablePromise } from './core/CancelablePromise.ts'; import { OpenAPI } from './core/OpenAPI.ts'; import { request as __request } from './core/request.ts'; -import type { BackendVersionResponse, BackendUptodateResponse, GetLicenseIdResponse, GetOpenApiYamlResponse, GetAuditLogData, GetAuditLogResponse, ListAuditLogsData, ListAuditLogsResponse, LoginData, LoginResponse, LogoutResponse, GetUserData, GetUserResponse, UpdateUserData, UpdateUserResponse, IsOwnerOfPathData, IsOwnerOfPathResponse, SetPasswordData, SetPasswordResponse, SetPasswordForUserData, SetPasswordForUserResponse, SetLoginTypeForUserData, SetLoginTypeForUserResponse, CreateUserGloballyData, CreateUserGloballyResponse, GlobalUserUpdateData, GlobalUserUpdateResponse, GlobalUsernameInfoData, GlobalUsernameInfoResponse, GlobalUserRenameData, GlobalUserRenameResponse, GlobalUserDeleteData, GlobalUserDeleteResponse, GlobalUsersOverwriteData, GlobalUsersOverwriteResponse, GlobalUsersExportResponse, DeleteUserData, DeleteUserResponse, ListWorkspacesResponse, IsDomainAllowedResponse, ListUserWorkspacesResponse, ListWorkspacesAsSuperAdminData, ListWorkspacesAsSuperAdminResponse, CreateWorkspaceData, CreateWorkspaceResponse, ExistsWorkspaceData, ExistsWorkspaceResponse, ExistsUsernameData, ExistsUsernameResponse, GetGlobalData, GetGlobalResponse, SetGlobalData, SetGlobalResponse, GetLocalResponse, TestSmtpData, TestSmtpResponse, TestCriticalChannelsData, TestCriticalChannelsResponse, GetCriticalAlertsData, GetCriticalAlertsResponse, AcknowledgeCriticalAlertData, AcknowledgeCriticalAlertResponse, AcknowledgeAllCriticalAlertsResponse, TestLicenseKeyData, TestLicenseKeyResponse, TestObjectStorageConfigData, TestObjectStorageConfigResponse, SendStatsResponse, GetLatestKeyRenewalAttemptResponse, RenewLicenseKeyData, RenewLicenseKeyResponse, CreateCustomerPortalSessionData, CreateCustomerPortalSessionResponse, TestMetadataData, TestMetadataResponse, ListGlobalSettingsResponse, GetCurrentEmailResponse, RefreshUserTokenData, RefreshUserTokenResponse, GetTutorialProgressResponse, UpdateTutorialProgressData, UpdateTutorialProgressResponse, LeaveInstanceResponse, GetUsageResponse, GetRunnableResponse, GlobalWhoamiResponse, ListWorkspaceInvitesResponse, WhoamiData, WhoamiResponse, AcceptInviteData, AcceptInviteResponse, DeclineInviteData, DeclineInviteResponse, InviteUserData, InviteUserResponse, AddUserData, AddUserResponse, DeleteInviteData, DeleteInviteResponse, ArchiveWorkspaceData, ArchiveWorkspaceResponse, UnarchiveWorkspaceData, UnarchiveWorkspaceResponse, DeleteWorkspaceData, DeleteWorkspaceResponse, LeaveWorkspaceData, LeaveWorkspaceResponse, GetWorkspaceNameData, GetWorkspaceNameResponse, ChangeWorkspaceNameData, ChangeWorkspaceNameResponse, ChangeWorkspaceIdData, ChangeWorkspaceIdResponse, ChangeWorkspaceColorData, ChangeWorkspaceColorResponse, WhoisData, WhoisResponse, ExistsEmailData, ExistsEmailResponse, ListUsersAsSuperAdminData, ListUsersAsSuperAdminResponse, ListPendingInvitesData, ListPendingInvitesResponse, GetSettingsData, GetSettingsResponse, GetDeployToData, GetDeployToResponse, GetIsPremiumData, GetIsPremiumResponse, GetPremiumInfoData, GetPremiumInfoResponse, SetAutomaticBillingData, SetAutomaticBillingResponse, GetThresholdAlertData, GetThresholdAlertResponse, SetThresholdAlertData, SetThresholdAlertResponse, EditSlackCommandData, EditSlackCommandResponse, RunSlackMessageTestJobData, RunSlackMessageTestJobResponse, EditDeployToData, EditDeployToResponse, EditAutoInviteData, EditAutoInviteResponse, EditWebhookData, EditWebhookResponse, EditCopilotConfigData, EditCopilotConfigResponse, GetCopilotInfoData, GetCopilotInfoResponse, EditErrorHandlerData, EditErrorHandlerResponse, EditLargeFileStorageConfigData, EditLargeFileStorageConfigResponse, EditWorkspaceGitSyncConfigData, EditWorkspaceGitSyncConfigResponse, EditWorkspaceDeployUiSettingsData, EditWorkspaceDeployUiSettingsResponse, EditWorkspaceDefaultAppData, EditWorkspaceDefaultAppResponse, EditDefaultScriptsData, EditDefaultScriptsResponse, GetDefaultScriptsData, GetDefaultScriptsResponse, SetEnvironmentVariableData, SetEnvironmentVariableResponse, GetWorkspaceEncryptionKeyData, GetWorkspaceEncryptionKeyResponse, SetWorkspaceEncryptionKeyData, SetWorkspaceEncryptionKeyResponse, GetWorkspaceDefaultAppData, GetWorkspaceDefaultAppResponse, GetLargeFileStorageConfigData, GetLargeFileStorageConfigResponse, GetWorkspaceUsageData, GetWorkspaceUsageResponse, GetUsedTriggersData, GetUsedTriggersResponse, ListUsersData, ListUsersResponse, ListUsersUsageData, ListUsersUsageResponse, ListUsernamesData, ListUsernamesResponse, UsernameToEmailData, UsernameToEmailResponse, CreateTokenData, CreateTokenResponse, CreateTokenImpersonateData, CreateTokenImpersonateResponse, DeleteTokenData, DeleteTokenResponse, ListTokensData, ListTokensResponse, GetOidcTokenData, GetOidcTokenResponse, CreateVariableData, CreateVariableResponse, EncryptValueData, EncryptValueResponse, DeleteVariableData, DeleteVariableResponse, UpdateVariableData, UpdateVariableResponse, GetVariableData, GetVariableResponse, GetVariableValueData, GetVariableValueResponse, ExistsVariableData, ExistsVariableResponse, ListVariableData, ListVariableResponse, ListContextualVariablesData, ListContextualVariablesResponse, WorkspaceGetCriticalAlertsData, WorkspaceGetCriticalAlertsResponse, WorkspaceAcknowledgeCriticalAlertData, WorkspaceAcknowledgeCriticalAlertResponse, WorkspaceAcknowledgeAllCriticalAlertsData, WorkspaceAcknowledgeAllCriticalAlertsResponse, WorkspaceMuteCriticalAlertsUiData, WorkspaceMuteCriticalAlertsUiResponse, LoginWithOauthData, LoginWithOauthResponse, ConnectSlackCallbackData, ConnectSlackCallbackResponse, ConnectSlackCallbackInstanceData, ConnectSlackCallbackInstanceResponse, ConnectCallbackData, ConnectCallbackResponse, CreateAccountData, CreateAccountResponse, RefreshTokenData, RefreshTokenResponse, DisconnectAccountData, DisconnectAccountResponse, DisconnectSlackData, DisconnectSlackResponse, ListOauthLoginsResponse, ListOauthConnectsResponse, GetOauthConnectData, GetOauthConnectResponse, CreateResourceData, CreateResourceResponse, DeleteResourceData, DeleteResourceResponse, UpdateResourceData, UpdateResourceResponse, UpdateResourceValueData, UpdateResourceValueResponse, GetResourceData, GetResourceResponse, GetResourceValueInterpolatedData, GetResourceValueInterpolatedResponse, GetResourceValueData, GetResourceValueResponse, ExistsResourceData, ExistsResourceResponse, ListResourceData, ListResourceResponse, ListSearchResourceData, ListSearchResourceResponse, ListResourceNamesData, ListResourceNamesResponse, CreateResourceTypeData, CreateResourceTypeResponse, FileResourceTypeToFileExtMapData, FileResourceTypeToFileExtMapResponse, DeleteResourceTypeData, DeleteResourceTypeResponse, UpdateResourceTypeData, UpdateResourceTypeResponse, GetResourceTypeData, GetResourceTypeResponse, ExistsResourceTypeData, ExistsResourceTypeResponse, ListResourceTypeData, ListResourceTypeResponse, ListResourceTypeNamesData, ListResourceTypeNamesResponse, QueryResourceTypesData, QueryResourceTypesResponse, ListHubIntegrationsData, ListHubIntegrationsResponse, ListHubFlowsResponse, GetHubFlowByIdData, GetHubFlowByIdResponse, ListHubAppsResponse, GetHubAppByIdData, GetHubAppByIdResponse, GetPublicAppByCustomPathData, GetPublicAppByCustomPathResponse, GetHubScriptContentByPathData, GetHubScriptContentByPathResponse, GetHubScriptByPathData, GetHubScriptByPathResponse, GetTopHubScriptsData, GetTopHubScriptsResponse, QueryHubScriptsData, QueryHubScriptsResponse, ListSearchScriptData, ListSearchScriptResponse, ListScriptsData, ListScriptsResponse, ListScriptPathsData, ListScriptPathsResponse, CreateDraftData, CreateDraftResponse, DeleteDraftData, DeleteDraftResponse, CreateScriptData, CreateScriptResponse, ToggleWorkspaceErrorHandlerForScriptData, ToggleWorkspaceErrorHandlerForScriptResponse, GetCustomTagsData, GetCustomTagsResponse, GeDefaultTagsResponse, IsDefaultTagsPerWorkspaceResponse, ArchiveScriptByPathData, ArchiveScriptByPathResponse, ArchiveScriptByHashData, ArchiveScriptByHashResponse, DeleteScriptByHashData, DeleteScriptByHashResponse, DeleteScriptByPathData, DeleteScriptByPathResponse, GetScriptByPathData, GetScriptByPathResponse, GetTriggersCountOfScriptData, GetTriggersCountOfScriptResponse, ListTokensOfScriptData, ListTokensOfScriptResponse, GetScriptByPathWithDraftData, GetScriptByPathWithDraftResponse, GetScriptHistoryByPathData, GetScriptHistoryByPathResponse, GetScriptLatestVersionData, GetScriptLatestVersionResponse, UpdateScriptHistoryData, UpdateScriptHistoryResponse, RawScriptByPathData, RawScriptByPathResponse, RawScriptByPathTokenedData, RawScriptByPathTokenedResponse, ExistsScriptByPathData, ExistsScriptByPathResponse, GetScriptByHashData, GetScriptByHashResponse, RawScriptByHashData, RawScriptByHashResponse, GetScriptDeploymentStatusData, GetScriptDeploymentStatusResponse, RunScriptByPathData, RunScriptByPathResponse, OpenaiSyncScriptByPathData, OpenaiSyncScriptByPathResponse, RunWaitResultScriptByPathData, RunWaitResultScriptByPathResponse, RunWaitResultScriptByPathGetData, RunWaitResultScriptByPathGetResponse, OpenaiSyncFlowByPathData, OpenaiSyncFlowByPathResponse, RunWaitResultFlowByPathData, RunWaitResultFlowByPathResponse, ResultByIdData, ResultByIdResponse, ListFlowPathsData, ListFlowPathsResponse, ListSearchFlowData, ListSearchFlowResponse, ListFlowsData, ListFlowsResponse, GetFlowHistoryData, GetFlowHistoryResponse, GetFlowLatestVersionData, GetFlowLatestVersionResponse, GetFlowVersionData, GetFlowVersionResponse, UpdateFlowHistoryData, UpdateFlowHistoryResponse, GetFlowByPathData, GetFlowByPathResponse, GetTriggersCountOfFlowData, GetTriggersCountOfFlowResponse, ListTokensOfFlowData, ListTokensOfFlowResponse, ToggleWorkspaceErrorHandlerForFlowData, ToggleWorkspaceErrorHandlerForFlowResponse, GetFlowByPathWithDraftData, GetFlowByPathWithDraftResponse, ExistsFlowByPathData, ExistsFlowByPathResponse, CreateFlowData, CreateFlowResponse, UpdateFlowData, UpdateFlowResponse, ArchiveFlowByPathData, ArchiveFlowByPathResponse, DeleteFlowByPathData, DeleteFlowByPathResponse, ListRawAppsData, ListRawAppsResponse, ExistsRawAppData, ExistsRawAppResponse, GetRawAppDataData, GetRawAppDataResponse, ListSearchAppData, ListSearchAppResponse, ListAppsData, ListAppsResponse, CreateAppData, CreateAppResponse, ExistsAppData, ExistsAppResponse, GetAppByPathData, GetAppByPathResponse, GetAppLiteByPathData, GetAppLiteByPathResponse, GetAppByPathWithDraftData, GetAppByPathWithDraftResponse, GetAppHistoryByPathData, GetAppHistoryByPathResponse, GetAppLatestVersionData, GetAppLatestVersionResponse, UpdateAppHistoryData, UpdateAppHistoryResponse, GetPublicAppBySecretData, GetPublicAppBySecretResponse, GetPublicResourceData, GetPublicResourceResponse, GetPublicSecretOfAppData, GetPublicSecretOfAppResponse, GetAppByVersionData, GetAppByVersionResponse, CreateRawAppData, CreateRawAppResponse, UpdateRawAppData, UpdateRawAppResponse, DeleteRawAppData, DeleteRawAppResponse, DeleteAppData, DeleteAppResponse, UpdateAppData, UpdateAppResponse, CustomPathExistsData, CustomPathExistsResponse, ExecuteComponentData, ExecuteComponentResponse, RunFlowByPathData, RunFlowByPathResponse, RestartFlowAtStepData, RestartFlowAtStepResponse, RunScriptByHashData, RunScriptByHashResponse, RunScriptPreviewData, RunScriptPreviewResponse, RunCodeWorkflowTaskData, RunCodeWorkflowTaskResponse, RunRawScriptDependenciesData, RunRawScriptDependenciesResponse, RunFlowPreviewData, RunFlowPreviewResponse, ListQueueData, ListQueueResponse, GetQueueCountData, GetQueueCountResponse, GetCompletedCountData, GetCompletedCountResponse, CountCompletedJobsData, CountCompletedJobsResponse, ListFilteredUuidsData, ListFilteredUuidsResponse, CancelSelectionData, CancelSelectionResponse, ListCompletedJobsData, ListCompletedJobsResponse, ListJobsData, ListJobsResponse, GetDbClockResponse, CountJobsByTagData, CountJobsByTagResponse, GetJobData, GetJobResponse, GetRootJobIdData, GetRootJobIdResponse, GetJobLogsData, GetJobLogsResponse, GetJobArgsData, GetJobArgsResponse, GetJobUpdatesData, GetJobUpdatesResponse, GetLogFileFromStoreData, GetLogFileFromStoreResponse, GetFlowDebugInfoData, GetFlowDebugInfoResponse, GetCompletedJobData, GetCompletedJobResponse, GetCompletedJobResultData, GetCompletedJobResultResponse, GetCompletedJobResultMaybeData, GetCompletedJobResultMaybeResponse, DeleteCompletedJobData, DeleteCompletedJobResponse, CancelQueuedJobData, CancelQueuedJobResponse, CancelPersistentQueuedJobsData, CancelPersistentQueuedJobsResponse, ForceCancelQueuedJobData, ForceCancelQueuedJobResponse, CreateJobSignatureData, CreateJobSignatureResponse, GetResumeUrlsData, GetResumeUrlsResponse, GetSlackApprovalPayloadData, GetSlackApprovalPayloadResponse, ResumeSuspendedJobGetData, ResumeSuspendedJobGetResponse, ResumeSuspendedJobPostData, ResumeSuspendedJobPostResponse, SetFlowUserStateData, SetFlowUserStateResponse, GetFlowUserStateData, GetFlowUserStateResponse, ResumeSuspendedFlowAsOwnerData, ResumeSuspendedFlowAsOwnerResponse, CancelSuspendedJobGetData, CancelSuspendedJobGetResponse, CancelSuspendedJobPostData, CancelSuspendedJobPostResponse, GetSuspendedJobFlowData, GetSuspendedJobFlowResponse, PreviewScheduleData, PreviewScheduleResponse, CreateScheduleData, CreateScheduleResponse, UpdateScheduleData, UpdateScheduleResponse, SetScheduleEnabledData, SetScheduleEnabledResponse, DeleteScheduleData, DeleteScheduleResponse, GetScheduleData, GetScheduleResponse, ExistsScheduleData, ExistsScheduleResponse, ListSchedulesData, ListSchedulesResponse, ListSchedulesWithJobsData, ListSchedulesWithJobsResponse, SetDefaultErrorOrRecoveryHandlerData, SetDefaultErrorOrRecoveryHandlerResponse, CreateHttpTriggerData, CreateHttpTriggerResponse, UpdateHttpTriggerData, UpdateHttpTriggerResponse, DeleteHttpTriggerData, DeleteHttpTriggerResponse, GetHttpTriggerData, GetHttpTriggerResponse, ListHttpTriggersData, ListHttpTriggersResponse, ExistsHttpTriggerData, ExistsHttpTriggerResponse, ExistsRouteData, ExistsRouteResponse, CreateWebsocketTriggerData, CreateWebsocketTriggerResponse, UpdateWebsocketTriggerData, UpdateWebsocketTriggerResponse, DeleteWebsocketTriggerData, DeleteWebsocketTriggerResponse, GetWebsocketTriggerData, GetWebsocketTriggerResponse, ListWebsocketTriggersData, ListWebsocketTriggersResponse, ExistsWebsocketTriggerData, ExistsWebsocketTriggerResponse, SetWebsocketTriggerEnabledData, SetWebsocketTriggerEnabledResponse, CreateKafkaTriggerData, CreateKafkaTriggerResponse, UpdateKafkaTriggerData, UpdateKafkaTriggerResponse, DeleteKafkaTriggerData, DeleteKafkaTriggerResponse, GetKafkaTriggerData, GetKafkaTriggerResponse, ListKafkaTriggersData, ListKafkaTriggersResponse, ExistsKafkaTriggerData, ExistsKafkaTriggerResponse, SetKafkaTriggerEnabledData, SetKafkaTriggerEnabledResponse, CreateNatsTriggerData, CreateNatsTriggerResponse, UpdateNatsTriggerData, UpdateNatsTriggerResponse, DeleteNatsTriggerData, DeleteNatsTriggerResponse, GetNatsTriggerData, GetNatsTriggerResponse, ListNatsTriggersData, ListNatsTriggersResponse, ExistsNatsTriggerData, ExistsNatsTriggerResponse, SetNatsTriggerEnabledData, SetNatsTriggerEnabledResponse, ListInstanceGroupsResponse, GetInstanceGroupData, GetInstanceGroupResponse, CreateInstanceGroupData, CreateInstanceGroupResponse, UpdateInstanceGroupData, UpdateInstanceGroupResponse, DeleteInstanceGroupData, DeleteInstanceGroupResponse, AddUserToInstanceGroupData, AddUserToInstanceGroupResponse, RemoveUserFromInstanceGroupData, RemoveUserFromInstanceGroupResponse, ExportInstanceGroupsResponse, OverwriteInstanceGroupsData, OverwriteInstanceGroupsResponse, ListGroupsData, ListGroupsResponse, ListGroupNamesData, ListGroupNamesResponse, CreateGroupData, CreateGroupResponse, UpdateGroupData, UpdateGroupResponse, DeleteGroupData, DeleteGroupResponse, GetGroupData, GetGroupResponse, AddUserToGroupData, AddUserToGroupResponse, RemoveUserToGroupData, RemoveUserToGroupResponse, ListFoldersData, ListFoldersResponse, ListFolderNamesData, ListFolderNamesResponse, CreateFolderData, CreateFolderResponse, UpdateFolderData, UpdateFolderResponse, DeleteFolderData, DeleteFolderResponse, GetFolderData, GetFolderResponse, GetFolderUsageData, GetFolderUsageResponse, AddOwnerToFolderData, AddOwnerToFolderResponse, RemoveOwnerToFolderData, RemoveOwnerToFolderResponse, ListWorkersData, ListWorkersResponse, ExistsWorkerWithTagData, ExistsWorkerWithTagResponse, GetQueueMetricsResponse, GetCountsOfJobsWaitingPerTagResponse, ListWorkerGroupsResponse, GetConfigData, GetConfigResponse, UpdateConfigData, UpdateConfigResponse, DeleteConfigData, DeleteConfigResponse, ListConfigsResponse, ListAutoscalingEventsData, ListAutoscalingEventsResponse, GetGranularAclsData, GetGranularAclsResponse, AddGranularAclsData, AddGranularAclsResponse, RemoveGranularAclsData, RemoveGranularAclsResponse, SetCaptureConfigData, SetCaptureConfigResponse, PingCaptureConfigData, PingCaptureConfigResponse, GetCaptureConfigsData, GetCaptureConfigsResponse, ListCapturesData, ListCapturesResponse, DeleteCaptureData, DeleteCaptureResponse, StarData, StarResponse, UnstarData, UnstarResponse, GetInputHistoryData, GetInputHistoryResponse, GetArgsFromHistoryOrSavedInputData, GetArgsFromHistoryOrSavedInputResponse, ListInputsData, ListInputsResponse, CreateInputData, CreateInputResponse, UpdateInputData, UpdateInputResponse, DeleteInputData, DeleteInputResponse, DuckdbConnectionSettingsData, DuckdbConnectionSettingsResponse, DuckdbConnectionSettingsV2Data, DuckdbConnectionSettingsV2Response, PolarsConnectionSettingsData, PolarsConnectionSettingsResponse, PolarsConnectionSettingsV2Data, PolarsConnectionSettingsV2Response, S3ResourceInfoData, S3ResourceInfoResponse, DatasetStorageTestConnectionData, DatasetStorageTestConnectionResponse, ListStoredFilesData, ListStoredFilesResponse, LoadFileMetadataData, LoadFileMetadataResponse, LoadFilePreviewData, LoadFilePreviewResponse, LoadParquetPreviewData, LoadParquetPreviewResponse, LoadTableRowCountData, LoadTableRowCountResponse, LoadCsvPreviewData, LoadCsvPreviewResponse, DeleteS3FileData, DeleteS3FileResponse, MoveS3FileData, MoveS3FileResponse, FileUploadData, FileUploadResponse, FileDownloadData, FileDownloadResponse, FileDownloadParquetAsCsvData, FileDownloadParquetAsCsvResponse, GetJobMetricsData, GetJobMetricsResponse, SetJobProgressData, SetJobProgressResponse, GetJobProgressData, GetJobProgressResponse, ListLogFilesData, ListLogFilesResponse, GetLogFileData, GetLogFileResponse, ListConcurrencyGroupsResponse, DeleteConcurrencyGroupData, DeleteConcurrencyGroupResponse, GetConcurrencyKeyData, GetConcurrencyKeyResponse, ListExtendedJobsData, ListExtendedJobsResponse, SearchJobsIndexData, SearchJobsIndexResponse, SearchLogsIndexData, SearchLogsIndexResponse, CountSearchLogsIndexData, CountSearchLogsIndexResponse, ClearIndexData, ClearIndexResponse } from './types.gen.ts'; +import type { BackendVersionResponse, BackendUptodateResponse, GetLicenseIdResponse, GetOpenApiYamlResponse, GetAuditLogData, GetAuditLogResponse, ListAuditLogsData, ListAuditLogsResponse, LoginData, LoginResponse, LogoutResponse, GetUserData, GetUserResponse, UpdateUserData, UpdateUserResponse, IsOwnerOfPathData, IsOwnerOfPathResponse, SetPasswordData, SetPasswordResponse, SetPasswordForUserData, SetPasswordForUserResponse, SetLoginTypeForUserData, SetLoginTypeForUserResponse, CreateUserGloballyData, CreateUserGloballyResponse, GlobalUserUpdateData, GlobalUserUpdateResponse, GlobalUsernameInfoData, GlobalUsernameInfoResponse, GlobalUserRenameData, GlobalUserRenameResponse, GlobalUserDeleteData, GlobalUserDeleteResponse, GlobalUsersOverwriteData, GlobalUsersOverwriteResponse, GlobalUsersExportResponse, DeleteUserData, DeleteUserResponse, ListWorkspacesResponse, IsDomainAllowedResponse, ListUserWorkspacesResponse, ListWorkspacesAsSuperAdminData, ListWorkspacesAsSuperAdminResponse, CreateWorkspaceData, CreateWorkspaceResponse, ExistsWorkspaceData, ExistsWorkspaceResponse, ExistsUsernameData, ExistsUsernameResponse, GetGlobalData, GetGlobalResponse, SetGlobalData, SetGlobalResponse, GetLocalResponse, TestSmtpData, TestSmtpResponse, TestCriticalChannelsData, TestCriticalChannelsResponse, GetCriticalAlertsData, GetCriticalAlertsResponse, AcknowledgeCriticalAlertData, AcknowledgeCriticalAlertResponse, AcknowledgeAllCriticalAlertsResponse, TestLicenseKeyData, TestLicenseKeyResponse, TestObjectStorageConfigData, TestObjectStorageConfigResponse, SendStatsResponse, GetLatestKeyRenewalAttemptResponse, RenewLicenseKeyData, RenewLicenseKeyResponse, CreateCustomerPortalSessionData, CreateCustomerPortalSessionResponse, TestMetadataData, TestMetadataResponse, ListGlobalSettingsResponse, GetCurrentEmailResponse, RefreshUserTokenData, RefreshUserTokenResponse, GetTutorialProgressResponse, UpdateTutorialProgressData, UpdateTutorialProgressResponse, LeaveInstanceResponse, GetUsageResponse, GetRunnableResponse, GlobalWhoamiResponse, ListWorkspaceInvitesResponse, WhoamiData, WhoamiResponse, AcceptInviteData, AcceptInviteResponse, DeclineInviteData, DeclineInviteResponse, InviteUserData, InviteUserResponse, AddUserData, AddUserResponse, DeleteInviteData, DeleteInviteResponse, ArchiveWorkspaceData, ArchiveWorkspaceResponse, UnarchiveWorkspaceData, UnarchiveWorkspaceResponse, DeleteWorkspaceData, DeleteWorkspaceResponse, LeaveWorkspaceData, LeaveWorkspaceResponse, GetWorkspaceNameData, GetWorkspaceNameResponse, ChangeWorkspaceNameData, ChangeWorkspaceNameResponse, ChangeWorkspaceIdData, ChangeWorkspaceIdResponse, ChangeWorkspaceColorData, ChangeWorkspaceColorResponse, WhoisData, WhoisResponse, UpdateOperatorSettingsData, UpdateOperatorSettingsResponse, ExistsEmailData, ExistsEmailResponse, ListUsersAsSuperAdminData, ListUsersAsSuperAdminResponse, ListPendingInvitesData, ListPendingInvitesResponse, GetSettingsData, GetSettingsResponse, GetDeployToData, GetDeployToResponse, GetIsPremiumData, GetIsPremiumResponse, GetPremiumInfoData, GetPremiumInfoResponse, SetAutomaticBillingData, SetAutomaticBillingResponse, GetThresholdAlertData, GetThresholdAlertResponse, SetThresholdAlertData, SetThresholdAlertResponse, EditSlackCommandData, EditSlackCommandResponse, RunSlackMessageTestJobData, RunSlackMessageTestJobResponse, EditDeployToData, EditDeployToResponse, EditAutoInviteData, EditAutoInviteResponse, EditWebhookData, EditWebhookResponse, EditCopilotConfigData, EditCopilotConfigResponse, GetCopilotInfoData, GetCopilotInfoResponse, EditErrorHandlerData, EditErrorHandlerResponse, EditLargeFileStorageConfigData, EditLargeFileStorageConfigResponse, EditWorkspaceGitSyncConfigData, EditWorkspaceGitSyncConfigResponse, EditWorkspaceDeployUiSettingsData, EditWorkspaceDeployUiSettingsResponse, EditWorkspaceDefaultAppData, EditWorkspaceDefaultAppResponse, EditDefaultScriptsData, EditDefaultScriptsResponse, GetDefaultScriptsData, GetDefaultScriptsResponse, SetEnvironmentVariableData, SetEnvironmentVariableResponse, GetWorkspaceEncryptionKeyData, GetWorkspaceEncryptionKeyResponse, SetWorkspaceEncryptionKeyData, SetWorkspaceEncryptionKeyResponse, GetWorkspaceDefaultAppData, GetWorkspaceDefaultAppResponse, GetLargeFileStorageConfigData, GetLargeFileStorageConfigResponse, GetWorkspaceUsageData, GetWorkspaceUsageResponse, GetUsedTriggersData, GetUsedTriggersResponse, ListUsersData, ListUsersResponse, ListUsersUsageData, ListUsersUsageResponse, ListUsernamesData, ListUsernamesResponse, UsernameToEmailData, UsernameToEmailResponse, CreateTokenData, CreateTokenResponse, CreateTokenImpersonateData, CreateTokenImpersonateResponse, DeleteTokenData, DeleteTokenResponse, ListTokensData, ListTokensResponse, GetOidcTokenData, GetOidcTokenResponse, CreateVariableData, CreateVariableResponse, EncryptValueData, EncryptValueResponse, DeleteVariableData, DeleteVariableResponse, UpdateVariableData, UpdateVariableResponse, GetVariableData, GetVariableResponse, GetVariableValueData, GetVariableValueResponse, ExistsVariableData, ExistsVariableResponse, ListVariableData, ListVariableResponse, ListContextualVariablesData, ListContextualVariablesResponse, WorkspaceGetCriticalAlertsData, WorkspaceGetCriticalAlertsResponse, WorkspaceAcknowledgeCriticalAlertData, WorkspaceAcknowledgeCriticalAlertResponse, WorkspaceAcknowledgeAllCriticalAlertsData, WorkspaceAcknowledgeAllCriticalAlertsResponse, WorkspaceMuteCriticalAlertsUiData, WorkspaceMuteCriticalAlertsUiResponse, LoginWithOauthData, LoginWithOauthResponse, ConnectSlackCallbackData, ConnectSlackCallbackResponse, ConnectSlackCallbackInstanceData, ConnectSlackCallbackInstanceResponse, ConnectCallbackData, ConnectCallbackResponse, CreateAccountData, CreateAccountResponse, RefreshTokenData, RefreshTokenResponse, DisconnectAccountData, DisconnectAccountResponse, DisconnectSlackData, DisconnectSlackResponse, ListOauthLoginsResponse, ListOauthConnectsResponse, GetOauthConnectData, GetOauthConnectResponse, SyncTeamsResponse, CreateResourceData, CreateResourceResponse, DeleteResourceData, DeleteResourceResponse, UpdateResourceData, UpdateResourceResponse, UpdateResourceValueData, UpdateResourceValueResponse, GetResourceData, GetResourceResponse, GetResourceValueInterpolatedData, GetResourceValueInterpolatedResponse, GetResourceValueData, GetResourceValueResponse, ExistsResourceData, ExistsResourceResponse, ListResourceData, ListResourceResponse, ListSearchResourceData, ListSearchResourceResponse, ListResourceNamesData, ListResourceNamesResponse, CreateResourceTypeData, CreateResourceTypeResponse, FileResourceTypeToFileExtMapData, FileResourceTypeToFileExtMapResponse, DeleteResourceTypeData, DeleteResourceTypeResponse, UpdateResourceTypeData, UpdateResourceTypeResponse, GetResourceTypeData, GetResourceTypeResponse, ExistsResourceTypeData, ExistsResourceTypeResponse, ListResourceTypeData, ListResourceTypeResponse, ListResourceTypeNamesData, ListResourceTypeNamesResponse, QueryResourceTypesData, QueryResourceTypesResponse, ListHubIntegrationsData, ListHubIntegrationsResponse, ListHubFlowsResponse, GetHubFlowByIdData, GetHubFlowByIdResponse, ListHubAppsResponse, GetHubAppByIdData, GetHubAppByIdResponse, GetPublicAppByCustomPathData, GetPublicAppByCustomPathResponse, GetHubScriptContentByPathData, GetHubScriptContentByPathResponse, GetHubScriptByPathData, GetHubScriptByPathResponse, GetTopHubScriptsData, GetTopHubScriptsResponse, QueryHubScriptsData, QueryHubScriptsResponse, ListSearchScriptData, ListSearchScriptResponse, ListScriptsData, ListScriptsResponse, ListScriptPathsData, ListScriptPathsResponse, CreateDraftData, CreateDraftResponse, DeleteDraftData, DeleteDraftResponse, CreateScriptData, CreateScriptResponse, ToggleWorkspaceErrorHandlerForScriptData, ToggleWorkspaceErrorHandlerForScriptResponse, GetCustomTagsData, GetCustomTagsResponse, GeDefaultTagsResponse, IsDefaultTagsPerWorkspaceResponse, ArchiveScriptByPathData, ArchiveScriptByPathResponse, ArchiveScriptByHashData, ArchiveScriptByHashResponse, DeleteScriptByHashData, DeleteScriptByHashResponse, DeleteScriptByPathData, DeleteScriptByPathResponse, GetScriptByPathData, GetScriptByPathResponse, GetTriggersCountOfScriptData, GetTriggersCountOfScriptResponse, ListTokensOfScriptData, ListTokensOfScriptResponse, GetScriptByPathWithDraftData, GetScriptByPathWithDraftResponse, GetScriptHistoryByPathData, GetScriptHistoryByPathResponse, GetScriptLatestVersionData, GetScriptLatestVersionResponse, UpdateScriptHistoryData, UpdateScriptHistoryResponse, RawScriptByPathData, RawScriptByPathResponse, RawScriptByPathTokenedData, RawScriptByPathTokenedResponse, ExistsScriptByPathData, ExistsScriptByPathResponse, GetScriptByHashData, GetScriptByHashResponse, RawScriptByHashData, RawScriptByHashResponse, GetScriptDeploymentStatusData, GetScriptDeploymentStatusResponse, RunScriptByPathData, RunScriptByPathResponse, OpenaiSyncScriptByPathData, OpenaiSyncScriptByPathResponse, RunWaitResultScriptByPathData, RunWaitResultScriptByPathResponse, RunWaitResultScriptByPathGetData, RunWaitResultScriptByPathGetResponse, OpenaiSyncFlowByPathData, OpenaiSyncFlowByPathResponse, RunWaitResultFlowByPathData, RunWaitResultFlowByPathResponse, ResultByIdData, ResultByIdResponse, ListFlowPathsData, ListFlowPathsResponse, ListSearchFlowData, ListSearchFlowResponse, ListFlowsData, ListFlowsResponse, GetFlowHistoryData, GetFlowHistoryResponse, GetFlowLatestVersionData, GetFlowLatestVersionResponse, GetFlowVersionData, GetFlowVersionResponse, UpdateFlowHistoryData, UpdateFlowHistoryResponse, GetFlowByPathData, GetFlowByPathResponse, GetTriggersCountOfFlowData, GetTriggersCountOfFlowResponse, ListTokensOfFlowData, ListTokensOfFlowResponse, ToggleWorkspaceErrorHandlerForFlowData, ToggleWorkspaceErrorHandlerForFlowResponse, GetFlowByPathWithDraftData, GetFlowByPathWithDraftResponse, ExistsFlowByPathData, ExistsFlowByPathResponse, CreateFlowData, CreateFlowResponse, UpdateFlowData, UpdateFlowResponse, ArchiveFlowByPathData, ArchiveFlowByPathResponse, DeleteFlowByPathData, DeleteFlowByPathResponse, ListRawAppsData, ListRawAppsResponse, ExistsRawAppData, ExistsRawAppResponse, GetRawAppDataData, GetRawAppDataResponse, ListSearchAppData, ListSearchAppResponse, ListAppsData, ListAppsResponse, CreateAppData, CreateAppResponse, ExistsAppData, ExistsAppResponse, GetAppByPathData, GetAppByPathResponse, GetAppLiteByPathData, GetAppLiteByPathResponse, GetAppByPathWithDraftData, GetAppByPathWithDraftResponse, GetAppHistoryByPathData, GetAppHistoryByPathResponse, GetAppLatestVersionData, GetAppLatestVersionResponse, UpdateAppHistoryData, UpdateAppHistoryResponse, GetPublicAppBySecretData, GetPublicAppBySecretResponse, GetPublicResourceData, GetPublicResourceResponse, GetPublicSecretOfAppData, GetPublicSecretOfAppResponse, GetAppByVersionData, GetAppByVersionResponse, CreateRawAppData, CreateRawAppResponse, UpdateRawAppData, UpdateRawAppResponse, DeleteRawAppData, DeleteRawAppResponse, DeleteAppData, DeleteAppResponse, UpdateAppData, UpdateAppResponse, CustomPathExistsData, CustomPathExistsResponse, ExecuteComponentData, ExecuteComponentResponse, RunFlowByPathData, RunFlowByPathResponse, RestartFlowAtStepData, RestartFlowAtStepResponse, RunScriptByHashData, RunScriptByHashResponse, RunScriptPreviewData, RunScriptPreviewResponse, RunCodeWorkflowTaskData, RunCodeWorkflowTaskResponse, RunRawScriptDependenciesData, RunRawScriptDependenciesResponse, RunFlowPreviewData, RunFlowPreviewResponse, ListQueueData, ListQueueResponse, GetQueueCountData, GetQueueCountResponse, GetCompletedCountData, GetCompletedCountResponse, CountCompletedJobsData, CountCompletedJobsResponse, ListFilteredUuidsData, ListFilteredUuidsResponse, CancelSelectionData, CancelSelectionResponse, ListCompletedJobsData, ListCompletedJobsResponse, ListJobsData, ListJobsResponse, GetDbClockResponse, CountJobsByTagData, CountJobsByTagResponse, GetJobData, GetJobResponse, GetRootJobIdData, GetRootJobIdResponse, GetJobLogsData, GetJobLogsResponse, GetJobArgsData, GetJobArgsResponse, GetJobUpdatesData, GetJobUpdatesResponse, GetLogFileFromStoreData, GetLogFileFromStoreResponse, GetFlowDebugInfoData, GetFlowDebugInfoResponse, GetCompletedJobData, GetCompletedJobResponse, GetCompletedJobResultData, GetCompletedJobResultResponse, GetCompletedJobResultMaybeData, GetCompletedJobResultMaybeResponse, DeleteCompletedJobData, DeleteCompletedJobResponse, CancelQueuedJobData, CancelQueuedJobResponse, CancelPersistentQueuedJobsData, CancelPersistentQueuedJobsResponse, ForceCancelQueuedJobData, ForceCancelQueuedJobResponse, CreateJobSignatureData, CreateJobSignatureResponse, GetResumeUrlsData, GetResumeUrlsResponse, GetSlackApprovalPayloadData, GetSlackApprovalPayloadResponse, ResumeSuspendedJobGetData, ResumeSuspendedJobGetResponse, ResumeSuspendedJobPostData, ResumeSuspendedJobPostResponse, SetFlowUserStateData, SetFlowUserStateResponse, GetFlowUserStateData, GetFlowUserStateResponse, ResumeSuspendedFlowAsOwnerData, ResumeSuspendedFlowAsOwnerResponse, CancelSuspendedJobGetData, CancelSuspendedJobGetResponse, CancelSuspendedJobPostData, CancelSuspendedJobPostResponse, GetSuspendedJobFlowData, GetSuspendedJobFlowResponse, PreviewScheduleData, PreviewScheduleResponse, CreateScheduleData, CreateScheduleResponse, UpdateScheduleData, UpdateScheduleResponse, SetScheduleEnabledData, SetScheduleEnabledResponse, DeleteScheduleData, DeleteScheduleResponse, GetScheduleData, GetScheduleResponse, ExistsScheduleData, ExistsScheduleResponse, ListSchedulesData, ListSchedulesResponse, ListSchedulesWithJobsData, ListSchedulesWithJobsResponse, SetDefaultErrorOrRecoveryHandlerData, SetDefaultErrorOrRecoveryHandlerResponse, CreateHttpTriggerData, CreateHttpTriggerResponse, UpdateHttpTriggerData, UpdateHttpTriggerResponse, DeleteHttpTriggerData, DeleteHttpTriggerResponse, GetHttpTriggerData, GetHttpTriggerResponse, ListHttpTriggersData, ListHttpTriggersResponse, ExistsHttpTriggerData, ExistsHttpTriggerResponse, ExistsRouteData, ExistsRouteResponse, CreateWebsocketTriggerData, CreateWebsocketTriggerResponse, UpdateWebsocketTriggerData, UpdateWebsocketTriggerResponse, DeleteWebsocketTriggerData, DeleteWebsocketTriggerResponse, GetWebsocketTriggerData, GetWebsocketTriggerResponse, ListWebsocketTriggersData, ListWebsocketTriggersResponse, ExistsWebsocketTriggerData, ExistsWebsocketTriggerResponse, SetWebsocketTriggerEnabledData, SetWebsocketTriggerEnabledResponse, TestWebsocketConnectionData, TestWebsocketConnectionResponse, CreateKafkaTriggerData, CreateKafkaTriggerResponse, UpdateKafkaTriggerData, UpdateKafkaTriggerResponse, DeleteKafkaTriggerData, DeleteKafkaTriggerResponse, GetKafkaTriggerData, GetKafkaTriggerResponse, ListKafkaTriggersData, ListKafkaTriggersResponse, ExistsKafkaTriggerData, ExistsKafkaTriggerResponse, SetKafkaTriggerEnabledData, SetKafkaTriggerEnabledResponse, TestKafkaConnectionData, TestKafkaConnectionResponse, CreateNatsTriggerData, CreateNatsTriggerResponse, UpdateNatsTriggerData, UpdateNatsTriggerResponse, DeleteNatsTriggerData, DeleteNatsTriggerResponse, GetNatsTriggerData, GetNatsTriggerResponse, ListNatsTriggersData, ListNatsTriggersResponse, ExistsNatsTriggerData, ExistsNatsTriggerResponse, SetNatsTriggerEnabledData, SetNatsTriggerEnabledResponse, TestNatsConnectionData, TestNatsConnectionResponse, IsValidPostgresConfigurationData, IsValidPostgresConfigurationResponse, CreateTemplateScriptData, CreateTemplateScriptResponse, GetTemplateScriptData, GetTemplateScriptResponse, ListPostgresReplicationSlotData, ListPostgresReplicationSlotResponse, CreatePostgresReplicationSlotData, CreatePostgresReplicationSlotResponse, DeletePostgresReplicationSlotData, DeletePostgresReplicationSlotResponse, ListPostgresPublicationData, ListPostgresPublicationResponse, GetPostgresPublicationData, GetPostgresPublicationResponse, CreatePostgresPublicationData, CreatePostgresPublicationResponse, UpdatePostgresPublicationData, UpdatePostgresPublicationResponse, DeletePostgresPublicationData, DeletePostgresPublicationResponse, CreatePostgresTriggerData, CreatePostgresTriggerResponse, UpdatePostgresTriggerData, UpdatePostgresTriggerResponse, DeletePostgresTriggerData, DeletePostgresTriggerResponse, GetPostgresTriggerData, GetPostgresTriggerResponse, ListPostgresTriggersData, ListPostgresTriggersResponse, ExistsPostgresTriggerData, ExistsPostgresTriggerResponse, SetPostgresTriggerEnabledData, SetPostgresTriggerEnabledResponse, ListInstanceGroupsResponse, GetInstanceGroupData, GetInstanceGroupResponse, CreateInstanceGroupData, CreateInstanceGroupResponse, UpdateInstanceGroupData, UpdateInstanceGroupResponse, DeleteInstanceGroupData, DeleteInstanceGroupResponse, AddUserToInstanceGroupData, AddUserToInstanceGroupResponse, RemoveUserFromInstanceGroupData, RemoveUserFromInstanceGroupResponse, ExportInstanceGroupsResponse, OverwriteInstanceGroupsData, OverwriteInstanceGroupsResponse, ListGroupsData, ListGroupsResponse, ListGroupNamesData, ListGroupNamesResponse, CreateGroupData, CreateGroupResponse, UpdateGroupData, UpdateGroupResponse, DeleteGroupData, DeleteGroupResponse, GetGroupData, GetGroupResponse, AddUserToGroupData, AddUserToGroupResponse, RemoveUserToGroupData, RemoveUserToGroupResponse, ListFoldersData, ListFoldersResponse, ListFolderNamesData, ListFolderNamesResponse, CreateFolderData, CreateFolderResponse, UpdateFolderData, UpdateFolderResponse, DeleteFolderData, DeleteFolderResponse, GetFolderData, GetFolderResponse, GetFolderUsageData, GetFolderUsageResponse, AddOwnerToFolderData, AddOwnerToFolderResponse, RemoveOwnerToFolderData, RemoveOwnerToFolderResponse, ListWorkersData, ListWorkersResponse, ExistsWorkerWithTagData, ExistsWorkerWithTagResponse, GetQueueMetricsResponse, GetCountsOfJobsWaitingPerTagResponse, ListWorkerGroupsResponse, GetConfigData, GetConfigResponse, UpdateConfigData, UpdateConfigResponse, DeleteConfigData, DeleteConfigResponse, ListConfigsResponse, ListAutoscalingEventsData, ListAutoscalingEventsResponse, GetGranularAclsData, GetGranularAclsResponse, AddGranularAclsData, AddGranularAclsResponse, RemoveGranularAclsData, RemoveGranularAclsResponse, SetCaptureConfigData, SetCaptureConfigResponse, PingCaptureConfigData, PingCaptureConfigResponse, GetCaptureConfigsData, GetCaptureConfigsResponse, ListCapturesData, ListCapturesResponse, GetCaptureData, GetCaptureResponse, DeleteCaptureData, DeleteCaptureResponse, StarData, StarResponse, UnstarData, UnstarResponse, GetInputHistoryData, GetInputHistoryResponse, GetArgsFromHistoryOrSavedInputData, GetArgsFromHistoryOrSavedInputResponse, ListInputsData, ListInputsResponse, CreateInputData, CreateInputResponse, UpdateInputData, UpdateInputResponse, DeleteInputData, DeleteInputResponse, DuckdbConnectionSettingsData, DuckdbConnectionSettingsResponse, DuckdbConnectionSettingsV2Data, DuckdbConnectionSettingsV2Response, PolarsConnectionSettingsData, PolarsConnectionSettingsResponse, PolarsConnectionSettingsV2Data, PolarsConnectionSettingsV2Response, S3ResourceInfoData, S3ResourceInfoResponse, DatasetStorageTestConnectionData, DatasetStorageTestConnectionResponse, ListStoredFilesData, ListStoredFilesResponse, LoadFileMetadataData, LoadFileMetadataResponse, LoadFilePreviewData, LoadFilePreviewResponse, LoadParquetPreviewData, LoadParquetPreviewResponse, LoadTableRowCountData, LoadTableRowCountResponse, LoadCsvPreviewData, LoadCsvPreviewResponse, DeleteS3FileData, DeleteS3FileResponse, MoveS3FileData, MoveS3FileResponse, FileUploadData, FileUploadResponse, FileDownloadData, FileDownloadResponse, FileDownloadParquetAsCsvData, FileDownloadParquetAsCsvResponse, GetJobMetricsData, GetJobMetricsResponse, SetJobProgressData, SetJobProgressResponse, GetJobProgressData, GetJobProgressResponse, ListLogFilesData, ListLogFilesResponse, GetLogFileData, GetLogFileResponse, ListConcurrencyGroupsResponse, DeleteConcurrencyGroupData, DeleteConcurrencyGroupResponse, GetConcurrencyKeyData, GetConcurrencyKeyResponse, ListExtendedJobsData, ListExtendedJobsResponse, SearchJobsIndexData, SearchJobsIndexResponse, SearchLogsIndexData, SearchLogsIndexResponse, CountSearchLogsIndexData, CountSearchLogsIndexResponse, ClearIndexData, ClearIndexResponse } from './types.gen.ts'; /** * get backend version @@ -997,6 +997,25 @@ export const whois = (data: WhoisData): CancelablePromise => { re } }); }; +/** + * Update operator settings for a workspace + * Updates the operator settings for a specific workspace. Requires workspace admin privileges. + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody + * @returns string Operator settings updated successfully + * @throws ApiError + */ +export const updateOperatorSettings = (data: UpdateOperatorSettingsData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/workspaces/operator_settings', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + /** * exists email * @param data The data for the request. @@ -2081,6 +2100,16 @@ export const getOauthConnect = (data: GetOauthConnectData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/teams/sync' +}); }; + /** * create resource * @param data The data for the request. @@ -5736,6 +5765,24 @@ export const setWebsocketTriggerEnabled = (data: SetWebsocketTriggerEnabledData) mediaType: 'application/json' }); }; +/** + * test websocket connection + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody test websocket connection + * @returns string successfuly connected to websocket + * @throws ApiError + */ +export const testWebsocketConnection = (data: TestWebsocketConnectionData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/websocket_triggers/test', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + /** * create kafka trigger * @param data The data for the request. @@ -5872,6 +5919,24 @@ export const setKafkaTriggerEnabled = (data: SetKafkaTriggerEnabledData): Cancel mediaType: 'application/json' }); }; +/** + * test kafka connection + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody test kafka connection + * @returns string successfuly connected to kafka brokers + * @throws ApiError + */ +export const testKafkaConnection = (data: TestKafkaConnectionData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/kafka_triggers/test', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + /** * create nats trigger * @param data The data for the request. @@ -6008,6 +6073,368 @@ export const setNatsTriggerEnabled = (data: SetNatsTriggerEnabledData): Cancelab mediaType: 'application/json' }); }; +/** + * test NATS connection + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody test nats connection + * @returns string successfuly connected to NATS servers + * @throws ApiError + */ +export const testNatsConnection = (data: TestNatsConnectionData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/nats_triggers/test', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * check if postgres configuration is set to logical + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean boolean that indicates if postgres is set to logical level or not + * @throws ApiError + */ +export const isValidPostgresConfiguration = (data: IsValidPostgresConfigurationData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/postgres_triggers/is_valid_postgres_configuration/{path}', + path: { + workspace: data.workspace, + path: data.path + } +}); }; + +/** + * create template script + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody template script + * @returns string custom id to retrieve template script + * @throws ApiError + */ +export const createTemplateScript = (data: CreateTemplateScriptData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/postgres_triggers/create_template_script', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * get template script + * @param data The data for the request. + * @param data.workspace + * @param data.id + * @returns string template script + * @throws ApiError + */ +export const getTemplateScript = (data: GetTemplateScriptData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/postgres_triggers/get_template_script/{id}', + path: { + workspace: data.workspace, + id: data.id + } +}); }; + +/** + * list postgres replication slot + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns SlotList list postgres slot + * @throws ApiError + */ +export const listPostgresReplicationSlot = (data: ListPostgresReplicationSlotData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/postgres_triggers/slot/list/{path}', + path: { + workspace: data.workspace, + path: data.path + } +}); }; + +/** + * create replication slot for postgres + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody new slot for postgres + * @returns string slot created + * @throws ApiError + */ +export const createPostgresReplicationSlot = (data: CreatePostgresReplicationSlotData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/postgres_triggers/slot/create/{path}', + path: { + workspace: data.workspace, + path: data.path + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * delete postgres replication slot + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody replication slot of postgres + * @returns string postgres replication slot deleted + * @throws ApiError + */ +export const deletePostgresReplicationSlot = (data: DeletePostgresReplicationSlotData): CancelablePromise => { return __request(OpenAPI, { + method: 'DELETE', + url: '/w/{workspace}/postgres_triggers/slot/delete/{path}', + path: { + workspace: data.workspace, + path: data.path + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * list postgres publication + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string database publication list + * @throws ApiError + */ +export const listPostgresPublication = (data: ListPostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/postgres_triggers/publication/list/{path}', + path: { + workspace: data.workspace, + path: data.path + } +}); }; + +/** + * get postgres publication + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.publication + * @returns PublicationData postgres publication get + * @throws ApiError + */ +export const getPostgresPublication = (data: GetPostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/postgres_triggers/publication/get/{publication}/{path}', + path: { + workspace: data.workspace, + path: data.path, + publication: data.publication + } +}); }; + +/** + * create publication for postgres + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.publication + * @param data.requestBody new publication for postgres + * @returns string publication created + * @throws ApiError + */ +export const createPostgresPublication = (data: CreatePostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/postgres_triggers/publication/create/{publication}/{path}', + path: { + workspace: data.workspace, + path: data.path, + publication: data.publication + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * update publication for postgres + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.publication + * @param data.requestBody update publication for postgres + * @returns string publication updated + * @throws ApiError + */ +export const updatePostgresPublication = (data: UpdatePostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/postgres_triggers/publication/update/{publication}/{path}', + path: { + workspace: data.workspace, + path: data.path, + publication: data.publication + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * delete postgres publication + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.publication + * @returns string postgres publication deleted + * @throws ApiError + */ +export const deletePostgresPublication = (data: DeletePostgresPublicationData): CancelablePromise => { return __request(OpenAPI, { + method: 'DELETE', + url: '/w/{workspace}/postgres_triggers/publication/delete/{publication}/{path}', + path: { + workspace: data.workspace, + path: data.path, + publication: data.publication + } +}); }; + +/** + * create postgres trigger + * @param data The data for the request. + * @param data.workspace + * @param data.requestBody new postgres trigger + * @returns string postgres trigger created + * @throws ApiError + */ +export const createPostgresTrigger = (data: CreatePostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/postgres_triggers/create', + path: { + workspace: data.workspace + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * update postgres trigger + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated trigger + * @returns string postgres trigger updated + * @throws ApiError + */ +export const updatePostgresTrigger = (data: UpdatePostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/postgres_triggers/update/{path}', + path: { + workspace: data.workspace, + path: data.path + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + +/** + * delete postgres trigger + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns string postgres trigger deleted + * @throws ApiError + */ +export const deletePostgresTrigger = (data: DeletePostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'DELETE', + url: '/w/{workspace}/postgres_triggers/delete/{path}', + path: { + workspace: data.workspace, + path: data.path + } +}); }; + +/** + * get postgres trigger + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns PostgresTrigger get postgres trigger + * @throws ApiError + */ +export const getPostgresTrigger = (data: GetPostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/postgres_triggers/get/{path}', + path: { + workspace: data.workspace, + path: data.path + } +}); }; + +/** + * list postgres triggers + * @param data The data for the request. + * @param data.workspace + * @param data.page which page to return (start at 1, default 1) + * @param data.perPage number of items to return for a given page (default 30, max 100) + * @param data.path filter by path + * @param data.isFlow + * @param data.pathStart + * @returns PostgresTrigger postgres trigger list + * @throws ApiError + */ +export const listPostgresTriggers = (data: ListPostgresTriggersData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/postgres_triggers/list', + path: { + workspace: data.workspace + }, + query: { + page: data.page, + per_page: data.perPage, + path: data.path, + is_flow: data.isFlow, + path_start: data.pathStart + } +}); }; + +/** + * does postgres trigger exists + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @returns boolean postgres trigger exists + * @throws ApiError + */ +export const existsPostgresTrigger = (data: ExistsPostgresTriggerData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/postgres_triggers/exists/{path}', + path: { + workspace: data.workspace, + path: data.path + } +}); }; + +/** + * set enabled postgres trigger + * @param data The data for the request. + * @param data.workspace + * @param data.path + * @param data.requestBody updated postgres trigger enable + * @returns string postgres trigger enabled set + * @throws ApiError + */ +export const setPostgresTriggerEnabled = (data: SetPostgresTriggerEnabledData): CancelablePromise => { return __request(OpenAPI, { + method: 'POST', + url: '/w/{workspace}/postgres_triggers/setenabled/{path}', + path: { + workspace: data.workspace, + path: data.path + }, + body: data.requestBody, + mediaType: 'application/json' +}); }; + /** * list instance groups * @returns InstanceGroup instance group list @@ -6746,6 +7173,23 @@ export const listCaptures = (data: ListCapturesData): CancelablePromise => { return __request(OpenAPI, { + method: 'GET', + url: '/w/{workspace}/capture/{id}', + path: { + workspace: data.workspace, + id: data.id + } +}); }; + /** * delete a capture * @param data The data for the request. diff --git a/cli/gen/types.gen.ts b/cli/gen/types.gen.ts index ee075449a9..4695b99980 100644 --- a/cli/gen/types.gen.ts +++ b/cli/gen/types.gen.ts @@ -1,8 +1,10 @@ // This file is auto-generated by @hey-api/openapi-ts -export type AiResource = { +export type AIProvider = 'openai' | 'anthropic' | 'mistral' | 'deepseek' | 'customai'; + +export type AIResource = { path: string; - provider: string; + provider: AIProvider; }; export type Script = { @@ -30,7 +32,7 @@ export type Script = { }; lock?: string; lock_error_logs?: string; - language: 'python3' | 'deno' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'bun' | 'php' | 'rust' | 'ansible' | 'csharp'; + language: ScriptLang; kind: 'script' | 'failure' | 'trigger' | 'command' | 'approval' | 'preprocessor'; starred: boolean; tag?: string; @@ -54,8 +56,6 @@ export type Script = { on_behalf_of_email?: string; }; -export type language = 'python3' | 'deno' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'bun' | 'php' | 'rust' | 'ansible' | 'csharp'; - export type kind = 'script' | 'failure' | 'trigger' | 'command' | 'approval' | 'preprocessor'; export type NewScript = { @@ -69,7 +69,7 @@ export type NewScript = { }; is_template?: boolean; lock?: string; - language: 'python3' | 'deno' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'bun' | 'php' | 'rust' | 'ansible' | 'csharp'; + language: ScriptLang; kind?: 'script' | 'failure' | 'trigger' | 'command' | 'approval' | 'preprocessor'; tag?: string; draft_only?: boolean; @@ -159,7 +159,7 @@ export type QueuedJob = { flow_status?: FlowStatus; raw_flow?: FlowValue; is_flow_step: boolean; - language?: 'python3' | 'deno' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'bun' | 'php' | 'rust' | 'ansible' | 'csharp'; + language?: ScriptLang; email: string; visible_to_owner: boolean; mem_peak?: number; @@ -202,7 +202,7 @@ export type CompletedJob = { flow_status?: FlowStatus; raw_flow?: FlowValue; is_flow_step: boolean; - language?: 'python3' | 'deno' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'bun' | 'php' | 'rust' | 'ansible' | 'csharp'; + language?: ScriptLang; is_skipped: boolean; email: string; visible_to_owner: boolean; @@ -372,11 +372,13 @@ export type MainArgSignature = { export type type2 = 'Valid' | 'Invalid'; +export type ScriptLang = 'python3' | 'deno' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'bun' | 'php' | 'rust' | 'ansible' | 'csharp'; + export type Preview = { content?: string; path?: string; args: ScriptArgs; - language?: 'python3' | 'deno' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'bun' | 'php' | 'rust' | 'ansible' | 'csharp'; + language?: ScriptLang; tag?: string; kind?: 'code' | 'identity' | 'http'; dedicated_worker?: boolean; @@ -550,10 +552,18 @@ export type EditSchedule = { cron_version?: string; }; -export type HttpTrigger = { - path: string; +export type TriggerExtraProperty = { + email: string; + extra_perms: { + [key: string]: (boolean); + }; + workspace_id: string; edited_by: string; edited_at: string; +}; + +export type HttpTrigger = TriggerExtraProperty & { + path: string; script_path: string; route_path: string; static_asset_config?: { @@ -562,11 +572,6 @@ export type HttpTrigger = { filename?: string; }; is_flow: boolean; - extra_perms: { - [key: string]: (boolean); - }; - email: string; - workspace_id: string; http_method: 'get' | 'post' | 'put' | 'delete' | 'patch'; is_async: boolean; requires_auth: boolean; @@ -613,22 +618,16 @@ export type TriggersCount = { webhook_count?: number; email_count?: number; websocket_count?: number; + postgres_count?: number; kafka_count?: number; nats_count?: number; }; -export type WebsocketTrigger = { +export type WebsocketTrigger = TriggerExtraProperty & { path: string; - edited_by: string; - edited_at: string; script_path: string; url: string; is_flow: boolean; - extra_perms: { - [key: string]: (boolean); - }; - email: string; - workspace_id: string; server_id?: string; last_server_ping?: string; error?: string; @@ -639,6 +638,7 @@ export type WebsocketTrigger = { }>; initial_messages?: Array; url_runnable_args?: ScriptArgs; + can_return_message: boolean; }; export type NewWebsocketTrigger = { @@ -653,6 +653,7 @@ export type NewWebsocketTrigger = { }>; initial_messages?: Array; url_runnable_args?: ScriptArgs; + can_return_message: boolean; }; export type EditWebsocketTrigger = { @@ -666,6 +667,7 @@ export type EditWebsocketTrigger = { }>; initial_messages?: Array; url_runnable_args?: ScriptArgs; + can_return_message: boolean; }; export type WebsocketTriggerInitialMessage = { @@ -678,6 +680,74 @@ export type WebsocketTriggerInitialMessage = { }; }; +export type Slot = { + name?: string; +}; + +export type SlotList = { + slot_name?: string; + active?: boolean; +}; + +export type PublicationData = { + table_to_track?: Array; + transaction_to_track: Array<(string)>; +}; + +export type TableToTrack = Array<{ + table_name: string; + columns_name?: Array<(string)>; + where_clause?: string; +}>; + +export type Relations = { + schema_name: string; + table_to_track: TableToTrack; +}; + +export type Language = 'Typescript'; + +export type TemplateScript = { + postgres_resource_path: string; + relations: Array; + language: Language; +}; + +export type PostgresTrigger = TriggerExtraProperty & { + path: string; + script_path: string; + is_flow: boolean; + enabled: boolean; + postgres_resource_path: string; + publication_name: string; + server_id?: string; + replication_slot_name: string; + error?: string; + last_server_ping?: string; +}; + +export type NewPostgresTrigger = { + replication_slot_name?: string; + publication_name?: string; + path: string; + script_path: string; + is_flow: boolean; + enabled: boolean; + postgres_resource_path: string; + publication?: PublicationData; +}; + +export type EditPostgresTrigger = { + replication_slot_name: string; + publication_name: string; + path: string; + script_path: string; + is_flow: boolean; + enabled: boolean; + postgres_resource_path: string; + publication?: PublicationData; +}; + export type KafkaTrigger = { path: string; edited_by: string; @@ -817,6 +887,7 @@ export type UserWorkspaceList = { name: string; username: string; color: string; + operator_settings?: OperatorSettings; }>; }; @@ -1111,7 +1182,7 @@ export type MetricDataPoint = { export type RawScriptForDependencies = { raw_code: string; path: string; - language: 'python3' | 'deno' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'bun' | 'php' | 'rust' | 'ansible' | 'csharp'; + language: ScriptLang; }; export type ConcurrencyGroup = { @@ -1223,6 +1294,79 @@ export type CaptureConfig = { last_server_ping?: string; }; +export type OperatorSettings = { + /** + * Whether operators can view runs + */ + runs: boolean; + /** + * Whether operators can view schedules + */ + schedules: boolean; + /** + * Whether operators can view resources + */ + resources: boolean; + /** + * Whether operators can view variables + */ + variables: boolean; + /** + * Whether operators can view audit logs + */ + audit_logs: boolean; + /** + * Whether operators can view triggers + */ + triggers: boolean; + /** + * Whether operators can view groups page + */ + groups: boolean; + /** + * Whether operators can view folders page + */ + folders: boolean; + /** + * Whether operators can view workers page + */ + workers: boolean; +} | null; + +export type TeamInfo = { + /** + * The unique identifier of the Microsoft Teams team + */ + team_id: string; + /** + * The display name of the Microsoft Teams team + */ + team_name: string; + /** + * List of channels within the team + */ + channels: Array; +}; + +export type ChannelInfo = { + /** + * The unique identifier of the channel + */ + channel_id: string; + /** + * The display name of the channel + */ + channel_name: string; + /** + * The Microsoft Teams tenant identifier + */ + tenant_id: string; + /** + * The service URL for the channel + */ + service_url: string; +}; + export type OpenFlow = { summary: string; description?: string; @@ -1331,7 +1475,7 @@ export type RawScript = { is_trigger?: boolean; }; -export type language2 = 'deno' | 'bun' | 'python3' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'php'; +export type language = 'deno' | 'bun' | 'python3' | 'go' | 'bash' | 'powershell' | 'postgresql' | 'mysql' | 'bigquery' | 'snowflake' | 'mssql' | 'oracledb' | 'graphql' | 'nativets' | 'php'; export type PathScript = { input_transforms: { @@ -1441,10 +1585,14 @@ export type FlowStatusModule = { export type type4 = 'WaitingForPriorSteps' | 'WaitingForEvents' | 'WaitingForExecutor' | 'InProgress' | 'Success' | 'Failure'; +export type ParameterId = string; + export type ParameterKey = string; export type ParameterWorkspaceId = string; +export type ParameterPublicationName = string; + export type ParameterVersionId = number; export type ParameterToken = string; @@ -2239,6 +2387,13 @@ export type WhoisData = { export type WhoisResponse = (User); +export type UpdateOperatorSettingsData = { + requestBody: OperatorSettings; + workspace: string; +}; + +export type UpdateOperatorSettingsResponse = (string); + export type ExistsEmailData = { email: string; }; @@ -2285,8 +2440,9 @@ export type GetSettingsResponse = ({ customer_id?: string; webhook?: string; deploy_to?: string; - ai_resource?: AiResource; - code_completion_enabled: boolean; + ai_resource?: AIResource; + code_completion_model?: string; + ai_models: Array<(string)>; error_handler?: string; error_handler_extra_args?: ScriptArgs; error_handler_muted_on_cancel: boolean; @@ -2297,6 +2453,7 @@ export type GetSettingsResponse = ({ default_scripts?: WorkspaceDefaultScripts; mute_critical_alerts?: boolean; color?: string; + operator_settings?: OperatorSettings; }); export type GetDeployToData = { @@ -2427,8 +2584,9 @@ export type EditCopilotConfigData = { * WorkspaceCopilotConfig */ requestBody: { - ai_resource?: AiResource; - code_completion_enabled: boolean; + ai_resource?: AIResource; + code_completion_model?: string; + ai_models: Array<(string)>; }; workspace: string; }; @@ -2440,9 +2598,10 @@ export type GetCopilotInfoData = { }; export type GetCopilotInfoResponse = ({ - ai_provider: string; + ai_provider: AIProvider; exists_ai_resource: boolean; - code_completion_enabled: boolean; + code_completion_model?: string; + ai_models: Array<(string)>; }); export type EditErrorHandlerData = { @@ -2586,6 +2745,7 @@ export type GetUsedTriggersResponse = ({ websocket_used: boolean; kafka_used: boolean; nats_used: boolean; + postgres_used: boolean; }); export type ListUsersData = { @@ -2916,6 +3076,8 @@ export type GetOauthConnectResponse = ({ scopes?: Array<(string)>; }); +export type SyncTeamsResponse = (Array); + export type CreateResourceData = { /** * new resource @@ -5490,6 +5652,20 @@ export type SetWebsocketTriggerEnabledData = { export type SetWebsocketTriggerEnabledResponse = (string); +export type TestWebsocketConnectionData = { + /** + * test websocket connection + */ + requestBody: { + url: string; + url_runnable_args?: ScriptArgs; + can_return_message: boolean; + }; + workspace: string; +}; + +export type TestWebsocketConnectionResponse = (string); + export type CreateKafkaTriggerData = { /** * new kafka trigger @@ -5565,6 +5741,20 @@ export type SetKafkaTriggerEnabledData = { export type SetKafkaTriggerEnabledResponse = (string); +export type TestKafkaConnectionData = { + /** + * test kafka connection + */ + requestBody: { + connection: { + [key: string]: unknown; + }; + }; + workspace: string; +}; + +export type TestKafkaConnectionResponse = (string); + export type CreateNatsTriggerData = { /** * new nats trigger @@ -5640,6 +5830,195 @@ export type SetNatsTriggerEnabledData = { export type SetNatsTriggerEnabledResponse = (string); +export type TestNatsConnectionData = { + /** + * test nats connection + */ + requestBody: { + connection: { + [key: string]: unknown; + }; + }; + workspace: string; +}; + +export type TestNatsConnectionResponse = (string); + +export type IsValidPostgresConfigurationData = { + path: string; + workspace: string; +}; + +export type IsValidPostgresConfigurationResponse = (boolean); + +export type CreateTemplateScriptData = { + /** + * template script + */ + requestBody: TemplateScript; + workspace: string; +}; + +export type CreateTemplateScriptResponse = (string); + +export type GetTemplateScriptData = { + id: string; + workspace: string; +}; + +export type GetTemplateScriptResponse = (string); + +export type ListPostgresReplicationSlotData = { + path: string; + workspace: string; +}; + +export type ListPostgresReplicationSlotResponse = (Array); + +export type CreatePostgresReplicationSlotData = { + path: string; + /** + * new slot for postgres + */ + requestBody: Slot; + workspace: string; +}; + +export type CreatePostgresReplicationSlotResponse = (string); + +export type DeletePostgresReplicationSlotData = { + path: string; + /** + * replication slot of postgres + */ + requestBody: Slot; + workspace: string; +}; + +export type DeletePostgresReplicationSlotResponse = (string); + +export type ListPostgresPublicationData = { + path: string; + workspace: string; +}; + +export type ListPostgresPublicationResponse = (Array<(string)>); + +export type GetPostgresPublicationData = { + path: string; + publication: string; + workspace: string; +}; + +export type GetPostgresPublicationResponse = (PublicationData); + +export type CreatePostgresPublicationData = { + path: string; + publication: string; + /** + * new publication for postgres + */ + requestBody: PublicationData; + workspace: string; +}; + +export type CreatePostgresPublicationResponse = (string); + +export type UpdatePostgresPublicationData = { + path: string; + publication: string; + /** + * update publication for postgres + */ + requestBody: PublicationData; + workspace: string; +}; + +export type UpdatePostgresPublicationResponse = (string); + +export type DeletePostgresPublicationData = { + path: string; + publication: string; + workspace: string; +}; + +export type DeletePostgresPublicationResponse = (string); + +export type CreatePostgresTriggerData = { + /** + * new postgres trigger + */ + requestBody: NewPostgresTrigger; + workspace: string; +}; + +export type CreatePostgresTriggerResponse = (string); + +export type UpdatePostgresTriggerData = { + path: string; + /** + * updated trigger + */ + requestBody: EditPostgresTrigger; + workspace: string; +}; + +export type UpdatePostgresTriggerResponse = (string); + +export type DeletePostgresTriggerData = { + path: string; + workspace: string; +}; + +export type DeletePostgresTriggerResponse = (string); + +export type GetPostgresTriggerData = { + path: string; + workspace: string; +}; + +export type GetPostgresTriggerResponse = (PostgresTrigger); + +export type ListPostgresTriggersData = { + isFlow?: boolean; + /** + * which page to return (start at 1, default 1) + */ + page?: number; + /** + * filter by path + */ + path?: string; + pathStart?: string; + /** + * number of items to return for a given page (default 30, max 100) + */ + perPage?: number; + workspace: string; +}; + +export type ListPostgresTriggersResponse = (Array); + +export type ExistsPostgresTriggerData = { + path: string; + workspace: string; +}; + +export type ExistsPostgresTriggerResponse = (boolean); + +export type SetPostgresTriggerEnabledData = { + path: string; + /** + * updated postgres trigger enable + */ + requestBody: { + enabled: boolean; + }; + workspace: string; +}; + +export type SetPostgresTriggerEnabledResponse = (string); + export type ListInstanceGroupsResponse = (Array); export type GetInstanceGroupData = { @@ -5987,7 +6366,7 @@ export type ListAutoscalingEventsData = { export type ListAutoscalingEventsResponse = (Array); export type GetGranularAclsData = { - kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger'; + kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger'; path: string; workspace: string; }; @@ -5997,7 +6376,7 @@ export type GetGranularAclsResponse = ({ }); export type AddGranularAclsData = { - kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger'; + kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger'; path: string; /** * acl to add @@ -6012,7 +6391,7 @@ export type AddGranularAclsData = { export type AddGranularAclsResponse = (string); export type RemoveGranularAclsData = { - kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger'; + kind: 'script' | 'group_' | 'resource' | 'schedule' | 'variable' | 'flow' | 'folder' | 'app' | 'raw_app' | 'http_trigger' | 'websocket_trigger' | 'kafka_trigger' | 'nats_trigger' | 'postgres_trigger'; path: string; /** * acl to add @@ -6076,6 +6455,13 @@ export type ListCapturesData = { export type ListCapturesResponse = (Array); +export type GetCaptureData = { + id: number; + workspace: string; +}; + +export type GetCaptureResponse = (Capture); + export type DeleteCaptureData = { id: number; workspace: string; @@ -6200,6 +6586,7 @@ export type DuckdbConnectionSettingsV2Data = { export type DuckdbConnectionSettingsV2Response = ({ connection_settings_str: string; + azure_container_path?: string; }); export type PolarsConnectionSettingsData = { diff --git a/cli/settings.ts b/cli/settings.ts index 4952b7ac7a..1cfe0b9bf0 100644 --- a/cli/settings.ts +++ b/cli/settings.ts @@ -1,15 +1,11 @@ -import { yamlStringify } from "./deps.ts"; -import { Confirm } from "./deps.ts"; -import { colors } from "./deps.ts"; -import { yamlParseFile } from "./deps.ts"; -import { log } from "./deps.ts"; +import process from "node:process"; +import { colors, Confirm, log, yamlParseFile, yamlStringify } from "./deps.ts"; +import * as wmill from "./gen/services.gen.ts"; +import { AiResource, Config, GlobalSetting } from "./gen/types.gen.ts"; import { compareInstanceObjects, InstanceSyncOptions } from "./instance.ts"; import { isSuperset } from "./types.ts"; import { deepEqual } from "./utils.ts"; -import * as wmill from "./gen/services.gen.ts"; -import { AiResource, Config, GlobalSetting } from "./gen/types.gen.ts"; import { removeWorkerPrefix } from "./worker_groups.ts"; -import process from "node:process"; export interface SimplifiedSettings { // slack_team_id?: string; @@ -25,7 +21,8 @@ export interface SimplifiedSettings { error_handler_extra_args?: any; error_handler_muted_on_cancel?: boolean; ai_resource?: AiResource; - code_completion_enabled: boolean; + code_completion_model?: string; + ai_models: string[]; large_file_storage?: any; git_sync?: any; default_app?: string; @@ -81,7 +78,8 @@ export async function pushWorkspaceSettings( error_handler_muted_on_cancel: remoteSettings.error_handler_muted_on_cancel, ai_resource: remoteSettings.ai_resource, - code_completion_enabled: remoteSettings.code_completion_enabled, + code_completion_model: remoteSettings.code_completion_model, + ai_models: remoteSettings.ai_models, large_file_storage: remoteSettings.large_file_storage, git_sync: remoteSettings.git_sync, default_app: remoteSettings.default_app, @@ -153,15 +151,17 @@ export async function pushWorkspaceSettings( } } if ( - localSettings.ai_resource !== settings.ai_resource || - localSettings.code_completion_enabled !== settings.code_completion_enabled + localSettings.ai_resource != settings.ai_resource || + localSettings.code_completion_model != settings.code_completion_model || + !deepEqual(localSettings.ai_models, settings.ai_models) ) { - log.debug(`Updating openai settings...`); + log.debug(`Updating copilot settings...`); await wmill.editCopilotConfig({ workspace, requestBody: { ai_resource: localSettings.ai_resource, - code_completion_enabled: localSettings.code_completion_enabled, + code_completion_model: localSettings.code_completion_model, + ai_models: localSettings.ai_models, }, }); } @@ -282,7 +282,9 @@ export async function readInstanceSettings(opts: InstanceSyncOptions) { await checkInstanceSettingsPath(opts); try { - localSettings = (await yamlParseFile(instanceSettingsPath)) as GlobalSetting[]; + localSettings = (await yamlParseFile( + instanceSettingsPath + )) as GlobalSetting[]; } catch { log.warn(`No ${instanceSettingsPath} found`); } @@ -445,9 +447,7 @@ export async function pushInstanceSettings( } } -export async function readLocalConfigs( - opts: InstanceSyncOptions -) { +export async function readLocalConfigs(opts: InstanceSyncOptions) { let localConfigs: Config[] = []; await checkInstanceConfigPath(opts); diff --git a/frontend/src/lib/components/Dev.svelte b/frontend/src/lib/components/Dev.svelte index ccdda7524e..e53d49eb72 100644 --- a/frontend/src/lib/components/Dev.svelte +++ b/frontend/src/lib/components/Dev.svelte @@ -48,7 +48,7 @@ } from '$lib/relative_imports' import Tooltip from './Tooltip.svelte' import type { ScheduleTrigger, TriggerContext } from './triggers' - import { initAllAiWorkspace } from './copilot/lib' + import { workspaceAIClients } from './copilot/lib' import type { FlowPropPickerConfig, PropPickerContext } from './prop_picker' import type { PickableProperties } from './flows/previousResults' $: token = $page.url.searchParams.get('wm_token') ?? undefined @@ -110,14 +110,19 @@ async function setCopilotInfo() { if (workspace) { - initAllAiWorkspace(workspace) + workspaceAIClients.init(workspace) try { - copilotInfo.set(await WorkspaceService.getCopilotInfo({ workspace })) + const info = await WorkspaceService.getCopilotInfo({ workspace }) + copilotInfo.set({ + ...info, + ai_provider: info.ai_provider ?? 'openai' + }) } catch (err) { copilotInfo.set({ - ai_provider: '', + ai_provider: 'openai', exists_ai_resource: false, - code_completion_enabled: false + code_completion_model: undefined, + ai_models: [] }) console.error('Could not get copilot info') diff --git a/frontend/src/lib/components/Editor.svelte b/frontend/src/lib/components/Editor.svelte index 350d26b643..b5f6565362 100644 --- a/frontend/src/lib/components/Editor.svelte +++ b/frontend/src/lib/components/Editor.svelte @@ -172,7 +172,6 @@ import { initVim } from './monaco_keybindings' import { buildWorkerDefinition } from '$lib/monaco_workers/build_workers' import { parseTypescriptDeps } from '$lib/relative_imports' - import type { AiProviderTypes } from './copilot/lib' // import EditorTheme from './EditorTheme.svelte' @@ -640,7 +639,7 @@ textAfterPosition, lang, abortController, - aiProvider as AiProviderTypes + aiProvider ) if (insertText) { items = [ @@ -668,7 +667,7 @@ } $: $copilotInfo.exists_ai_resource && - $copilotInfo.code_completion_enabled && + $copilotInfo.code_completion_model && $codeCompletionSessionEnabled && initialized && addCopilotSuggestions() diff --git a/frontend/src/lib/components/FlowBuilder.svelte b/frontend/src/lib/components/FlowBuilder.svelte index 3db9804bb8..a713835829 100644 --- a/frontend/src/lib/components/FlowBuilder.svelte +++ b/frontend/src/lib/components/FlowBuilder.svelte @@ -86,7 +86,6 @@ import type { FlowBuilderWhitelabelCustomUi } from './custom_ui' import FlowYamlEditor from './flows/header/FlowYamlEditor.svelte' import { type TriggerContext, type ScheduleTrigger } from './triggers' - import type { AiProviderTypes } from './copilot/lib' export let initialPath: string = '' export let pathStoreInit: string | undefined = undefined @@ -821,7 +820,7 @@ try { push(history, $flowStore) let module = stepOnly ? $copilotModulesStore[0] : $copilotModulesStore[idx] - const aiProvider = $copilotInfo.ai_provider as AiProviderTypes + const aiProvider = $copilotInfo.ai_provider copilotLoading = true copilotStatus = "Generating code for step '" + module.id + "'..." diff --git a/frontend/src/lib/components/copilot/CodeCompletionStatus.svelte b/frontend/src/lib/components/copilot/CodeCompletionStatus.svelte index 8a77a8df9f..920f2d137e 100644 --- a/frontend/src/lib/components/copilot/CodeCompletionStatus.svelte +++ b/frontend/src/lib/components/copilot/CodeCompletionStatus.svelte @@ -12,7 +12,7 @@ } -{#if $copilotInfo.exists_ai_resource && $copilotInfo.code_completion_enabled} +{#if $copilotInfo.exists_ai_resource && $copilotInfo.code_completion_model} import { ExternalLink, Wand2 } from 'lucide-svelte' import Button from '../common/button/Button.svelte' - import { getNonStreamingCompletion, type AiProviderTypes } from './lib' + import { getNonStreamingCompletion } from './lib' import Popup from '../common/popup/Popup.svelte' import { sendUserToast } from '$lib/toast' import { copilotInfo } from '$lib/stores' import { base } from '$lib/base' import type { ChatCompletionMessageParam } from 'openai/resources/index.mjs' + import type { AIProvider } from '$lib/gen' export let schedule: string export let cronVersion: string @@ -56,7 +57,7 @@ const response = await getNonStreamingCompletion( messages, abortController, - aiProvider as AiProviderTypes + aiProvider as AIProvider ) if (response.startsWith('ERROR:')) { diff --git a/frontend/src/lib/components/copilot/IteratorGen.svelte b/frontend/src/lib/components/copilot/IteratorGen.svelte index 361a6a5c1a..a53423670b 100644 --- a/frontend/src/lib/components/copilot/IteratorGen.svelte +++ b/frontend/src/lib/components/copilot/IteratorGen.svelte @@ -1,7 +1,7 @@ @@ -21,8 +24,10 @@ setTimeout(() => { abortController.abort() }, 10000) + await testKey({ apiKey, + resourcePath, messages: [ { role: 'user', @@ -30,7 +35,8 @@ } ], abortController, - aiProvider + aiProvider, + model }) sendUserToast('Valid key') } catch (err) { @@ -42,5 +48,11 @@ } finally { loading = false } - }}>Test key + {#if apiKey} + Test key + {:else} + Test + {/if} + diff --git a/frontend/src/lib/components/copilot/completion.ts b/frontend/src/lib/components/copilot/completion.ts index b30a3fe7e0..01c90035c6 100644 --- a/frontend/src/lib/components/copilot/completion.ts +++ b/frontend/src/lib/components/copilot/completion.ts @@ -1,6 +1,7 @@ -import type { ChatCompletionMessageParam } from 'openai/resources/chat/index.mjs' -import { getNonStreamingCompletion, type AiProviderTypes } from './lib' +import type { AIProvider } from '$lib/gen' import { codeCompletionLoading } from '$lib/stores' +import type { ChatCompletionMessageParam } from 'openai/resources/chat/index.mjs' +import { getNonStreamingCompletion } from './lib' const systemPrompt = `You are a code completion assistant, return the code that should go instead of the . @@ -74,7 +75,7 @@ export async function editorCodeCompletion( after: string, lang: string, abortController: AbortController, - aiProvider: AiProviderTypes + aiProvider: AIProvider ) { codeCompletionLoading.set(true) const messages: ChatCompletionMessageParam[] = [ diff --git a/frontend/src/lib/components/copilot/flow.ts b/frontend/src/lib/components/copilot/flow.ts index 7867a2f2d5..1c5f8685bd 100644 --- a/frontend/src/lib/components/copilot/flow.ts +++ b/frontend/src/lib/components/copilot/flow.ts @@ -1,21 +1,17 @@ import { - type FlowModule, ScriptService, - type RawScript, - type PathScript, + type AIProvider, + type FlowModule, type InputTransform, + type PathScript, + type RawScript, type Script } from '$lib/gen' -import { - addResourceTypes, - deltaCodeCompletion, - getNonStreamingCompletion, - type AiProviderTypes -} from './lib' +import { scriptLangToEditorLang } from '$lib/scripts' import type { Writable } from 'svelte/store' import type Editor from '../Editor.svelte' import type { Drawer } from '../common' -import { scriptLangToEditorLang } from '$lib/scripts' +import { addResourceTypes, deltaCodeCompletion, getNonStreamingCompletion } from './lib' export type FlowCopilotModule = { id: string @@ -259,7 +255,7 @@ export async function stepCopilot( | undefined, isFirstInLoop: boolean, abortController: AbortController, - aiProvider: AiProviderTypes + aiProvider: AIProvider ) { if (module.source !== 'custom') { throw new Error('Not a custom module') @@ -328,7 +324,7 @@ export async function glueCopilot( }, isFirstInLoop: boolean, abortController: AbortController, - aiProvider: AiProviderTypes + aiProvider: AIProvider ) { const { prevCode, prevLang } = await getPreviousStepContent(pastModule, workspace) diff --git a/frontend/src/lib/components/copilot/lib.ts b/frontend/src/lib/components/copilot/lib.ts index fe7028dc1e..69d5ac9b7d 100644 --- a/frontend/src/lib/components/copilot/lib.ts +++ b/frontend/src/lib/components/copilot/lib.ts @@ -1,56 +1,130 @@ -import { OpenAI } from 'openai' -import { OpenAPI, ResourceService, type Script } from '../../gen' -import type { Writable } from 'svelte/store' +import type { AIProvider } from '$lib/gen' +import { + copilotInfo, + copilotSessionModel, + type DBSchema, + type GraphqlSchema, + type SQLSchema +} from '$lib/stores' import { Anthropic } from '@anthropic-ai/sdk' -import type { DBSchema, GraphqlSchema, SQLSchema } from '$lib/stores' -import { formatResourceTypes } from './utils' -import { EDIT_CONFIG, FIX_CONFIG, GEN_CONFIG } from './prompts' import { Mistral } from '@mistralai/mistralai' import { buildClientSchema, printSchema } from 'graphql' +import { OpenAI } from 'openai' import type { ChatCompletionCreateParamsStreaming, ChatCompletionMessageParam } from 'openai/resources/index.mjs' +import { get, type Writable } from 'svelte/store' +import { OpenAPI, ResourceService, type Script } from '../../gen' +import { EDIT_CONFIG, FIX_CONFIG, GEN_CONFIG } from './prompts' +import { formatResourceTypes } from './utils' import type { MessageCreateParams, MessageParam } from '@anthropic-ai/sdk/resources/messages.mjs' -import type { ChatCompletionRequest } from '@mistralai/mistralai/models/components/chatcompletionrequest' import type { - SystemMessage, - UserMessage, AssistantMessage, - ToolMessage, CompletionEvent, - ContentChunk + ContentChunk, + SystemMessage, + ToolMessage, + UserMessage } from '@mistralai/mistralai/models/components' +import type { ChatCompletionRequest } from '@mistralai/mistralai/models/components/chatcompletionrequest' export const SUPPORTED_LANGUAGES = new Set(Object.keys(GEN_CONFIG.prompts)) -export type AiProviderTypes = 'openai' | 'anthropic' | 'mistral' - -interface AiProvider { - init: (workspace: string, updateClient: boolean, token?: string) => void +export const AI_DEFAULT_MODELS: Record = { + openai: ['gpt-4o', 'gpt-4o-mini'], + anthropic: ['claude-3-5-sonnet-latest', 'claude-3-5-haiku-latest'], + mistral: ['codestral-latest'], + deepseek: ['deepseek-chat', 'deepseek-reasoner'], + groq: ['llama-3.3-70b-versatile', 'llama-3.1-8b-instant'], + openrouter: ['meta-llama/llama-3.2-3b-instruct:free'], + customai: [] } -class WorkspacedMistral implements AiProvider { - private client: Mistral | undefined +export const OPENAI_COMPATIBLE_BASE_URLS = { + groq: 'https://api.groq.com/openai/v1', + openrouter: 'https://openrouter.ai/api/v1', + deepseek: 'https://api.deepseek.com/v1' +} as const - init(workspace: string, updateClient: boolean, token?: string) { - if (!this.client || updateClient) { - this.client = initWorkspaceAiProvider(workspace, 'mistral', token) as unknown as Mistral - } +class WorkspacedAIClients { + private openaiClient: OpenAI | undefined + private anthropicClient: Anthropic | undefined + private mistralClient: Mistral | undefined + + init(workspace: string) { + this.initOpenai(workspace) + this.initAnthropic(workspace) + this.initMistral(workspace) } - getClient() { - if (!this.client) { - throw new Error('AnthropicAi not initialized') + private getBaseURL(workspace: string) { + return `${location.origin}${OpenAPI.BASE}/w/${workspace}/ai/proxy` + } + + private initOpenai(workspace: string) { + const baseURL = this.getBaseURL(workspace) + this.openaiClient = new OpenAI({ + baseURL, + apiKey: 'fake-key', + defaultHeaders: { + Authorization: '' // a non empty string will be unable to access Windmill backend proxy + }, + dangerouslyAllowBrowser: true + }) + } + + private initAnthropic(workspace: string) { + const baseURL = this.getBaseURL(workspace) + this.anthropicClient = new Anthropic({ + baseURL, + apiKey: 'fake-key', + dangerouslyAllowBrowser: true + }) + } + + private initMistral(workspace: string) { + const baseURL = this.getBaseURL(workspace) + this.mistralClient = new Mistral({ + serverURL: baseURL + }) + } + + getOpenaiClient() { + if (!this.openaiClient) { + throw new Error('OpenAI not initialized') } - return this.client + return this.openaiClient + } + + getAnthropicClient() { + if (!this.anthropicClient) { + throw new Error('Anthropic not initialized') + } + return this.anthropicClient + } + + getMistralClient() { + if (!this.mistralClient) { + throw new Error('Mistral not initialized') + } + return this.mistralClient } } -export namespace MistralAi { - export let workspace = new WorkspacedMistral() +export const workspaceAIClients = new WorkspacedAIClients() +const DEFAULT_COMPLETION_CONFIG: ChatCompletionCreateParamsStreaming = { + model: '', + max_tokens: 8000, //TODO: make this dynamic + temperature: 0, + seed: 42, + stream: true, + messages: [] +} + +namespace MistralAI { export const mistralConfig: ChatCompletionRequest = { temperature: 0, model: null, @@ -78,30 +152,11 @@ export namespace MistralAi { } } -class WorkspacedAnthropic implements AiProvider { - private client: Anthropic | undefined - - init(workspace: string, updateClient: boolean, token: string | undefined = undefined) { - if (!this.client || updateClient) { - this.client = initWorkspaceAiProvider(workspace, 'anthropic', token) as unknown as Anthropic - } - } - - getClient() { - if (!this.client) { - throw new Error('AnthropicAi not initialized') - } - return this.client - } -} - -export namespace AnthropicAi { - export let workspace = new WorkspacedAnthropic() - +export namespace AnthropicAI { export const config: MessageCreateParams = { temperature: 0, max_tokens: 8192, - model: 'claude-3-5-sonnet-20241022', + model: '', messages: [] } @@ -135,30 +190,11 @@ export namespace AnthropicAi { } } -class WorkspacedOpenai implements AiProvider { - private client: OpenAI | undefined - - init(workspace: string, updateClient: boolean, token: string | undefined = undefined) { - if (!this.client || updateClient) { - this.client = initWorkspaceAiProvider(workspace, 'openai', token) as unknown as OpenAI - } - } - - getClient() { - if (!this.client) { - throw new Error('OpenAI not initialized') - } - return this.client - } -} - -export namespace OpenAi { - export let workspace = new WorkspacedOpenai() - +namespace OpenAi { export const openaiConfig: ChatCompletionCreateParamsStreaming = { temperature: 0, max_tokens: 16384, - model: 'gpt-4o-2024-08-06', + model: '', seed: 42, stream: true, messages: [] @@ -169,125 +205,38 @@ export namespace OpenAi { } } -export function initAllAiWorkspace(workspace: string, updateClient: boolean = false) { - OpenAi.workspace.init(workspace, updateClient) - AnthropicAi.workspace.init(workspace, updateClient) - MistralAi.workspace.init(workspace, updateClient) -} - -function initWorkspaceAiProvider( - workspace: string, - aiProvider: AiProviderTypes, - token: string | undefined = undefined -): Anthropic | OpenAI | Mistral { - const baseURL = `${location.origin}${OpenAPI.BASE}/w/${workspace}/ai/proxy` - let client - switch (aiProvider) { - case 'openai': { - client = new OpenAI({ - baseURL, - apiKey: 'fake-key', - defaultHeaders: { - Authorization: token ? `Bearer ${token}` : '' - }, - dangerouslyAllowBrowser: true - }) - break - } - case 'anthropic': { - client = new Anthropic({ - baseURL, - apiKey: 'fake-key', - defaultHeaders: { - Authorization: token ? `Bearer ${token}` : '' - }, - dangerouslyAllowBrowser: true - }) - break - } - case 'mistral': { - client = new Mistral({ - serverURL: baseURL - }) - } - } - return client -} - export async function testKey({ apiKey, + resourcePath, + model, abortController, messages, aiProvider }: { apiKey?: string + resourcePath?: string + model: string | undefined messages: ChatCompletionMessageParam[] abortController: AbortController - aiProvider: AiProviderTypes + aiProvider: AIProvider }) { - if (apiKey) { - switch (aiProvider) { - case 'openai': { - const openai = new OpenAI({ - apiKey, - dangerouslyAllowBrowser: true - }) - await openai.chat.completions.create( - { - ...OpenAi.openaiConfig, - messages, - stream: false - }, - { - signal: abortController.signal - } - ) - break - } - case 'anthropic': { - const anthropic = new Anthropic({ - apiKey, - dangerouslyAllowBrowser: true - }) - const [, anthropicMessages] = AnthropicAi.getSystemPromptAndArrayMessages(messages) - await anthropic.messages.create( - { - ...AnthropicAi.config, - messages: anthropicMessages, - stream: false - }, - { - signal: abortController.signal - } - ) - break - } - case 'mistral': { - const mistral = new Mistral({ - apiKey - }) - await mistral.chat.complete( - { - ...MistralAi.mistralConfig, - model: 'codestral-latest', - stream: false, - messages: messages as MistralAi.MistralParamsMessage[] - }, - { - fetchOptions: { - signal: abortController.signal, - headers: { - 'content-type': 'application/json' - } - } - } - ) - break - } - } - } else { - await getNonStreamingCompletion(messages, abortController, aiProvider, undefined, true) + if (!apiKey && !resourcePath) { + throw new Error('API key or resource path is required') } + const modelToTest = model ?? AI_DEFAULT_MODELS[aiProvider][0] + + if (!modelToTest) { + throw new Error('Missing a model to test') + } + + await getNonStreamingCompletion( + messages, + abortController, + aiProvider, + apiKey, + resourcePath, + modelToTest + ) } interface BaseOptions { @@ -410,7 +359,9 @@ function addDBSChema(scriptOptions: CopilotOptions, prompt: string) { const { dbSchema, language } = scriptOptions if ( dbSchema && - ['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql', 'oracledb'].includes(language) && // make sure we are using a SQL/query language + ['postgresql', 'mysql', 'snowflake', 'bigquery', 'mssql', 'graphql', 'oracledb'].includes( + language + ) && // make sure we are using a SQL/query language language === dbSchema.lang // make sure we are using the same language as the schema ) { let { stringified } = dbSchema @@ -467,66 +418,109 @@ const PROMPTS_CONFIGS = { export async function getNonStreamingCompletion( messages: ChatCompletionMessageParam[], abortController: AbortController, - aiProvider: AiProviderTypes, - model = OpenAi.openaiConfig.model, - noCache?: boolean + aiProvider: AIProvider, + apiKey?: string, // testing API KEY directly from the frontend + resourcePath?: string, // testing resource path passed as a header to the backend proxy + forceModel?: string ) { let response: string | undefined = '' - const queryOptions = { - query: { - no_cache: noCache - }, + let model = forceModel + + if (!model) { + model = get(copilotSessionModel) + let info = get(copilotInfo) + const { ai_models: aiModels } = info + + if (!model || !aiModels.includes(model)) { + console.warn('Invalid model, using default model:', aiModels[0]) + model = aiModels[0] + } + } + + if (!model) { + throw new Error('No model found') + } + + const fetchOptions: { + signal: AbortSignal + headers?: Record + } = { signal: abortController.signal } - switch (aiProvider) { - case 'openai': { - const openaiClient = OpenAi.workspace.getClient() - const completion = await openaiClient.chat.completions.create( - { - ...OpenAi.openaiConfig, - messages, - stream: false, - model - }, - queryOptions - ) - response = completion.choices[0]?.message.content || '' - break + if (resourcePath) { + fetchOptions.headers = { + 'X-Resource-Path': resourcePath } + } + switch (aiProvider) { case 'anthropic': { - const anthropicClient = AnthropicAi.workspace.getClient() - const [system, anthropicMessages] = AnthropicAi.getSystemPromptAndArrayMessages(messages) + const anthropicClient = apiKey + ? new Anthropic({ + apiKey, + dangerouslyAllowBrowser: true + }) + : workspaceAIClients.getAnthropicClient() + const [system, anthropicMessages] = AnthropicAI.getSystemPromptAndArrayMessages(messages) const message = await anthropicClient.messages.create( { - ...AnthropicAi.config, + ...AnthropicAI.config, system, + model, messages: anthropicMessages, stream: false }, - queryOptions + fetchOptions ) response = message.content[0].type === 'text' ? message.content[0].text : '' break } case 'mistral': { - const mistralClient = MistralAi.workspace.getClient() + const mistralClient = apiKey + ? new Mistral({ + apiKey + }) + : workspaceAIClients.getMistralClient() const message = await mistralClient.chat.complete( { - ...MistralAi.mistralConfig, - model: 'codestral-latest', + ...MistralAI.mistralConfig, + model, stream: false, - messages: messages as MistralAi.MistralParamsMessage[] + messages: messages as MistralAI.MistralParamsMessage[] }, { - fetchOptions: { - signal: abortController.signal, - cache: 'no-store' - } + fetchOptions: fetchOptions } ) - response = MistralAi.retrieveTextValue(message.choices && message.choices[0].message.content) + response = MistralAI.retrieveTextValue(message.choices && message.choices[0].message.content) break } + default: { + if (aiProvider === 'customai' && apiKey) { + throw new Error('Cannot test API key for Custom AI, only resource path is supported') + } + const baseURL = OPENAI_COMPATIBLE_BASE_URLS[aiProvider] + + if (apiKey && aiProvider !== 'openai' && !baseURL) { + throw new Error('No base URL for this provider: ' + aiProvider) + } + const openaiClient = apiKey + ? new OpenAI({ + apiKey, + baseURL, + dangerouslyAllowBrowser: true + }) + : workspaceAIClients.getOpenaiClient() + const completion = await openaiClient.chat.completions.create( + { + ...(aiProvider === 'openai' ? OpenAi.openaiConfig : DEFAULT_COMPLETION_CONFIG), + messages, + model, + stream: false + }, + fetchOptions + ) + response = completion.choices[0]?.message.content || '' + } } return response } @@ -534,17 +528,30 @@ export async function getNonStreamingCompletion( export async function getCompletion( messages: ChatCompletionMessageParam[], abortController: AbortController, - aiProvider: AiProviderTypes, - model = OpenAi.openaiConfig.model + aiProvider: AIProvider ) { + let model = get(copilotSessionModel) + let info = get(copilotInfo) + const { ai_models: aiModels } = info + + if (!model || !aiModels.includes(model)) { + console.warn('Invalid model, using default model:', aiModels[0]) + model = aiModels[0] + } + + if (!model) { + throw new Error('No model found') + } + switch (aiProvider) { case 'anthropic': { - const anthropicClient = AnthropicAi.workspace.getClient() - const [system, anthropicMessages] = AnthropicAi.getSystemPromptAndArrayMessages(messages) + const anthropicClient = workspaceAIClients.getAnthropicClient() + const [system, anthropicMessages] = AnthropicAI.getSystemPromptAndArrayMessages(messages) const completion = await anthropicClient.messages.create( { - ...AnthropicAi.config, + ...AnthropicAI.config, + model, system, messages: anthropicMessages, stream: true @@ -553,13 +560,29 @@ export async function getCompletion( ) return completion } - case 'openai': { - const openaiClient = OpenAi.workspace.getClient() + case 'mistral': { + const mistralClient = workspaceAIClients.getMistralClient() + const message = await mistralClient.chat.stream( + { + ...MistralAI.mistralConfig, + model, + messages: messages as MistralAI.MistralParamsMessage[] + }, + { + fetchOptions: { + signal: abortController.signal + } + } + ) + return message + } + default: { + const openaiClient = workspaceAIClients.getOpenaiClient() const completion = await openaiClient.chat.completions.create( { - ...OpenAi.openaiConfig, - messages, - model + ...(aiProvider === 'openai' ? OpenAi.openaiConfig : DEFAULT_COMPLETION_CONFIG), + model, + messages }, { signal: abortController.signal @@ -567,23 +590,6 @@ export async function getCompletion( ) return completion } - case 'mistral': { - const mistralClient = MistralAi.workspace.getClient() - const message = await mistralClient.chat.stream( - { - ...MistralAi.mistralConfig, - model: 'codestral-latest', - messages: messages as MistralAi.MistralParamsMessage[] - }, - { - fetchOptions: { - signal: abortController.signal, - cache: 'no-store' - } - } - ) - return message - } } } @@ -592,21 +598,20 @@ export function getResponseFromEvent( | Anthropic.Messages.RawMessageStreamEvent | OpenAI.Chat.Completions.ChatCompletionChunk | CompletionEvent, - aiProvider: AiProviderTypes + aiProvider: AIProvider ): string { switch (aiProvider) { - case 'openai': { - const messages = part as OpenAI.Chat.Completions.ChatCompletionChunk - return OpenAi.retrieveTextValue(messages) - } case 'anthropic': { const messages = part as Anthropic.Messages.RawMessageStreamEvent - return AnthropicAi.retrieveTextValue(messages) + return AnthropicAI.retrieveTextValue(messages) } case 'mistral': { const messages = part as CompletionEvent - return MistralAi.retrieveTextValue(messages.data.choices[0].delta.content) + return MistralAI.retrieveTextValue(messages.data.choices[0].delta.content) } + default: + const messages = part as OpenAI.Chat.Completions.ChatCompletionChunk + return OpenAi.retrieveTextValue(messages) } } @@ -614,7 +619,7 @@ export async function copilot( scriptOptions: CopilotOptions, generatedCode: Writable, abortController: AbortController, - aiProvider: AiProviderTypes, + aiProvider: AIProvider, generatedExplanation?: Writable ) { const { prompt, systemPrompt } = await getPrompts(scriptOptions) @@ -704,7 +709,7 @@ export async function deltaCodeCompletion( messages: ChatCompletionMessageParam[], generatedCodeDelta: Writable, abortController: AbortController, - aiProvider: AiProviderTypes + aiProvider: AIProvider ) { const completion = await getCompletion(messages, abortController, aiProvider) diff --git a/frontend/src/lib/components/instanceSettings.ts b/frontend/src/lib/components/instanceSettings.ts index 75af6e568c..b94ba7a19a 100644 --- a/frontend/src/lib/components/instanceSettings.ts +++ b/frontend/src/lib/components/instanceSettings.ts @@ -8,11 +8,11 @@ export interface Setting { key: string // If value is not specified for first element, it will automatcally use undefined select_items?: { - label: string, - tooltip?: string, + label: string + tooltip?: string // If not specified, label will be used - value?: any, - }[], + value?: any + }[] fieldType: | 'text' | 'number' @@ -41,7 +41,7 @@ export interface Setting { isValid?: (value: any) => boolean error?: string defaultValue?: () => any - codeAreaLang?: string, + codeAreaLang?: string } export type SettingStorage = 'setting' @@ -81,9 +81,9 @@ export const settings: Record = { isValid: (value: string | undefined) => value ? value?.startsWith('http') && - value.includes('://') && - !value?.endsWith('/') && - !value?.endsWith(' ') + value.includes('://') && + !value?.endsWith('/') && + !value?.endsWith(' ') : false }, { @@ -182,7 +182,7 @@ export const settings: Record = { { label: 'Azure OpenAI base path', description: - 'All Windmill AI features will run on the specified deployed model. Format: https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}. Learn more', + 'All workspaces using an OpenAI resource for Windmill AI will run on the specified deployed model. Format: https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}. Learn more', key: 'openai_azure_base_path', fieldType: 'text', storage: 'setting', @@ -236,24 +236,26 @@ export const settings: Record = { // 2. Change LATEST_STABLE_PY in dockerfile // 3. Change #[default] annotation for PyVersion in backend placeholder: '3.10,3.11,3.12,3.13', - select_items: [{ - label: "Latest Stable", - value: "default", - tooltip: "python-3.11", - }, - { - label: "3.10", - }, - { - label: "3.11", - }, - { - label: "3.12", - }, - { - label: "3.13", - }], - storage: 'setting', + select_items: [ + { + label: 'Latest Stable', + value: 'default', + tooltip: 'python-3.11' + }, + { + label: '3.10' + }, + { + label: '3.11' + }, + { + label: '3.12' + }, + { + label: '3.13' + } + ], + storage: 'setting' }, { label: 'Pip index url', @@ -294,8 +296,7 @@ export const settings: Record = { }, { label: 'Nuget Config', - description: - 'Write a nuget.config file to set custom package sources and credentials', + description: 'Write a nuget.config file to set custom package sources and credentials', key: 'nuget_config', fieldType: 'codearea', codeAreaLang: 'xml', diff --git a/frontend/src/lib/components/sidebar/WorkspaceMenu.svelte b/frontend/src/lib/components/sidebar/WorkspaceMenu.svelte index 7b75c77ab1..b17055ac52 100644 --- a/frontend/src/lib/components/sidebar/WorkspaceMenu.svelte +++ b/frontend/src/lib/components/sidebar/WorkspaceMenu.svelte @@ -19,7 +19,7 @@ import MenuButton from './MenuButton.svelte' import { MenuItem } from '@rgossiaux/svelte-headlessui' import { isCloudHosted } from '$lib/cloud' - import { initAllAiWorkspace } from '../copilot/lib' + import { workspaceAIClients } from '../copilot/lib' import { twMerge } from 'tailwind-merge' export let isCollapsed: boolean = false @@ -31,7 +31,7 @@ if ($workspaceStore === id) { return } - initAllAiWorkspace(id, true) + workspaceAIClients.init(id) const editPages = [ '/scripts/edit/', '/flows/edit/', @@ -82,8 +82,11 @@ >
-
{workspace.name}
-
+
{workspace.name}
+
{workspace.id}
@@ -91,7 +94,7 @@
+ /> {/if}
diff --git a/frontend/src/lib/stores.ts b/frontend/src/lib/stores.ts index 98886e1bc0..ec158f371a 100644 --- a/frontend/src/lib/stores.ts +++ b/frontend/src/lib/stores.ts @@ -1,13 +1,14 @@ import { BROWSER } from 'esm-env' import { derived, type Readable, writable } from 'svelte/store' +import type { IntrospectionQuery } from 'graphql' import { - type WorkspaceDefaultScripts, + type AIProvider, + type OperatorSettings, type TokenResponse, type UserWorkspaceList, - type OperatorSettings + type WorkspaceDefaultScripts } from './gen' -import type { IntrospectionQuery } from 'graphql' import { getLocalSetting } from './utils' export interface UserExt { @@ -82,13 +83,14 @@ export const userWorkspaces: Readable< } }) export const copilotInfo = writable<{ - ai_provider: string + ai_provider: AIProvider exists_ai_resource: boolean - code_completion_enabled: boolean + code_completion_model?: string + ai_models: string[] }>({ - ai_provider: '', + ai_provider: 'openai', exists_ai_resource: false, - code_completion_enabled: false + ai_models: [] }) export const codeCompletionLoading = writable(false) export const metadataCompletionEnabled = writable(true) @@ -103,6 +105,9 @@ export const vimMode = writable(getLocalSetting(VIM_MODE_SETTING_NAME) export const codeCompletionSessionEnabled = writable( getLocalSetting(CODE_COMPLETION_SETTING_NAME) != 'false' ) +export const copilotSessionModel = writable( + getLocalSetting(CODE_COMPLETION_SETTING_NAME) ?? undefined +) export const usedTriggerKinds = writable([]) type SQLBaseSchema = { diff --git a/frontend/src/routes/(root)/(logged)/+layout.svelte b/frontend/src/routes/(root)/(logged)/+layout.svelte index 90b49a3ed2..f94049271c 100644 --- a/frontend/src/routes/(root)/(logged)/+layout.svelte +++ b/frontend/src/routes/(root)/(logged)/+layout.svelte @@ -44,7 +44,7 @@ import { syncTutorialsTodos } from '$lib/tutorialUtils' import { ArrowLeft, Search } from 'lucide-svelte' import { getUserExt } from '$lib/user' - import { initAllAiWorkspace } from '$lib/components/copilot/lib' + import { workspaceAIClients } from '$lib/components/copilot/lib' import { twMerge } from 'tailwind-merge' import OperatorMenu from '$lib/components/sidebar/OperatorMenu.svelte' import GlobalSearchModal from '$lib/components/search/GlobalSearchModal.svelte' @@ -192,11 +192,9 @@ async function loadUsedTriggerKinds() { let usedKinds: string[] = [] const { http_routes_used, websocket_used, kafka_used, postgres_used, nats_used } = - await WorkspaceService.getUsedTriggers( - { - workspace: $workspaceStore ?? '' - } - ) + await WorkspaceService.getUsedTriggers({ + workspace: $workspaceStore ?? '' + }) if (http_routes_used) { usedKinds.push('http') } @@ -242,15 +240,19 @@ let devOnly = $page.url.pathname.startsWith(base + '/scripts/dev') async function loadCopilot(workspace: string) { - initAllAiWorkspace(workspace) + workspaceAIClients.init(workspace) try { - copilotInfo.set(await WorkspaceService.getCopilotInfo({ workspace })) - } catch (err) { - console.log(err) + const info = await WorkspaceService.getCopilotInfo({ workspace }) copilotInfo.set({ - ai_provider: '', + ...info, + ai_provider: info.ai_provider ?? 'openai' + }) + } catch (err) { + copilotInfo.set({ + ai_provider: 'openai', exists_ai_resource: false, - code_completion_enabled: false + code_completion_model: undefined, + ai_models: [] }) console.error('Could not get copilot info') } @@ -294,7 +296,13 @@ setContext('openSearchWithPrefilledText', openSearchModal) $: { - if ($enterpriseLicense && $workspaceStore && $userStore && $devopsRole !== undefined && ($devopsRole || $userStore.is_admin)) { + if ( + $enterpriseLicense && + $workspaceStore && + $userStore && + $devopsRole !== undefined && + ($devopsRole || $userStore.is_admin) + ) { mountModal = true loadCriticalAlertsMuted() } diff --git a/frontend/src/routes/(root)/(logged)/user/(user)/create_workspace/+page.svelte b/frontend/src/routes/(root)/(logged)/user/(user)/create_workspace/+page.svelte index 11697cad39..a5fb6ee636 100644 --- a/frontend/src/routes/(root)/(logged)/user/(user)/create_workspace/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/user/(user)/create_workspace/+page.svelte @@ -6,7 +6,8 @@ SettingService, UserService, VariableService, - WorkspaceService + WorkspaceService, + type AIProvider } from '$lib/gen' import { validateUsername } from '$lib/utils' import { logoutWithRedirect } from '$lib/logout' @@ -18,12 +19,12 @@ import Tooltip from '$lib/components/Tooltip.svelte' import { onMount } from 'svelte' import { sendUserToast } from '$lib/toast' - import TestAiKey from '$lib/components/copilot/TestAiKey.svelte' + import TestAIKey from '$lib/components/copilot/TestAIKey.svelte' import { switchWorkspace } from '$lib/storeUtils' import { isCloudHosted } from '$lib/cloud' import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte' import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte' - import type { AiProviderTypes } from '$lib/components/copilot/lib' + import { AI_DEFAULT_MODELS } from '$lib/components/copilot/lib' const rd = $page.url.searchParams.get('rd') @@ -41,8 +42,12 @@ let colorEnabled = false function generateRandomColor() { - const randomColor = '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0'); - workspaceColor = randomColor; + const randomColor = + '#' + + Math.floor(Math.random() * 16777215) + .toString(16) + .padStart(6, '0') + workspaceColor = randomColor } $: id = name.toLowerCase().replace(/\s/gi, '-') @@ -111,7 +116,8 @@ workspace: id, requestBody: { ai_resource: { path, provider: selected }, - code_completion_enabled: codeCompletionEnabled + ai_models: aiKey ? AI_DEFAULT_MODELS[selected].slice(0, 1) : [], + code_completion_model: codeCompletionEnabled ? AI_DEFAULT_MODELS[selected][0] : undefined } }) } @@ -178,7 +184,7 @@ let auto_invite = false let operatorOnly = false - let selected: AiProviderTypes = 'openai' + let selected: Exclude = 'openai' @@ -198,11 +204,23 @@ @@ -230,9 +248,12 @@
- + + + +
@@ -243,7 +264,12 @@ bind:value={aiKey} on:keyup={handleKeyUp} /> - + {#if aiKey} { - // in JS, an empty string is also falsy - aiResourceInitialPath = aiResourcePath - aiResourceInitialProvider = aiProvider + async function editCopilotConfig(): Promise { if (aiResourcePath) { await WorkspaceService.editCopilotConfig({ workspace: $workspaceStore!, @@ -203,26 +205,30 @@ path: aiResourcePath, provider: aiProvider }, - code_completion_enabled: codeCompletionEnabled + code_completion_model: codeCompletionModel, + ai_models: aiModels } }) copilotInfo.set({ ai_provider: aiProvider, exists_ai_resource: true, - code_completion_enabled: codeCompletionEnabled + code_completion_model: codeCompletionModel, + ai_models: aiModels }) } else { await WorkspaceService.editCopilotConfig({ workspace: $workspaceStore!, requestBody: { ai_resource: undefined, - code_completion_enabled: codeCompletionEnabled + code_completion_model: codeCompletionModel, + ai_models: [] } }) copilotInfo.set({ - ai_provider: '', + ai_provider: 'openai', exists_ai_resource: false, - code_completion_enabled: codeCompletionEnabled + code_completion_model: codeCompletionModel, + ai_models: [] }) } sendUserToast(`Copilot settings updated`) @@ -382,6 +388,7 @@ }, 1000 - (timeEnd - timeStart)) } + let loadedSettings = false async function loadSettings(): Promise { const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! }) team_name = settings.slack_name @@ -395,9 +402,12 @@ customer_id = settings.customer_id workspaceToDeployTo = settings.deploy_to webhook = settings.webhook - aiResourceInitialPath = settings.ai_resource?.path - aiResourceInitialProvider = settings.ai_resource?.provider - selected = (aiResourceInitialProvider as AiProviderTypes) ?? 'openai' + + aiResourcePath = settings.ai_resource?.path + aiProvider = settings.ai_resource?.provider ?? 'openai' + codeCompletionModel = settings.code_completion_model + aiModels = settings.ai_models + errorHandlerItemKind = settings.error_handler?.split('/')[0] as 'flow' | 'script' errorHandlerScriptPath = (settings.error_handler ?? '').split('/').slice(1).join('/') errorHandlerInitialScriptPath = errorHandlerScriptPath @@ -415,7 +425,6 @@ : 'custom' } errorHandlerExtraArgs = settings.error_handler_extra_args ?? {} - codeCompletionEnabled = settings.code_completion_enabled workspaceDefaultAppPath = settings.default_app s3ResourceSettings = convertBackendSettingsToFrontendSettings(settings.large_file_storage) @@ -508,6 +517,8 @@ workspace: $workspaceStore!, path: 'openai_client_credentials_oauth' }) + + loadedSettings = true } let deployUiSettings: { @@ -698,7 +709,9 @@ - {#if tab == 'users'} + {#if !loadedSettings} + + {:else if tab == 'users'} {:else if tab == 'deploy_to'}
@@ -1044,43 +1057,104 @@
- { - aiResourceInitialPath = '' - aiResourceInitialProvider = '' - }} - > - - - - -
- {#key [aiResourceInitialPath, aiResourceInitialProvider, usingOpenaiClientCredentialsOauth, selected]} - { - editCopilotConfig(ev.detail, selected) - }} - /> - - {/key} -
-
- { - editCopilotConfig(aiResourceInitialPath || '', aiResourceInitialProvider || '') + +
+ { + aiResourcePath = '' + aiModels = [] + codeCompletionModel = undefined }} - /> + > + + + + + + + + +
+ {#key aiProvider} + { + if (aiResourcePath && aiModels.length === 0) { + if (aiProvider !== 'customai') { + aiModels = AI_DEFAULT_MODELS[aiProvider].slice(0, 1) + } + } + }} + /> + + {/key} +
+ + {#if aiResourcePath} + + +
+ { + if (codeCompletionModel != undefined) { + codeCompletionModel = undefined + } else { + codeCompletionModel = AI_DEFAULT_MODELS[aiProvider][0] ?? '' + } + }} + checked={codeCompletionModel != undefined} + options={{ + right: 'Code completion' + }} + /> + + {#if codeCompletionModel != undefined} + + {/if} +
+ {/if} +
{:else if tab == 'windmill_lfs'}
From b09ff9787209a76eb1f834751fa476a01fbc9a20 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 4 Feb 2025 22:37:19 +0100 Subject: [PATCH 04/27] chore(main): release 1.457.0 (#5201) * chore(main): release 1.457.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 16 + backend/Cargo.lock | 371 +++++++++--------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 218 insertions(+), 203 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f664e6685..6ea285221a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## [1.457.0](https://github.com/windmill-labs/windmill/compare/v1.456.0...v1.457.0) (2025-02-04) + + +### Features + +* more AI models ([#5207](https://github.com/windmill-labs/windmill/issues/5207)) ([245c871](https://github.com/windmill-labs/windmill/commit/245c8719fc4bf9779be6c653f057d9ffb2724886)) +* **python:** make S3 cache arch specific ([#5196](https://github.com/windmill-labs/windmill/issues/5196)) ([0e80775](https://github.com/windmill-labs/windmill/commit/0e80775d6d2bd1931fed6d5a1d63fc2e70980d55)) + + +### Bug Fixes + +* hide values of WHITELIST_ENVS ([62bfec0](https://github.com/windmill-labs/windmill/commit/62bfec029c00fd067b9906f546b3c9597a54c319)) +* **python:** clear env before installing/finding python ([#5209](https://github.com/windmill-labs/windmill/issues/5209)) ([97c1134](https://github.com/windmill-labs/windmill/commit/97c11340c3175ba946ac6fc77481899fed508af5)) +* support specialization of list of strings to list of enums ([76afbc3](https://github.com/windmill-labs/windmill/commit/76afbc3df33ec87aff47b0db8c57e64209f8f613)) +* timeout on list_user_usage after 300s ([fd0cd58](https://github.com/windmill-labs/windmill/commit/fd0cd587bbe8f453893ae600ae553ee74eb1ba04)) + ## [1.456.0](https://github.com/windmill-labs/windmill/compare/v1.455.2...v1.456.0) (2025-02-01) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index a50ea3f309..10eeca03b0 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -442,7 +442,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -538,7 +538,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -560,7 +560,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -596,7 +596,7 @@ checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -651,9 +651,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-config" -version = "1.5.15" +version = "1.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc47e70fc35d054c8fcd296d47a61711f043ac80534a10b4f741904f81e73a90" +checksum = "50236e4d60fe8458de90a71c0922c761e41755adf091b1b03de1cef537179915" dependencies = [ "aws-credential-types", "aws-runtime", @@ -693,9 +693,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee7643696e7fdd74c10f9eb42848a87fe469d35eae9c3323f80aa98f350baac" +checksum = "76dd04d39cc12844c0994f2c9c5a6f5184c22e9188ec1ff723de41910a21dcad" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -718,9 +718,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.57.0" +version = "1.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54bab121fe1881a74c338c5f723d1592bf3b53167f80268a1274f404e1acc38" +checksum = "16ff718c9ee45cc1ebd4774a0e086bb80a6ab752b4902edf1c9f56b86ee1f770" dependencies = [ "aws-credential-types", "aws-runtime", @@ -740,9 +740,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.58.0" +version = "1.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c8234fd024f7ac61c4e44ea008029bde934250f371efe7d4a39708397b1080c" +checksum = "5183e088715cc135d8d396fdd3bc02f018f0da4c511f53cb8d795b6a31c55809" dependencies = [ "aws-credential-types", "aws-runtime", @@ -762,9 +762,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.58.0" +version = "1.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba60e1d519d6f23a9df712c04fdeadd7872ac911c84b2f62a8bda92e129b7962" +checksum = "c9f944ef032717596639cea4a2118a3a457268ef51bbb5fde9637e54c465da00" dependencies = [ "aws-credential-types", "aws-runtime", @@ -785,9 +785,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.2.7" +version = "1.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "690118821e46967b3c4501d67d7d52dd75106a9c54cf36cefa1985cedbe94e05" +checksum = "0bc5bbd1e4a2648fd8c5982af03935972c24a2f9846b396de661d351ee3ce837" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -858,9 +858,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.7.7" +version = "1.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865f7050bbc7107a6c98a397a9fcd9413690c27fa718446967cf03b2d3ac517e" +checksum = "d526a12d9ed61fadefda24abe2e682892ba288c2018bcb38b1b4c111d13f6d92" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -902,9 +902,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.2.12" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28f6feb647fb5e0d5b50f0472c19a7db9462b74e2fec01bb0b44eedcc834e97" +checksum = "c7b8a53819e42f10d0821f56da995e1470b199686a1809168db6ca485665f042" dependencies = [ "base64-simd 0.8.0", "bytes", @@ -937,9 +937,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.4" +version = "1.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0df5a18c4f951c645300d365fec53a61418bcf4650f604f85fe2a665bfaa0c2" +checksum = "dfbd0a668309ec1f66c0f6bda4840dd6d4796ae26d699ebc266d7cc95c6d040f" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1136,7 +1136,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.96", + "syn 2.0.98", "which 4.4.2", ] @@ -1157,7 +1157,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1332,7 +1332,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1434,7 +1434,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -1445,9 +1445,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" dependencies = [ "serde", ] @@ -1552,9 +1552,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.11" +version = "1.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" +checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" dependencies = [ "jobserver", "libc", @@ -1668,9 +1668,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.27" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" dependencies = [ "clap_builder", "clap_derive", @@ -1690,14 +1690,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2082,7 +2082,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2154,7 +2154,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2187,7 +2187,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -2714,7 +2714,7 @@ dependencies = [ "stringcase", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.96", + "syn 2.0.98", "thiserror 1.0.69", ] @@ -2892,15 +2892,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.18" +version = "0.99.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" dependencies = [ "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3005,7 +3005,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3028,7 +3028,7 @@ checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3185,7 +3185,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3205,7 +3205,7 @@ checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3410,7 +3410,7 @@ checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ "proc-macro2", "swc_macros_common", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3534,7 +3534,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3799,7 +3799,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -3964,7 +3964,7 @@ checksum = "f208758247e68e239acaa059e72e4ce1f30f2a4b6523f19c1b923d25b7e9cceb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -4527,7 +4527,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -4649,7 +4649,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5099,9 +5099,9 @@ dependencies = [ [[package]] name = "malachite-base" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7d99f6446827fb878a005ea5cf7db1d78d236391733012216a9b8eb5dbd824" +checksum = "5ea0ed76adf7defc1a92240b5c36d5368cfe9251640dcce5bd2d0b7c1fd87aeb" dependencies = [ "hashbrown 0.14.5", "itertools 0.11.0", @@ -5124,9 +5124,9 @@ dependencies = [ [[package]] name = "malachite-nz" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36157fa6b969ca652feefe159e202ebd85683e57463074492a7ec3eaa468c97" +checksum = "34a79feebb2bc9aa7762047c8e5495269a367da6b5a90a99882a0aeeac1841f7" dependencies = [ "itertools 0.11.0", "libm", @@ -5135,9 +5135,9 @@ dependencies = [ [[package]] name = "malachite-q" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dde29a4fcf7025f482745247951f7c5d0459671f59aedf09035b2e2c3746fdf" +checksum = "50f235d5747b1256b47620f5640c2a17a88c7569eebdf27cd9cb130e1a619191" dependencies = [ "itertools 0.11.0", "malachite-base", @@ -5302,7 +5302,7 @@ checksum = "a7ce64b975ed4f123575d11afd9491f2e37bbd5813fbfbc0f09ae1fbddea74e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5341,7 +5341,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "termcolor", "thiserror 1.0.69", ] @@ -5616,7 +5616,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5701,9 +5701,9 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oneshot" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e296cf87e61c9cfc1a61c3c63a0f7f286ed4554e0e22be84e8a38e1d264a2a29" +checksum = "79d72a7c0f743d2ebb0a2ad1d219db75fdc799092ed3a884c9144c42a31225bd" [[package]] name = "onig" @@ -5777,9 +5777,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.69" +version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -5798,7 +5798,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -5818,9 +5818,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", @@ -6196,7 +6196,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -6222,22 +6222,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -6285,19 +6285,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" -[[package]] -name = "postgres-native-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d442770e2b1e244bb5eb03b31c79b65bb2568f413b899eaba850fa945a65954" -dependencies = [ - "futures", - "native-tls", - "tokio", - "tokio-native-tls", - "tokio-postgres 0.7.12", -] - [[package]] name = "postgres-native-tls" version = "0.5.0" @@ -6309,11 +6296,22 @@ dependencies = [ "tokio-postgres 0.7.11", ] +[[package]] +name = "postgres-native-tls" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f39498473c92f7b6820ae970382c1d83178a3454c618161cb772e8598d9f6f" +dependencies = [ + "native-tls", + "tokio", + "tokio-native-tls", + "tokio-postgres 0.7.13", +] + [[package]] name = "postgres-protocol" version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acda0ebdebc28befa84bee35e651e4c5f09073d668c7aed4cf7e23c3cda84b23" +source = "git+https://github.com/imor/rust-postgres?rev=20265ef38e32a06f76b6f9b678e2077fc2211f6b#20265ef38e32a06f76b6f9b678e2077fc2211f6b" dependencies = [ "base64 0.22.1", "byteorder", @@ -6329,8 +6327,9 @@ dependencies = [ [[package]] name = "postgres-protocol" -version = "0.6.7" -source = "git+https://github.com/imor/rust-postgres?rev=20265ef38e32a06f76b6f9b678e2077fc2211f6b#20265ef38e32a06f76b6f9b678e2077fc2211f6b" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" dependencies = [ "base64 0.22.1", "byteorder", @@ -6339,7 +6338,7 @@ dependencies = [ "hmac", "md-5 0.10.6", "memchr", - "rand 0.8.5", + "rand 0.9.0", "sha2 0.10.8", "stringprep", ] @@ -6351,21 +6350,21 @@ source = "git+https://github.com/imor/rust-postgres?rev=20265ef38e32a06f76b6f9b6 dependencies = [ "bytes", "fallible-iterator", - "postgres-protocol 0.6.7 (git+https://github.com/imor/rust-postgres?rev=20265ef38e32a06f76b6f9b678e2077fc2211f6b)", + "postgres-protocol 0.6.7", ] [[package]] name = "postgres-types" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66ea23a2d0e5734297357705193335e0a957696f34bed2f2faefacb2fec336f" +checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" dependencies = [ "array-init", "bit-vec", "bytes", "chrono", "fallible-iterator", - "postgres-protocol 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", + "postgres-protocol 0.6.8", "serde", "serde_json", "uuid 1.12.1", @@ -6409,7 +6408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -6449,7 +6448,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -6460,7 +6459,7 @@ checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", "proc-macro2", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -6472,7 +6471,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -6533,7 +6532,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "syn 2.0.96", + "syn 2.0.98", "thiserror 1.0.69", "typify", "unicode-ident", @@ -6553,7 +6552,7 @@ dependencies = [ "serde_json", "serde_tokenstream", "serde_yaml", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -6596,7 +6595,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -6798,7 +6797,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.0", - "zerocopy 0.8.14", + "zerocopy 0.8.16", ] [[package]] @@ -6856,7 +6855,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" dependencies = [ "getrandom 0.3.1", - "zerocopy 0.8.14", + "zerocopy 0.8.16", ] [[package]] @@ -7280,7 +7279,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.96", + "syn 2.0.98", "walkdir", ] @@ -7314,7 +7313,7 @@ dependencies = [ "borsh", "bytes", "num-traits", - "postgres-types 0.2.8", + "postgres-types 0.2.9", "rand 0.8.5", "rkyv", "serde", @@ -7666,7 +7665,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -7826,7 +7825,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -7837,7 +7836,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -7911,7 +7910,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -7932,7 +7931,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -7987,7 +7986,7 @@ dependencies = [ "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8337,7 +8336,7 @@ checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8402,7 +8401,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8425,7 +8424,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.96", + "syn 2.0.98", "tempfile", "tokio", "url", @@ -8576,7 +8575,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8636,7 +8635,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8649,7 +8648,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8757,7 +8756,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8806,7 +8805,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8891,7 +8890,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -8998,7 +8997,7 @@ checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -9009,7 +9008,7 @@ checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -9032,7 +9031,7 @@ dependencies = [ "proc-macro2", "quote", "swc_macros_common", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -9048,9 +9047,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -9080,7 +9079,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -9355,7 +9354,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -9366,7 +9365,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -9601,7 +9600,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -9630,7 +9629,7 @@ dependencies = [ "percent-encoding", "phf", "pin-project-lite", - "postgres-protocol 0.6.7 (git+https://github.com/imor/rust-postgres?rev=20265ef38e32a06f76b6f9b678e2077fc2211f6b)", + "postgres-protocol 0.6.7", "postgres-types 0.2.7", "rand 0.8.5", "socket2", @@ -9641,9 +9640,9 @@ dependencies = [ [[package]] name = "tokio-postgres" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5d3742945bc7d7f210693b0c58ae542c6fd47b17adbbda0885f3dcb34a6bdb" +checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" dependencies = [ "async-trait", "byteorder", @@ -9656,9 +9655,9 @@ dependencies = [ "percent-encoding", "phf", "pin-project-lite", - "postgres-protocol 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "postgres-types 0.2.8", - "rand 0.8.5", + "postgres-protocol 0.6.8", + "postgres-types 0.2.9", + "rand 0.9.0", "socket2", "tokio", "tokio-util", @@ -9828,7 +9827,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.7.0", + "winnow 0.7.1", ] [[package]] @@ -9995,7 +9994,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -10224,7 +10223,7 @@ dependencies = [ "regress", "schemars", "serde_json", - "syn 2.0.96", + "syn 2.0.98", "thiserror 1.0.69", "unicode-ident", ] @@ -10241,7 +10240,7 @@ dependencies = [ "serde", "serde_json", "serde_tokenstream", - "syn 2.0.96", + "syn 2.0.98", "typify-impl", ] @@ -10656,7 +10655,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-shared", ] @@ -10690,7 +10689,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -10723,7 +10722,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -10860,7 +10859,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "axum", @@ -10903,7 +10902,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "argon2", @@ -10948,7 +10947,7 @@ dependencies = [ "openssl", "pg_escape", "pin-project", - "postgres-native-tls 0.5.0 (git+https://github.com/imor/rust-postgres?rev=20265ef38e32a06f76b6f9b678e2077fc2211f6b)", + "postgres-native-tls 0.5.0", "prometheus", "quick_cache", "rand 0.9.0", @@ -10996,7 +10995,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.456.0" +version = "1.457.0" dependencies = [ "base64 0.22.1", "chrono", @@ -11014,7 +11013,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.456.0" +version = "1.457.0" dependencies = [ "chrono", "serde", @@ -11027,7 +11026,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "serde", @@ -11041,7 +11040,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "async-stream", @@ -11100,7 +11099,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.456.0" +version = "1.457.0" dependencies = [ "regex", "serde", @@ -11114,7 +11113,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "bytes", @@ -11137,19 +11136,19 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.456.0" +version = "1.457.0" dependencies = [ "itertools 0.14.0", "lazy_static", "proc-macro2", "quote", "regex", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "windmill-parser" -version = "1.456.0" +version = "1.457.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -11158,7 +11157,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "lazy_static", @@ -11170,7 +11169,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "serde_json", @@ -11182,7 +11181,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "gosyn", @@ -11194,7 +11193,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "lazy_static", @@ -11206,7 +11205,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -11217,7 +11216,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -11228,7 +11227,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "async-recursion", @@ -11248,7 +11247,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -11258,14 +11257,14 @@ dependencies = [ "quote", "regex", "serde_json", - "syn 2.0.96", + "syn 2.0.98", "toml 0.7.8", "windmill-parser", ] [[package]] name = "windmill-parser-sql" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "lazy_static", @@ -11277,7 +11276,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "lazy_static", @@ -11295,7 +11294,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "getrandom 0.2.15", @@ -11317,7 +11316,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "serde_json", @@ -11327,7 +11326,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "async-recursion", @@ -11360,7 +11359,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.456.0" +version = "1.457.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -11370,7 +11369,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.456.0" +version = "1.457.0" dependencies = [ "anyhow", "async-recursion", @@ -11410,7 +11409,7 @@ dependencies = [ "opentelemetry", "oracle", "pem 3.0.4", - "postgres-native-tls 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "postgres-native-tls 0.5.1", "prometheus", "rand 0.9.0", "regex", @@ -11423,7 +11422,7 @@ dependencies = [ "tar", "tiberius", "tokio", - "tokio-postgres 0.7.12", + "tokio-postgres 0.7.13", "tokio-util", "tracing", "urlencoding", @@ -11654,9 +11653,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e49d2d35d3fad69b39b94139037ecfb4f359f08958b9c11e7315ce770462419" +checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f" dependencies = [ "memchr", ] @@ -11768,7 +11767,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure", ] @@ -11784,11 +11783,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" +checksum = "7b8c07a70861ce02bad1607b5753ecb2501f67847b9f9ada7c160fff0ec6300c" dependencies = [ - "zerocopy-derive 0.8.14", + "zerocopy-derive 0.8.16", ] [[package]] @@ -11799,18 +11798,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] name = "zerocopy-derive" -version = "0.8.14" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" +checksum = "5226bc9a9a9836e7428936cde76bb6b22feea1a8bfdbc0d241136e4d13417e25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] @@ -11830,7 +11829,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", "synstructure", ] @@ -11859,7 +11858,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.96", + "syn 2.0.98", ] [[package]] diff --git a/backend/Cargo.toml b/backend/Cargo.toml index c174739a84..ff72b9e51e 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.456.0" +version = "1.457.0" authors.workspace = true edition.workspace = true @@ -30,7 +30,7 @@ members = [ ] [workspace.package] -version = "1.456.0" +version = "1.457.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 9df3cda043..aff23d0216 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.456.0 + version: 1.457.0 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 47f2684855..afd49c9abd 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts"; import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts"; import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts"; -export const VERSION = "v1.456.0"; +export const VERSION = "v1.457.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/main.ts b/cli/main.ts index f02dbbe841..ac1ea6d6dd 100644 --- a/cli/main.ts +++ b/cli/main.ts @@ -60,7 +60,7 @@ export { // } // }); -export const VERSION = "1.456.0"; +export const VERSION = "1.457.0"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 95f0c35a97..a08ab6ef8e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.456.0", + "version": "1.457.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.456.0", + "version": "1.457.0", "license": "AGPL-3.0", "dependencies": { "@anthropic-ai/sdk": "^0.32.1", diff --git a/frontend/package.json b/frontend/package.json index 3ea5fd90b8..7d7d5c07db 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.456.0", + "version": "1.457.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index 940df30fff..28a2bf1a73 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.456.0" -wmill_pg = ">=1.456.0" +wmill = ">=1.457.0" +wmill_pg = ">=1.457.0" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 2198173813..55d7780045 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.456.0 + version: 1.457.0 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index aac05daa78..24f2af00b1 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.456.0' + ModuleVersion = '1.457.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 0ee4ed7b5b..3022069d2a 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.456.0" +version = "1.457.0" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index 2c213d557c..2a40adea41 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.456.0" +version = "1.457.0" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 0947f2c0ad..3281395edc 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.456.0", + "version": "1.457.0", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index 347362bc92..02752456e7 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.456.0", + "version": "1.457.0", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index aaa526a611..2d336e491a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.456.0 +1.457.0 From 8a446a658a1b8e82fd8c3a051dd7274df8c54c71 Mon Sep 17 00:00:00 2001 From: HugoCasa Date: Tue, 4 Feb 2025 23:34:03 +0100 Subject: [PATCH 05/27] fix: preprocessor args python (#5210) --- backend/windmill-worker/src/python_executor.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index b5cd49822e..98e1483421 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -1395,12 +1395,12 @@ async fn prepare_wrapper( } else { format!( r#"pre_args["{name}"] = kwargs.get("{name}") - if pre_args["{name}"] is None: - del pre_args["{name}"]"# + if pre_args["{name}"] is None: + del pre_args["{name}"]"# ) } }) - .join("\n ") + .join("\n ") }; Some(spread) } else { From a457c0137cd60901619817cc2a3906def64667ac Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 5 Feb 2025 12:26:53 +0100 Subject: [PATCH 06/27] fix: fix autoscaling inc increase by customized parameter --- backend/ee-repo-ref.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 4076343f5a..dea884bdc9 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -eec544f334523171a3143ce382090d8bd80a23a7 \ No newline at end of file +f65140bebb46f154e7996dc47cac7e473c9b42c1 \ No newline at end of file From c1cab59e00d9a139663aa4fb76bd6ab403039e7d Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 5 Feb 2025 12:47:42 +0100 Subject: [PATCH 07/27] server for workers only shutdown after workers --- backend/src/main.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index d4459a3371..50e9bd2836 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -386,12 +386,18 @@ async fn windmill_main() -> anyhow::Result<()> { tracing::info!("SKIP_MIGRATION set, skipping db migration...") } } + let worker_mode = num_workers > 0; let (killpill_tx, mut killpill_rx) = tokio::sync::broadcast::channel::<()>(2); let mut monitor_killpill_rx = killpill_tx.subscribe(); - let server_killpill_rx = killpill_tx.subscribe(); let (killpill_phase2_tx, _killpill_phase2_rx) = tokio::sync::broadcast::channel::<()>(2); + let server_killpill_rx = if worker_mode { + killpill_phase2_tx.subscribe() + } else { + killpill_tx.subscribe() + }; + let shutdown_signal = windmill_common::shutdown_signal(killpill_tx.clone(), killpill_tx.subscribe()); @@ -450,8 +456,6 @@ Windmill Community Edition {GIT_VERSION} } } - let worker_mode = num_workers > 0; - if server_mode || worker_mode || indexer_mode { let port_var = std::env::var("PORT").ok().and_then(|x| x.parse().ok()); From 7573285a5947cb797a56c28a9fa9af65514b743b Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 5 Feb 2025 12:56:47 +0100 Subject: [PATCH 08/27] kill servers only after workers exited --- backend/src/main.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index 50e9bd2836..e3b846bfb2 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -386,17 +386,11 @@ async fn windmill_main() -> anyhow::Result<()> { tracing::info!("SKIP_MIGRATION set, skipping db migration...") } } - let worker_mode = num_workers > 0; let (killpill_tx, mut killpill_rx) = tokio::sync::broadcast::channel::<()>(2); let mut monitor_killpill_rx = killpill_tx.subscribe(); let (killpill_phase2_tx, _killpill_phase2_rx) = tokio::sync::broadcast::channel::<()>(2); - - let server_killpill_rx = if worker_mode { - killpill_phase2_tx.subscribe() - } else { - killpill_tx.subscribe() - }; + let server_killpill_rx = killpill_phase2_tx.subscribe(); let shutdown_signal = windmill_common::shutdown_signal(killpill_tx.clone(), killpill_tx.subscribe()); @@ -456,6 +450,8 @@ Windmill Community Edition {GIT_VERSION} } } + let worker_mode = num_workers > 0; + if server_mode || worker_mode || indexer_mode { let port_var = std::env::var("PORT").ok().and_then(|x| x.parse().ok()); @@ -650,9 +646,13 @@ Windmill Community Edition {GIT_VERSION} } } if killpill_phase2_tx.receiver_count() > 0 { - tracing::info!("Starting phase 2 of shutdown"); + if worker_mode { + tracing::info!("Starting phase 2 of shutdown"); + } killpill_phase2_tx.send(())?; - tracing::info!("Phase 2 of shutdown completed"); + if worker_mode { + tracing::info!("Phase 2 of shutdown completed"); + } } Ok(()) }; From 32b75b8d84e8f70921c810e676a01894af88d7aa Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 5 Feb 2025 15:56:58 +0100 Subject: [PATCH 09/27] improve error messages with locations --- backend/windmill-common/src/error.rs | 38 +++++++++++++++---- backend/windmill-common/src/lib.rs | 2 +- backend/windmill-common/src/utils.rs | 2 +- backend/windmill-worker/src/bun_executor.rs | 17 ++++++--- .../windmill-worker/src/result_processor.rs | 2 +- 5 files changed, 46 insertions(+), 15 deletions(-) diff --git a/backend/windmill-common/src/error.rs b/backend/windmill-common/src/error.rs index 426354a13d..17094dc717 100644 --- a/backend/windmill-common/src/error.rs +++ b/backend/windmill-common/src/error.rs @@ -42,8 +42,10 @@ pub enum Error { ExecutionErr(String), #[error("IO error: {0}")] IoErr(#[from] io::Error), - #[error("Sql error: {0}")] - SqlErr(#[from] sqlx::Error), + // #[error("Sql error: {0}")] + // SqlErr(#[from] sqlx::Error), + #[error("SqlErr: {error:#} @{location:#}")] + SqlErr { error: sqlx::Error, location: String }, #[error("Bad request: {0}")] BadRequest(String), #[error("Quota exceeded: {0}")] @@ -58,8 +60,8 @@ pub enum Error { DatabaseMigration(#[from] MigrateError), #[error("Non-zero exit status: {0}")] ExitStatus(i32), - #[error("Err: {0:#}")] - Anyhow(#[from] anyhow::Error), + #[error("Error: {error:#} @{location:#}")] + Anyhow { error: anyhow::Error, location: String }, #[error("Error: {0:#?}")] JsonErr(serde_json::Value), #[error("{0}")] @@ -74,6 +76,27 @@ pub enum Error { SerdeJson(#[from] serde_json::Error), } +fn prettify_location(location: &'static Location<'static>) -> String { + location + .to_string() + .split("/") + .last() + .unwrap_or("unknown") + .to_string() +} +impl From for Error { + #[track_caller] + fn from(e: anyhow::Error) -> Self { + Self::Anyhow { error: e, location: prettify_location(std::panic::Location::caller()) } + } +} + +impl From for Error { + fn from(e: sqlx::Error) -> Self { + Self::SqlErr { error: e, location: prettify_location(std::panic::Location::caller()) } + } +} + impl Error { /// https://docs.rs/anyhow/1/anyhow/struct.Error.html#display-representations pub fn alt(&self) -> String { @@ -109,9 +132,10 @@ impl IntoResponse for Error { Self::NotFound(_) => axum::http::StatusCode::NOT_FOUND, Self::NotAuthorized(_) => axum::http::StatusCode::UNAUTHORIZED, Self::RequireAdmin(_) => axum::http::StatusCode::FORBIDDEN, - Self::SqlErr(_) | Self::BadRequest(_) | Self::AiError(_) | Self::QuotaExceeded(_) => { - axum::http::StatusCode::BAD_REQUEST - } + Self::SqlErr { .. } + | Self::BadRequest(_) + | Self::AiError(_) + | Self::QuotaExceeded(_) => axum::http::StatusCode::BAD_REQUEST, _ => axum::http::StatusCode::INTERNAL_SERVER_ERROR, }; diff --git a/backend/windmill-common/src/lib.rs b/backend/windmill-common/src/lib.rs index e47c4fc87f..4bb224e21b 100644 --- a/backend/windmill-common/src/lib.rs +++ b/backend/windmill-common/src/lib.rs @@ -36,7 +36,6 @@ pub mod job_s3_helpers_ee; pub mod jobs; pub mod more_serde; pub mod oauth2; -pub mod teams_ee; pub mod otel_ee; pub mod queue; pub mod s3_helpers; @@ -44,6 +43,7 @@ pub mod schedule; pub mod scripts; pub mod server; pub mod stats_ee; +pub mod teams_ee; pub mod tracing_init; pub mod users; pub mod utils; diff --git a/backend/windmill-common/src/utils.rs b/backend/windmill-common/src/utils.rs index 06a5e20863..735c3d4a27 100644 --- a/backend/windmill-common/src/utils.rs +++ b/backend/windmill-common/src/utils.rs @@ -395,7 +395,7 @@ pub async fn fetch_mute_workspace(_db: &DB, workspace_id: &str) -> Result workspace_id, err ); - Err(Error::SqlErr(err)) + return Err(err.into()); } } } diff --git a/backend/windmill-worker/src/bun_executor.rs b/backend/windmill-worker/src/bun_executor.rs index 77bad32a5d..b810c888fc 100644 --- a/backend/windmill-worker/src/bun_executor.rs +++ b/backend/windmill-worker/src/bun_executor.rs @@ -1,7 +1,8 @@ #[cfg(feature = "deno_core")] use std::time::Instant; -use std::{collections::HashMap, fs, io, path::Path, process::Stdio}; +use std::{collections::HashMap, fs, path::Path, process::Stdio}; +use anyhow::Context; use base64::Engine; use itertools::Itertools; @@ -644,7 +645,7 @@ pub fn copy_recursively( source: impl AsRef, destination: impl AsRef, skip: Option<&Vec>, -) -> io::Result<()> { +) -> Result<()> { let mut stack = Vec::new(); stack.push(( source.as_ref().to_path_buf(), @@ -652,7 +653,9 @@ pub fn copy_recursively( 0, )); while let Some((current_source, current_destination, level)) = stack.pop() { - for entry in fs::read_dir(¤t_source)? { + for entry in fs::read_dir(¤t_source) + .context(format!("reading directory {current_source:?}"))? + { let entry = entry?; let filetype = entry.file_type()?; let destination = current_destination.join(entry.file_name()); @@ -670,7 +673,11 @@ pub fn copy_recursively( fs::create_dir_all(&destination)?; stack.push((entry.path(), destination, level + 1)); } else { - fs::hard_link(&original, &destination)? + fs::hard_link(&original, &destination).map_err(|e| { + error::Error::InternalErr(format!( + "hard linking from {original:?} to {destination:?}: {e:#}" + )) + })?; } } } @@ -984,7 +991,7 @@ pub async fn handle_bun_job( "bunfig.toml".to_string(), ]), ) { - fs::remove_dir_all(&buntar_path)?; + fs::remove_dir_all(&buntar_path).context("deleting buntar directory")?; tracing::error!("Could not create buntar: {e}"); } } diff --git a/backend/windmill-worker/src/result_processor.rs b/backend/windmill-worker/src/result_processor.rs index 5812b12175..f4acc66f29 100644 --- a/backend/windmill-worker/src/result_processor.rs +++ b/backend/windmill-worker/src/result_processor.rs @@ -352,7 +352,7 @@ pub async fn process_result( } } err @ _ => to_raw_value(&SerializedError { - message: format!("error during execution of the script:\n{}", err), + message: format!("error during execution of the script:\n{err:#}",), name: "ExecutionErr".to_string(), step_id: job.flow_step_id.clone(), exit_code: None, From 809242987e2662898a545d727cf3f8f9ea2676cf Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 5 Feb 2025 16:18:20 +0100 Subject: [PATCH 10/27] improve errors II --- backend/windmill-common/src/error.rs | 62 ++++++++++++++----- backend/windmill-worker/src/common.rs | 2 +- .../windmill-worker/src/dedicated_worker.rs | 4 +- backend/windmill-worker/src/handle_child.rs | 6 +- .../windmill-worker/src/result_processor.rs | 13 ++-- 5 files changed, 63 insertions(+), 24 deletions(-) diff --git a/backend/windmill-common/src/error.rs b/backend/windmill-common/src/error.rs index 17094dc717..2fe00bc4ab 100644 --- a/backend/windmill-common/src/error.rs +++ b/backend/windmill-common/src/error.rs @@ -22,8 +22,6 @@ pub type JsonResult = std::result::Result, Error>; #[derive(Debug, Error)] pub enum Error { - #[error("Uuid Error {0}")] - UuidErr(#[from] uuid::Error), #[error("Bad config: {0}")] BadConfig(String), #[error("Connecting to database: {0}")] @@ -40,12 +38,16 @@ pub enum Error { RequireAdmin(String), #[error("{0}")] ExecutionErr(String), - #[error("IO error: {0}")] - IoErr(#[from] io::Error), - // #[error("Sql error: {0}")] - // SqlErr(#[from] sqlx::Error), + #[error("IoErr: {error:#} @{location:#}")] + IoErr { error: io::Error, location: String }, + #[error("Utf8Err: {error:#} @{location:#}")] + Utf8Err { error: std::string::FromUtf8Error, location: String }, + #[error("UuidErr: {error:#} @{location:#}")] + UuidErr { error: uuid::Error, location: String }, #[error("SqlErr: {error:#} @{location:#}")] SqlErr { error: sqlx::Error, location: String }, + #[error("SerdeJson: {error:#} @{location:#}")] + SerdeJson { error: serde_json::Error, location: String }, #[error("Bad request: {0}")] BadRequest(String), #[error("Quota exceeded: {0}")] @@ -54,12 +56,12 @@ pub enum Error { InternalErr(String), #[error("Internal: {0}: {1}")] InternalErrAt(&'static Location<'static>, String), - #[error("Hexadecimal decoding error: {0}")] - HexErr(#[from] hex::FromHexError), + #[error("HexErr: {error:#} @{location:#}")] + HexErr { error: hex::FromHexError, location: String }, #[error("Migrating database: {0}")] DatabaseMigration(#[from] MigrateError), - #[error("Non-zero exit status: {0}")] - ExitStatus(i32), + #[error("Non-zero exit status for {0}: {1}")] + ExitStatus(String, i32), #[error("Error: {error:#} @{location:#}")] Anyhow { error: anyhow::Error, location: String }, #[error("Error: {0:#?}")] @@ -70,10 +72,6 @@ pub enum Error { AlreadyCompleted(String), #[error("Find python error: {0}")] FindPythonError(String), - #[error("{0}")] - Utf8(#[from] std::string::FromUtf8Error), - #[error("Encoding/decoding error: {0}")] - SerdeJson(#[from] serde_json::Error), } fn prettify_location(location: &'static Location<'static>) -> String { @@ -92,11 +90,47 @@ impl From for Error { } impl From for Error { + #[track_caller] fn from(e: sqlx::Error) -> Self { Self::SqlErr { error: e, location: prettify_location(std::panic::Location::caller()) } } } +impl From for Error { + #[track_caller] + fn from(e: uuid::Error) -> Self { + Self::UuidErr { error: e, location: prettify_location(std::panic::Location::caller()) } + } +} + +impl From for Error { + #[track_caller] + fn from(e: std::string::FromUtf8Error) -> Self { + Self::Utf8Err { error: e, location: prettify_location(std::panic::Location::caller()) } + } +} + +impl From for Error { + #[track_caller] + fn from(e: io::Error) -> Self { + Self::IoErr { error: e, location: prettify_location(std::panic::Location::caller()) } + } +} + +impl From for Error { + #[track_caller] + fn from(e: hex::FromHexError) -> Self { + Self::HexErr { error: e, location: prettify_location(std::panic::Location::caller()) } + } +} + +impl From for Error { + #[track_caller] + fn from(e: serde_json::Error) -> Self { + Self::SerdeJson { error: e, location: prettify_location(std::panic::Location::caller()) } + } +} + impl Error { /// https://docs.rs/anyhow/1/anyhow/struct.Error.html#display-representations pub fn alt(&self) -> String { diff --git a/backend/windmill-worker/src/common.rs b/backend/windmill-worker/src/common.rs index 099300f621..5212d80e11 100644 --- a/backend/windmill-worker/src/common.rs +++ b/backend/windmill-worker/src/common.rs @@ -570,7 +570,7 @@ impl OccupancyMetrics { pub async fn start_child_process(mut cmd: Command, executable: &str) -> Result { return cmd .spawn() - .map_err(|err| tentatively_improve_error(Error::IoErr(err), executable)); + .map_err(|err| tentatively_improve_error(err.into(), executable)); } pub async fn resolve_job_timeout( diff --git a/backend/windmill-worker/src/dedicated_worker.rs b/backend/windmill-worker/src/dedicated_worker.rs index 56ea01c038..ff6b8cec79 100644 --- a/backend/windmill-worker/src/dedicated_worker.rs +++ b/backend/windmill-worker/src/dedicated_worker.rs @@ -78,7 +78,7 @@ pub async fn handle_dedicated_process( //do not cache local dependencies use crate::{handle_child::process_status, PROXY_ENVS}; - + let cmd_name = format!("dedicated {command_path}"); let mut child = { let mut cmd = Command::new(command_path); cmd.current_dir(job_dir) @@ -126,7 +126,7 @@ pub async fn handle_dedicated_process( .wait() .await .expect("child process encountered an error"); - if let Err(e) = process_status(status) { + if let Err(e) = process_status(&cmd_name, status) { tracing::error!("child exit status was not success: {e:#}"); } else { tracing::info!("child exit status was success"); diff --git a/backend/windmill-worker/src/handle_child.rs b/backend/windmill-worker/src/handle_child.rs index 5dcdbfa287..32ec33f7f3 100644 --- a/backend/windmill-worker/src/handle_child.rs +++ b/backend/windmill-worker/src/handle_child.rs @@ -426,7 +426,7 @@ pub async fn handle_child( _ if *too_many_logs.borrow() => Err(Error::ExecutionErr(format!( "logs or result reached limit. (current max size: {MAX_RESULT_SIZE} characters)" ))), - Ok(Ok(status)) => process_status(status), + Ok(Ok(status)) => process_status(&child_name, status), Ok(Err(kill_reason)) => match kill_reason { KillReason::AlreadyCompleted => { Err(Error::AlreadyCompleted("Job already completed".to_string())) @@ -714,11 +714,11 @@ pub fn lines_to_stream( }) } -pub fn process_status(status: ExitStatus) -> error::Result<()> { +pub fn process_status(program: &str, status: ExitStatus) -> error::Result<()> { if status.success() { Ok(()) } else if let Some(code) = status.code() { - Err(error::Error::ExitStatus(code)) + Err(error::Error::ExitStatus(program.to_string(), code)) } else { #[cfg(any(target_os = "linux", target_os = "macos"))] return Err(error::Error::ExecutionErr(format!( diff --git a/backend/windmill-worker/src/result_processor.rs b/backend/windmill-worker/src/result_processor.rs index f4acc66f29..bb516cfda2 100644 --- a/backend/windmill-worker/src/result_processor.rs +++ b/backend/windmill-worker/src/result_processor.rs @@ -331,7 +331,7 @@ pub async fn process_result( } Err(e) => { let error_value = match e { - Error::ExitStatus(i) => { + Error::ExitStatus(program, i) => { let res = read_result(job_dir).await.ok(); if res.as_ref().is_some_and(|x| !x.get().is_empty()) { @@ -348,7 +348,7 @@ pub async fn process_result( .last() .unwrap_or(&last_10_log_lines); - extract_error_value(log_lines, i, job.flow_step_id.clone()) + extract_error_value(&program, log_lines, i, job.flow_step_id.clone()) } } err @ _ => to_raw_value(&SerializedError { @@ -662,10 +662,15 @@ pub struct SerializedError { #[serde(skip_serializing_if = "Option::is_none")] pub exit_code: Option, } -pub fn extract_error_value(log_lines: &str, i: i32, step_id: Option) -> Box { +pub fn extract_error_value( + program: &str, + log_lines: &str, + i: i32, + step_id: Option, +) -> Box { return to_raw_value(&SerializedError { message: format!( - "ExitCode: {i}, last log lines:\n{}", + "exit code for \"{program}\": {i}, last log lines:\n{}", ANSI_ESCAPE_RE.replace_all(log_lines.trim(), "").to_string() ), name: "ExecutionErr".to_string(), From 90ba65ae20c66e7bf5fa67c43ff417e19bc1b9aa Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 5 Feb 2025 16:29:56 +0100 Subject: [PATCH 11/27] improve error messages for internal err --- backend/windmill-api/src/ai.rs | 22 ++--- backend/windmill-api/src/apps.rs | 17 ++-- backend/windmill-api/src/capture.rs | 2 +- .../windmill-api/src/concurrency_groups.rs | 4 +- backend/windmill-api/src/embeddings.rs | 4 +- backend/windmill-api/src/flows.rs | 22 ++--- backend/windmill-api/src/folders.rs | 2 +- backend/windmill-api/src/http_triggers.rs | 8 +- backend/windmill-api/src/job_helpers_ee.rs | 10 +- backend/windmill-api/src/jobs.rs | 28 +++--- .../src/postgres_triggers/handler.rs | 6 +- backend/windmill-api/src/raw_apps.rs | 4 +- backend/windmill-api/src/resources.rs | 8 +- backend/windmill-api/src/schedule.rs | 6 +- backend/windmill-api/src/scripts.rs | 14 +-- backend/windmill-api/src/service_logs.rs | 6 +- backend/windmill-api/src/settings.rs | 8 +- backend/windmill-api/src/users.rs | 6 +- backend/windmill-api/src/users_ee.rs | 4 +- backend/windmill-api/src/utils.rs | 4 +- backend/windmill-api/src/variables.rs | 6 +- .../windmill-api/src/websocket_triggers.rs | 2 +- backend/windmill-api/src/workspaces.rs | 16 ++-- backend/windmill-api/src/workspaces_ee.rs | 2 +- backend/windmill-api/src/workspaces_export.rs | 6 +- backend/windmill-common/src/auth.rs | 6 +- backend/windmill-common/src/cache.rs | 16 ++-- backend/windmill-common/src/error.rs | 14 ++- backend/windmill-common/src/flows.rs | 4 +- backend/windmill-common/src/jobs.rs | 2 +- backend/windmill-common/src/s3_helpers.rs | 4 +- backend/windmill-common/src/scripts.rs | 2 +- backend/windmill-common/src/variables.rs | 6 +- backend/windmill-queue/src/jobs.rs | 64 ++++++------- backend/windmill-queue/src/schedule.rs | 2 +- backend/windmill-worker/src/bun_executor.rs | 12 +-- backend/windmill-worker/src/common.rs | 16 ++-- .../windmill-worker/src/dedicated_worker.rs | 4 +- backend/windmill-worker/src/deno_executor.rs | 4 +- backend/windmill-worker/src/mysql_executor.rs | 2 +- .../windmill-worker/src/oracledb_executor.rs | 2 +- .../windmill-worker/src/python_executor.rs | 6 +- backend/windmill-worker/src/worker.rs | 39 ++++---- backend/windmill-worker/src/worker_flow.rs | 91 ++++++++++--------- .../windmill-worker/src/worker_lockfiles.rs | 30 +++--- 45 files changed, 283 insertions(+), 260 deletions(-) diff --git a/backend/windmill-api/src/ai.rs b/backend/windmill-api/src/ai.rs index ba055d27de..d77943df9d 100644 --- a/backend/windmill-api/src/ai.rs +++ b/backend/windmill-api/src/ai.rs @@ -142,7 +142,7 @@ mod openai { tracing::debug!("Adding user to request body"); let mut json_body: HashMap> = serde_json::from_slice(&body) .map_err(|e| { - Error::InternalErr(format!("Failed to parse request body: {}", e)) + Error::internal_err(format!("Failed to parse request body: {}", e)) })?; let user_json_string = serde_json::Value::String(user.unwrap()).to_string(); // makes sure to escape characters @@ -150,12 +150,12 @@ mod openai { json_body.insert( "user".to_string(), RawValue::from_string(user_json_string) - .map_err(|e| Error::InternalErr(format!("Failed to parse user: {}", e)))?, + .map_err(|e| Error::internal_err(format!("Failed to parse user: {}", e)))?, ); body = serde_json::to_vec(&json_body) .map_err(|e| { - Error::InternalErr(format!("Failed to reserialize request body: {}", e)) + Error::internal_err(format!("Failed to reserialize request body: {}", e)) })? .into(); } @@ -204,13 +204,13 @@ mod openai { .send() .await .map_err(|err| { - Error::InternalErr(format!( + Error::internal_err(format!( "Failed to get OpenAI credentials using credentials flow: {}", err )) })?; let response = response.json::().await.map_err(|err| { - Error::InternalErr(format!( + Error::internal_err(format!( "Failed to parse OpenAI credentials from credentials flow: {}", err )) @@ -220,7 +220,7 @@ mod openai { pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result { let config = serde_json::from_value(resource) - .map_err(|e| Error::InternalErr(format!("validating openai resource {e:#}")))?; + .map_err(|e| Error::internal_err(format!("validating openai resource {e:#}")))?; let mut user = None::; let mut resource = match config { @@ -257,7 +257,7 @@ mod openai { let azure_base_path = if let Some(azure_base_path) = azure_base_path { Some( serde_json::from_value::(azure_base_path).map_err(|e| { - Error::InternalErr(format!("validating openai azure base path {e:#}")) + Error::internal_err(format!("validating openai azure base path {e:#}")) })?, ) } else { @@ -303,7 +303,7 @@ mod anthropic { pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result { let mut resource: AnthropicCache = serde_json::from_value(resource) - .map_err(|e| Error::InternalErr(format!("validating anthropic resource {e:#}")))?; + .map_err(|e| Error::internal_err(format!("validating anthropic resource {e:#}")))?; resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?; Ok(KeyCache::Anthropic(resource)) } @@ -335,7 +335,7 @@ mod mistral { pub async fn get_cached_value(db: &DB, w_id: &str, resource: Value) -> Result { let mut resource: MistralCache = serde_json::from_value(resource) - .map_err(|e| Error::InternalErr(format!("validating mistral resource {e:#}")))?; + .map_err(|e| Error::internal_err(format!("validating mistral resource {e:#}")))?; resource.api_key = get_variable_or_self(resource.api_key, db, w_id).await?; Ok(KeyCache::Mistral(resource)) } @@ -472,7 +472,7 @@ async fn proxy( .await?; if ai_resource.is_none() { - return Err(Error::InternalErr("AI resource not configured".to_string())); + return Err(Error::internal_err("AI resource not configured".to_string())); } let ai_resource = serde_json::from_value::(ai_resource.unwrap()) @@ -497,7 +497,7 @@ async fn proxy( }; if resource.is_none() { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "{:?} resource missing value", ai_provider ))); diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index bf134adc7f..bcfc72b4e0 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -360,7 +360,7 @@ async fn list_apps( .fields(&["dm.deployment_msg"]); } - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; let rows = sqlx::query_as::<_, ListableApp>(&sql) .fetch_all(&mut *tx) @@ -611,7 +611,7 @@ async fn get_public_app_by_secret( let decrypted = mc .decrypt_bytes_to_bytes(&(hex::decode(secret)?)) - .map_err(|e| Error::InternalErr(e.to_string()))?; + .map_err(|e| Error::internal_err(e.to_string()))?; let bytes = str::from_utf8(&decrypted).map_err(to_anyhow)?; let id: i64 = bytes.parse().map_err(to_anyhow)?; @@ -958,7 +958,7 @@ async fn delete_app( .execute(&db) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error deleting deployment metadata for script with path {path} in workspace {w_id}: {e:#}" )) })?; @@ -1061,7 +1061,7 @@ async fn update_app( sqlb.returning("path"); - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let npath_o: Option = sqlx::query_scalar(&sql).fetch_optional(&mut *tx).await?; not_found_if_none(npath_o, "App", path)? } else { @@ -1710,7 +1710,7 @@ async fn upload_s3_file_from_app( } }; - let s3_resource = s3_resource_opt.ok_or(Error::InternalErr( + let s3_resource = s3_resource_opt.ok_or(Error::internal_err( "No files storage resource defined at the workspace level".to_string(), ))?; let s3_client = build_object_store_client(&s3_resource).await?; @@ -2034,7 +2034,10 @@ async fn build_args( safe_args.insert( k.to_string(), to_raw_value(&value.unwrap_or(Ok(serde_json::Value::Null)).map_err(|e| { - Error::InternalErr(format!("failed to serialize ctx variable for {}: {}", k, e)) + Error::internal_err(format!( + "failed to serialize ctx variable for {}: {}", + k, e + )) })?), ); } else if !arg_str.contains("\"$var:") && !arg_str.contains("\"$res:") { @@ -2054,7 +2057,7 @@ async fn build_args( ), ) .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "failed to remove sensitive variable(s)/resource(s) with error: {}", e )) diff --git a/backend/windmill-api/src/capture.rs b/backend/windmill-api/src/capture.rs index fe752336f3..4b933b01b4 100644 --- a/backend/windmill-api/src/capture.rs +++ b/backend/windmill-api/src/capture.rs @@ -412,7 +412,7 @@ async fn get_capture_trigger_config_and_owner( Ok(( serde_json::from_str(trigger_config.get()).map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error parsing capture config for {} trigger: {}", kind, e )) diff --git a/backend/windmill-api/src/concurrency_groups.rs b/backend/windmill-api/src/concurrency_groups.rs index 945df75573..6a414d0a0c 100644 --- a/backend/windmill-api/src/concurrency_groups.rs +++ b/backend/windmill-api/src/concurrency_groups.rs @@ -16,7 +16,7 @@ use sql_builder::bind::Bind; use sql_builder::SqlBuilder; use uuid::Uuid; use windmill_common::db::UserDB; -use windmill_common::error::Error::{InternalErr, PermissionDenied}; +use windmill_common::error::Error::PermissionDenied; use windmill_common::error::{self, JsonResult}; use windmill_common::utils::require_admin; @@ -82,7 +82,7 @@ async fn prune_concurrency_group( if n_job_uuids > 0 { tx.commit().await?; - return Err(InternalErr( + return Err(error::Error::internal_err( "Concurrency group is currently in use, unable to remove it. Retry later.".to_string(), )); } diff --git a/backend/windmill-api/src/embeddings.rs b/backend/windmill-api/src/embeddings.rs index fd222bdbfa..30415156ce 100644 --- a/backend/windmill-api/src/embeddings.rs +++ b/backend/windmill-api/src/embeddings.rs @@ -90,7 +90,7 @@ async fn query_hub_scripts( Ok(Json(results)) } else { - Err(windmill_common::error::Error::InternalErr( + Err(windmill_common::error::Error::internal_err( "Embeddings db not initialized".to_string(), )) } @@ -124,7 +124,7 @@ async fn query_resource_types( Ok(Json(results)) } else { - Err(windmill_common::error::Error::InternalErr( + Err(windmill_common::error::Error::internal_err( "Embeddings db not initialized".to_string(), )) } diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index 1b283d43f3..38ce53069a 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -187,7 +187,7 @@ async fn list_flows( .fields(&["dm.deployment_msg"]); } - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; let rows = sqlx::query_as::<_, ListableFlow>(&sql) .fetch_all(&mut *tx) @@ -704,7 +704,7 @@ async fn update_flow( w_id, ) .execute(&mut *tx) - .await.map_err(|e| error::Error::InternalErr(format!("Error updating flow due to flow update: {e:#}")))?; + .await.map_err(|e| error::Error::internal_err(format!("Error updating flow due to flow update: {e:#}")))?; if is_new_path { // if new path, must clone flow to new path and delete old flow for flow_version foreign key constraint @@ -721,7 +721,7 @@ async fn update_flow( .execute(&mut *tx) .await .map_err(|e| { - error::Error::InternalErr(format!("Error updating flow due to create new flow: {e:#}")) + error::Error::internal_err(format!("Error updating flow due to create new flow: {e:#}")) })?; sqlx::query!( @@ -733,7 +733,7 @@ async fn update_flow( .execute(&mut *tx) .await .map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "Error updating flow due to updating flow history path: {e:#}" )) })?; @@ -746,7 +746,7 @@ async fn update_flow( .execute(&mut *tx) .await .map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "Error updating flow due to deleting old flow: {e:#}" )) })?; @@ -781,7 +781,7 @@ async fn update_flow( .fetch_one(&mut *tx) .await .map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "Error updating flow due to flow history insert: {e:#}" )) })?; @@ -805,7 +805,7 @@ async fn update_flow( .bind(&flow_path) .bind(&w_id) .fetch_all(&mut *tx) - .await.map_err(|e| error::Error::InternalErr(format!("Error updating flow due to related schedules update: {e:#}")))?; + .await.map_err(|e| error::Error::internal_err(format!("Error updating flow due to related schedules update: {e:#}")))?; let schedule = sqlx::query_as::<_, Schedule>( "UPDATE schedule SET path = $1, script_path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow IS true RETURNING *") @@ -813,7 +813,7 @@ async fn update_flow( .bind(&flow_path) .bind(&w_id) .fetch_optional(&mut *tx) - .await.map_err(|e| error::Error::InternalErr(format!("Error updating flow due to related schedule update: {e:#}")))?; + .await.map_err(|e| error::Error::internal_err(format!("Error updating flow due to related schedule update: {e:#}")))?; if let Some(schedule) = schedule { clear_schedule(&mut tx, &flow_path, &w_id).await?; @@ -907,7 +907,7 @@ async fn update_flow( .execute(&mut *new_tx) .await .map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "Error updating flow due to updating dependency job field: {e:#}" )) })?; @@ -919,7 +919,7 @@ async fn update_flow( .execute(&mut *new_tx) .await .map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "Error updating flow due to cancelling dependency job: {e:#}" )) })?; @@ -1204,7 +1204,7 @@ async fn delete_flow_by_path( .execute(&db) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error deleting deployment metadata for script with path {path} in workspace {w_id}: {e:#}" )) })?; diff --git a/backend/windmill-api/src/folders.rs b/backend/windmill-api/src/folders.rs index 208ec63985..cab039c64f 100644 --- a/backend/windmill-api/src/folders.rs +++ b/backend/windmill-api/src/folders.rs @@ -340,7 +340,7 @@ async fn update_folder( let sql = sqlb .sql() - .map_err(|e| error::Error::InternalErr(e.to_string()))?; + .map_err(|e| error::Error::internal_err(e.to_string()))?; let nfolder = sqlx::query_as::<_, Folder>(&sql) .fetch_optional(&mut *tx) .await?; diff --git a/backend/windmill-api/src/http_triggers.rs b/backend/windmill-api/src/http_triggers.rs index ef62928f2c..d0a0b43698 100644 --- a/backend/windmill-api/src/http_triggers.rs +++ b/backend/windmill-api/src/http_triggers.rs @@ -176,7 +176,7 @@ async fn list_triggers( } let sql = sqlb .sql() - .map_err(|e| error::Error::InternalErr(e.to_string()))?; + .map_err(|e| error::Error::internal_err(e.to_string()))?; let rows = sqlx::query_as::<_, Trigger>(&sql) .fetch_all(&mut *tx) .await?; @@ -590,7 +590,7 @@ async fn route_job( #[cfg(not(feature = "parquet"))] if trigger.static_asset_config.is_some() { - return error::Error::InternalErr( + return error::Error::internal_err( "Static asset configuration is not supported in this build".to_string(), ) .into_response(); @@ -608,14 +608,14 @@ async fn route_job( config.storage, ) .await?; - let s3_resource = s3_resource_opt.ok_or(error::Error::InternalErr( + let s3_resource = s3_resource_opt.ok_or(error::Error::internal_err( "No files storage resource defined at the workspace level".to_string(), ))?; let s3_client = build_object_store_client(&s3_resource).await?; let path = object_store::path::Path::from(config.s3); let s3_object = s3_client.get(&path).await.map_err(|err| { tracing::warn!("Error retrieving file from S3: {:?}", err); - error::Error::InternalErr(format!("Error retrieving file: {}", err.to_string())) + error::Error::internal_err(format!("Error retrieving file: {}", err.to_string())) })?; let mut response_headers = http::HeaderMap::new(); if let Some(ref e_tag) = s3_object.meta.e_tag { diff --git a/backend/windmill-api/src/job_helpers_ee.rs b/backend/windmill-api/src/job_helpers_ee.rs index 7b074a300a..b35f40d661 100644 --- a/backend/windmill-api/src/job_helpers_ee.rs +++ b/backend/windmill-api/src/job_helpers_ee.rs @@ -69,7 +69,7 @@ pub async fn get_s3_resource<'c>( _resource_type: Option, _job_id: Option, ) -> error::Result { - Err(error::Error::InternalErr( + Err(error::Error::internal_err( "Not implemented in Windmill's Open Source repository".to_string(), )) } @@ -81,7 +81,7 @@ pub async fn upload_file_from_req( _req: axum::extract::Request, _options: PutMultipartOpts, ) -> error::Result<()> { - Err(error::Error::InternalErr( + Err(error::Error::internal_err( "Not implemented in Windmill's Open Source repository".to_string(), )) } @@ -93,7 +93,7 @@ pub async fn upload_file_internal( _stream: impl Stream> + Unpin, _options: PutMultipartOpts, ) -> error::Result<()> { - Err(error::Error::InternalErr( + Err(error::Error::internal_err( "Not implemented in Windmill's Open Source repository".to_string(), )) } @@ -107,7 +107,7 @@ pub async fn download_s3_file_internal( _w_id: &str, _query: DownloadFileQuery, ) -> error::Result { - Err(error::Error::InternalErr( + Err(error::Error::internal_err( "Not implemented in Windmill's Open Source repository".to_string(), )) } @@ -120,7 +120,7 @@ pub async fn load_image_preview_internal( _w_id: &str, _query: LoadImagePreviewQuery, ) -> error::Result { - Err(error::Error::InternalErr( + Err(error::Error::internal_err( "Not implemented in Windmill's Open Source repository".to_string(), )) } diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index f039c220fb..4e477ab891 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -384,7 +384,7 @@ async fn cancel_job_api( ) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "timeout after 120s while cancelling job {id} in {w_id}: {e:#}" )) })??; @@ -495,7 +495,7 @@ async fn force_cancel( ) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "timeout after 120s while cancelling job {id} in {w_id}: {e:#}" )) })??; @@ -554,7 +554,7 @@ pub async fn get_path_tag_limits_cache_for_hash( .fetch_optional(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "querying getting path for hash {hash} in {w_id}: {e:#}" )) })?.ok_or_else(|| Error::NotFound(format!( @@ -3682,10 +3682,10 @@ pub async fn run_wait_result( if let Some(windmill_headers) = windmill_headers { for (k, v) in windmill_headers { let k = HeaderName::from_str(k.as_str()).map_err(|err| { - Error::InternalErr(format!("Invalid header name {k}: {err}")) + Error::internal_err(format!("Invalid header name {k}: {err}")) })?; let v = HeaderValue::from_str(v.as_str()).map_err(|err| { - Error::InternalErr(format!("Invalid header value {v}: {err}")) + Error::internal_err(format!("Invalid header value {v}: {err}")) })?; headers.insert(k, v); } @@ -3704,7 +3704,7 @@ pub async fn run_wait_result( headers.insert( http::header::CONTENT_TYPE, HeaderValue::from_str(content_type.as_str()).map_err(|err| { - Error::InternalErr(format!("Invalid content type {content_type}: {err}")) + Error::internal_err(format!("Invalid content type {content_type}: {err}")) })?, ); return Ok((status_code_or_default, headers, serialized_result).into_response()); @@ -3760,7 +3760,7 @@ pub async fn check_queue_too_long(db: &DB, queue_limit: Option) -> error::R .unwrap_or(0); if count > queue_limit.unwrap() { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "Number of queued job is too high: {count} > {limit}" ))); } @@ -3872,7 +3872,7 @@ pub async fn run_wait_result_job_by_path_get( return Ok(Json(serde_json::json!("")).into_response()); } let payload_r = run_query.payload.map(decode_payload).map(|x| { - x.map_err(|e| Error::InternalErr(format!("Impossible to decode query payload: {e:#?}"))) + x.map_err(|e| Error::internal_err(format!("Impossible to decode query payload: {e:#?}"))) }); let mut payload_args = if let Some(payload) = payload_r { @@ -3967,7 +3967,7 @@ pub async fn run_wait_result_flow_by_path_get( } let payload_r = run_query.payload.clone().map(decode_payload).map(|x| { x.map_err(|e| { - error::Error::InternalErr(format!("Impossible to decode query payload: {e:#?}")) + error::Error::internal_err(format!("Impossible to decode query payload: {e:#?}")) }) }); @@ -4551,7 +4551,7 @@ async fn run_dependencies_job( } if req.raw_scripts.len() != 1 || req.raw_scripts[0].script_path != req.entrypoint { - return Err(error::Error::InternalErr( + return Err(error::Error::internal_err( "For now only a single raw script can be passed to this endpoint, and the entrypoint should be set to the script path".to_string(), )); } @@ -4790,10 +4790,10 @@ async fn add_batch_jobs( ) .fetch_optional(&mut *tx) .await? - .ok_or_else(|| Error::InternalErr(format!("not found flow at path {:?}", path)))?; + .ok_or_else(|| Error::internal_err(format!("not found flow at path {:?}", path)))?; let value = serde_json::from_str::(value_json.value.get()).map_err(|err| { - Error::InternalErr(format!( + Error::internal_err(format!( "could not convert json to flow for {path}: {err:?}" )) })?; @@ -5138,7 +5138,7 @@ async fn get_log_file(Path((_w_id, file_p)): Path<(String, String)>) -> error::R .unwrap(); return Ok(res); } else { - return Err(error::Error::InternalErr(format!( + return Err(error::Error::internal_err(format!( "Error getting bytes from file: {}", file_p ))); @@ -5150,7 +5150,7 @@ async fn get_log_file(Path((_w_id, file_p)): Path<(String, String)>) -> error::R ))); } } else { - return Err(error::Error::InternalErr(format!( + return Err(error::Error::internal_err(format!( "Object store client not present and file not found on server logs volume at {local_file}" ))); } diff --git a/backend/windmill-api/src/postgres_triggers/handler.rs b/backend/windmill-api/src/postgres_triggers/handler.rs index 5bcfb69acc..ad23938e5e 100644 --- a/backend/windmill-api/src/postgres_triggers/handler.rs +++ b/backend/windmill-api/src/postgres_triggers/handler.rs @@ -428,17 +428,17 @@ pub async fn list_postgres_triggers( } let sql = sqlb .sql() - .map_err(|e| error::Error::InternalErr(e.to_string()))?; + .map_err(|e| error::Error::internal_err(e.to_string()))?; let rows = sqlx::query_as::<_, PostgresTrigger>(&sql) .fetch_all(&mut *tx) .await .map_err(|e| { tracing::debug!("Error fetching postgres_trigger: {:#?}", e); - windmill_common::error::Error::InternalErr("server error".to_string()) + windmill_common::error::Error::internal_err("server error".to_string()) })?; tx.commit().await.map_err(|e| { tracing::debug!("Error commiting postgres_trigger: {:#?}", e); - windmill_common::error::Error::InternalErr("server error".to_string()) + windmill_common::error::Error::internal_err("server error".to_string()) })?; Ok(Json(rows)) diff --git a/backend/windmill-api/src/raw_apps.rs b/backend/windmill-api/src/raw_apps.rs index 59f2471c4d..bb2763b73d 100644 --- a/backend/windmill-api/src/raw_apps.rs +++ b/backend/windmill-api/src/raw_apps.rs @@ -110,7 +110,7 @@ async fn list_apps( sqlb.and_where_eq("app.path", "?".bind(path_exact)); } - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; let rows = sqlx::query_as::<_, ListableApp>(&sql) .fetch_all(&mut *tx) @@ -293,7 +293,7 @@ async fn update_app( sqlb.returning("path"); - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let npath_o: Option = sqlx::query_scalar(&sql).fetch_optional(&mut *tx).await?; not_found_if_none(npath_o, "Raw App", path)?; diff --git a/backend/windmill-api/src/resources.rs b/backend/windmill-api/src/resources.rs index 88f8d79c29..564f9977b2 100644 --- a/backend/windmill-api/src/resources.rs +++ b/backend/windmill-api/src/resources.rs @@ -262,7 +262,7 @@ async fn list_resources( sqlb.and_where_like_left("resource.path", path_start); } - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; let rows = sqlx::query_as::<_, ListableResource>(&sql) .fetch_all(&mut *tx) @@ -516,7 +516,7 @@ pub async fn transform_json_value<'c>( Value::String(y) if y.starts_with("$res:") => { let path = y.strip_prefix("$res:").unwrap(); if path.split("/").count() < 2 { - return Err(Error::InternalErr(format!("Invalid resource path: {path}"))); + return Err(Error::internal_err(format!("Invalid resource path: {path}"))); } let mut tx: Transaction<'_, Postgres> = authed_transaction_or_default(authed, user_db.clone(), db).await?; @@ -815,7 +815,7 @@ async fn update_resource( } } - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let npath_o: Option = sqlx::query_scalar(&sql).fetch_optional(&mut *tx).await?; let npath = not_found_if_none(npath_o, "Resource", path)?; @@ -1165,7 +1165,7 @@ async fn update_resource_type( sqlb.set_str("description", ndesc); } sqlb.set_str("edited_at", "now()"); - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; sqlx::query(&sql).execute(&mut *tx).await?; diff --git a/backend/windmill-api/src/schedule.rs b/backend/windmill-api/src/schedule.rs index 79e8c4dcb4..5b22499ca9 100644 --- a/backend/windmill-api/src/schedule.rs +++ b/backend/windmill-api/src/schedule.rs @@ -196,7 +196,7 @@ async fn create_schedule( .bind(&ns.cron_version.unwrap_or("v2".to_string())) .fetch_one(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("inserting schedule in {w_id}: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("inserting schedule in {w_id}: {e:#}")))?; handle_deployment_metadata( &authed.email, @@ -282,7 +282,7 @@ async fn edit_schedule( .bind(&es.cron_version) .fetch_one(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("updating schedule in {w_id}: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("updating schedule in {w_id}: {e:#}")))?; handle_deployment_metadata( &authed.email, @@ -356,7 +356,7 @@ async fn list_schedule( if let Some(path_start) = &lsq.path_start { sqlb.and_where_like_left("path", path_start); } - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let rows = sqlx::query_as::<_, Schedule>(&sql) .fetch_all(&mut *tx) .await?; diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index 10ac5aa387..cbeb13e106 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -311,7 +311,7 @@ async fn list_scripts( .fields(&["dm.deployment_msg"]); } - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let mut tx = user_db.begin(&authed).await?; let rows = sqlx::query_as::<_, ListableScript>(&sql) .fetch_all(&mut *tx) @@ -1337,7 +1337,7 @@ async fn archive_script_by_path( ) .fetch_one(&db) .await - .map_err(|e| Error::InternalErr(format!("archiving script in {w_id}: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("archiving script in {w_id}: {e:#}")))?; audit_log( &mut *tx, &authed, @@ -1387,7 +1387,7 @@ async fn archive_script_by_hash( .bind(&hash.0) .fetch_one(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("archiving script in {w_id}: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("archiving script in {w_id}: {e:#}")))?; audit_log( &mut *tx, @@ -1427,7 +1427,7 @@ async fn delete_script_by_hash( .bind(&w_id) .fetch_one(&db) .await - .map_err(|e| Error::InternalErr(format!("deleting script by hash {w_id}: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("deleting script by hash {w_id}: {e:#}")))?; audit_log( &mut *tx, @@ -1487,7 +1487,7 @@ async fn delete_script_by_path( ) .fetch_one(&db) .await - .map_err(|e| Error::InternalErr(format!("deleting script by path {w_id}: {e:#}")))? + .map_err(|e| Error::internal_err(format!("deleting script by path {w_id}: {e:#}")))? } else { // If the script is draft only, we can delete it without admin permissions but we still need write permissions sqlx::query_scalar!( @@ -1497,7 +1497,7 @@ async fn delete_script_by_path( ) .fetch_one(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("deleting script by path {w_id}: {e:#}")))? + .map_err(|e| Error::internal_err(format!("deleting script by path {w_id}: {e:#}")))? }; sqlx::query!( @@ -1559,7 +1559,7 @@ async fn delete_script_by_path( .execute(&db) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error deleting deployment metadata for script with path {path} in workspace {w_id}: {e:#}" )) })?; diff --git a/backend/windmill-api/src/service_logs.rs b/backend/windmill-api/src/service_logs.rs index 277a08bca9..b11646fbe0 100644 --- a/backend/windmill-api/src/service_logs.rs +++ b/backend/windmill-api/src/service_logs.rs @@ -83,7 +83,7 @@ async fn list_files( if let Some(true) = lq.with_error { sqlb.and_where("err_lines > 0"); } - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let rows = sqlx::query_as::<_, LogFile>(&sql).fetch_all(&db).await?; Ok(Json(rows)) } @@ -114,7 +114,7 @@ async fn get_log_file( return Ok(content_plain(Body::from(bytes::Bytes::from(bytes)))); } Err(e) => { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "Error pulling the bytes: {}", e ))); @@ -122,7 +122,7 @@ async fn get_log_file( } } Err(e) => { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "Error fetching the file: {}", e ))); diff --git a/backend/windmill-api/src/settings.rs b/backend/windmill-api/src/settings.rs index c527eb481a..37a33ee81a 100644 --- a/backend/windmill-api/src/settings.rs +++ b/backend/windmill-api/src/settings.rs @@ -132,7 +132,7 @@ pub async fn test_s3_bucket( if first_file.is_some() { if let Err(e) = first_file.as_ref().unwrap() { tracing::error!("error listing bucket: {e:#}"); - error::Error::InternalErr(format!("Failed to list files in blob storage: {e:#}")); + error::Error::internal_err(format!("Failed to list files in blob storage: {e:#}")); } tracing::info!("Listed files: {:?}", first_file.unwrap()); } else { @@ -156,7 +156,7 @@ pub async fn test_s3_bucket( .await .map_err(to_anyhow)?; if content != Bytes::from_static(b"hello") { - return Err(error::Error::InternalErr( + return Err(error::Error::internal_err( "Failed to read back from blob storage".to_string(), )); } @@ -232,7 +232,7 @@ pub async fn set_global_setting_internal( generate_instance_username_for_all_users(db) .await .map_err(|err| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "Failed to generate instance wide usernames: {}", err )) @@ -349,7 +349,7 @@ pub async fn get_latest_key_renewal_attempt( Some(last_attempt) => { let last_attempt_result = serde_json::from_value::(last_attempt.value) .map_err(|e| { - error::Error::InternalErr(format!("Failed to parse last attempt: {}", e)) + error::Error::internal_err(format!("Failed to parse last attempt: {}", e)) })?; Ok(Json(Some(KeyRenewalAttempt { result: last_attempt_result, diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index c0159480e7..2424c5e35f 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -487,7 +487,7 @@ async fn list_user_usage( .fetch_all(&mut *tx), ) .await - .map_err(|e| Error::InternalErr(format!("Timed out while fetching user usage: {e:#}")))??; + .map_err(|e| Error::internal_err(format!("Timed out while fetching user usage: {e:#}")))??; tx.commit().await?; Ok(Json(rows)) } @@ -695,7 +695,7 @@ async fn global_whoami( ) .fetch_one(&db) .await - .map_err(|e| Error::InternalErr(format!("fetching global identity: {e:#}"))); + .map_err(|e| Error::internal_err(format!("fetching global identity: {e:#}"))); if let Ok(user) = user { Ok(Json(user)) @@ -1591,7 +1591,7 @@ async fn login( if let Some((email, hash, super_admin, first_time_user)) = email_w_h { let parsed_hash = - PasswordHash::new(&hash).map_err(|e| Error::InternalErr(e.to_string()))?; + PasswordHash::new(&hash).map_err(|e| Error::internal_err(e.to_string()))?; if argon2 .verify_password(password.as_bytes(), &parsed_hash) .is_err() diff --git a/backend/windmill-api/src/users_ee.rs b/backend/windmill-api/src/users_ee.rs index 0e20d5bf84..7a11239a2f 100644 --- a/backend/windmill-api/src/users_ee.rs +++ b/backend/windmill-api/src/users_ee.rs @@ -17,7 +17,7 @@ pub async fn create_user( _argon2: Arc>, mut _nu: NewUser, ) -> Result<(StatusCode, String)> { - Err(Error::InternalErr( + Err(Error::internal_err( "Not implemented in Windmill's Open Source repository".to_string(), )) } @@ -29,7 +29,7 @@ pub async fn set_password( _user_email: &str, _ep: EditPassword, ) -> Result { - Err(Error::InternalErr( + Err(Error::internal_err( "Not implemented in Windmill's Open Source repository".to_string(), )) } diff --git a/backend/windmill-api/src/utils.rs b/backend/windmill-api/src/utils.rs index 5e7adde1e6..439944e2b2 100644 --- a/backend/windmill-api/src/utils.rs +++ b/backend/windmill-api/src/utils.rs @@ -76,7 +76,7 @@ pub async fn generate_instance_wide_unique_username<'c>( let mut i = 1; while username_conflict { if i > 1000 { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "too many username conflicts for {}", email ))); @@ -168,7 +168,7 @@ pub async fn get_instance_username_or_create_pending<'c>( ) .execute(&mut **tx) .await - .map_err(|e| Error::InternalErr(format!("creating pending user: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("creating pending user: {e:#}")))?; Ok(username) } diff --git a/backend/windmill-api/src/variables.rs b/backend/windmill-api/src/variables.rs index 48800621b6..37960e8583 100644 --- a/backend/windmill-api/src/variables.rs +++ b/backend/windmill-api/src/variables.rs @@ -196,7 +196,7 @@ async fn get_variable( ) } #[cfg(not(feature = "oauth2"))] - return Err(Error::InternalErr("Require oauth2 feature".to_string())); + return Err(Error::internal_err("Require oauth2 feature".to_string())); } else if !value.is_empty() && decrypt_secret { let _ = tx.commit().await; let mc = build_crypt(&db, &w_id).await?; @@ -558,7 +558,7 @@ async fn update_variable( } } - let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?; + let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; let npath_o: Option = sqlx::query_scalar(&sql).fetch_optional(&mut *tx).await?; @@ -662,7 +662,7 @@ pub async fn get_value_internal<'c>( .await? } #[cfg(not(feature = "oauth2"))] - return Err(Error::InternalErr("Require oauth2 feature".to_string())); + return Err(Error::internal_err("Require oauth2 feature".to_string())); } else if !value.is_empty() { tx.commit().await?; let mc = build_crypt(&db, &w_id).await?; diff --git a/backend/windmill-api/src/websocket_triggers.rs b/backend/windmill-api/src/websocket_triggers.rs index f90bb8def0..a28ed01425 100644 --- a/backend/windmill-api/src/websocket_triggers.rs +++ b/backend/windmill-api/src/websocket_triggers.rs @@ -154,7 +154,7 @@ async fn list_websocket_triggers( } let sql = sqlb .sql() - .map_err(|e| error::Error::InternalErr(e.to_string()))?; + .map_err(|e| error::Error::internal_err(e.to_string()))?; let rows = sqlx::query_as::<_, WebsocketTrigger>(&sql) .fetch_all(&mut *tx) .await?; diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index 3b9fcc7049..af074c2d81 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -412,7 +412,7 @@ async fn get_settings( ) .fetch_one(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("getting settings: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("getting settings: {e:#}")))?; tx.commit().await?; Ok(Json(settings)) @@ -435,7 +435,7 @@ async fn get_deploy_to( ) .fetch_one(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("getting deploy_to: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("getting deploy_to: {e:#}")))?; tx.commit().await?; Ok(Json(settings)) @@ -744,7 +744,7 @@ async fn get_copilot_info( ) .fetch_one(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("getting ai_resource and code_completion_model: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("getting ai_resource and code_completion_model: {e:#}")))?; tx.commit().await?; let (ai_provider, exists_ai_resource) = if let Some(ai_resource) = record.ai_resource { @@ -788,7 +788,7 @@ async fn edit_large_file_storage_config( if let Some(lfs_config) = new_config.large_file_storage { let serialized_lfs_config = serde_json::to_value::(lfs_config) - .map_err(|err| Error::InternalErr(err.to_string()))?; + .map_err(|err| Error::internal_err(err.to_string()))?; sqlx::query!( "UPDATE workspace_settings SET large_file_storage = $1 WHERE workspace_id = $2", @@ -857,7 +857,7 @@ async fn edit_git_sync_config( if let Some(git_sync_settings) = new_config.git_sync_settings { let serialized_config = serde_json::to_value::(git_sync_settings) - .map_err(|err| Error::InternalErr(err.to_string()))?; + .map_err(|err| Error::internal_err(err.to_string()))?; sqlx::query!( "UPDATE workspace_settings SET git_sync = $1 WHERE workspace_id = $2", @@ -923,7 +923,7 @@ async fn edit_deploy_ui_config( if let Some(deploy_ui_settings) = new_config.deploy_ui_settings { let serialized_config = serde_json::to_value::(deploy_ui_settings) - .map_err(|err| Error::InternalErr(err.to_string()))?; + .map_err(|err| Error::internal_err(err.to_string()))?; sqlx::query!( "UPDATE workspace_settings SET deploy_ui = $1 WHERE workspace_id = $2", @@ -1018,7 +1018,7 @@ async fn get_default_scripts( ) .fetch_optional(&mut *tx) .await - .map_err(|err| Error::InternalErr(format!("getting default_app: {err}")))?; + .map_err(|err| Error::internal_err(format!("getting default_app: {err}")))?; tx.commit().await?; Ok(Json(default_scripts.flatten())) @@ -1092,7 +1092,7 @@ async fn get_default_app( ) .fetch_one(&mut *tx) .await - .map_err(|err| Error::InternalErr(format!("getting default_app: {err}")))?; + .map_err(|err| Error::internal_err(format!("getting default_app: {err}")))?; tx.commit().await?; Ok(Json(WorkspaceDefaultApp { default_app_path })) diff --git a/backend/windmill-api/src/workspaces_ee.rs b/backend/windmill-api/src/workspaces_ee.rs index d5fd2cdda1..aa8799e233 100644 --- a/backend/windmill-api/src/workspaces_ee.rs +++ b/backend/windmill-api/src/workspaces_ee.rs @@ -9,7 +9,7 @@ pub async fn edit_auto_invite( _w_id: String, _ea: EditAutoInvite, ) -> windmill_common::error::Result { - Err(windmill_common::error::Error::InternalErr( + Err(windmill_common::error::Error::internal_err( "Not implemented on OSS".to_string(), )) } diff --git a/backend/windmill-api/src/workspaces_export.rs b/backend/windmill-api/src/workspaces_export.rs index 8e7783d8c0..1f55d0172a 100644 --- a/backend/windmill-api/src/workspaces_export.rs +++ b/backend/windmill-api/src/workspaces_export.rs @@ -560,7 +560,7 @@ pub(crate) async fn tarball_workspace( for group in groups { let extra_perms: HashMap = serde_json::from_value(group.extra_perms) .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "Error parsing extra_perms for group {}: {}", group.name, e )) @@ -638,7 +638,7 @@ pub(crate) async fn tarball_workspace( .map(|v| serde_json::to_string_pretty(&v).ok()) .ok() .flatten() - .ok_or_else(|| Error::InternalErr("Error serializing settings".to_string()))?; + .ok_or_else(|| Error::internal_err("Error serializing settings".to_string()))?; archive .write_to_archive(&settings_str, "settings.json") @@ -657,7 +657,7 @@ pub(crate) async fn tarball_workspace( .map(|v| serde_json::to_string_pretty(&v).ok()) .ok() .flatten() - .ok_or_else(|| Error::InternalErr("Error serializing enryption key".to_string()))?; + .ok_or_else(|| Error::internal_err("Error serializing enryption key".to_string()))?; archive .write_to_archive(&key_json, "encryption_key.json") .await?; diff --git a/backend/windmill-common/src/auth.rs b/backend/windmill-common/src/auth.rs index 017131388d..52a966b105 100644 --- a/backend/windmill-common/src/auth.rs +++ b/backend/windmill-common/src/auth.rs @@ -67,7 +67,7 @@ pub async fn is_super_admin_email(db: &DB, email: &str) -> Result { let is_admin = sqlx::query_scalar!("SELECT super_admin FROM password WHERE email = $1", email) .fetch_optional(db) .await - .map_err(|e| Error::InternalErr(format!("fetching super admin: {e:#}")))? + .map_err(|e| Error::internal_err(format!("fetching super admin: {e:#}")))? .unwrap_or(false); Ok(is_admin) @@ -81,7 +81,7 @@ pub async fn is_devops_email(db: &DB, email: &str) -> Result { let is_devops = sqlx::query_scalar!("SELECT devops FROM password WHERE email = $1", email) .fetch_optional(db) .await - .map_err(|e| Error::InternalErr(format!("fetching super admin: {e:#}")))? + .map_err(|e| Error::internal_err(format!("fetching super admin: {e:#}")))? .unwrap_or(false); Ok(is_devops) @@ -123,7 +123,7 @@ pub async fn fetch_authed_from_permissioned_as( if let Some(r) = r { (r.is_admin, r.operator) } else { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "user {name} not found in workspace {w_id}" ))); } diff --git a/backend/windmill-common/src/cache.rs b/backend/windmill-common/src/cache.rs index 83cec0b417..5a65b35610 100644 --- a/backend/windmill-common/src/cache.rs +++ b/backend/windmill-common/src/cache.rs @@ -389,7 +389,7 @@ pub mod flow { async move { fetch_node.await.and_then(|data| match data { RawData::Script(data) => Ok(data), - RawData::Flow(_) => Err(error::Error::InternalErr(format!( + RawData::Flow(_) => Err(error::Error::internal_err(format!( "Flow node ({:x}) isn't a script node.", node.0 ))), @@ -410,7 +410,7 @@ pub mod flow { async move { fetch_node.await.and_then(|data| match data { RawData::Flow(data) => Ok(data), - RawData::Script(_) => Err(error::Error::InternalErr(format!( + RawData::Script(_) => Err(error::Error::internal_err(format!( "Flow node ({:x}) isn't a flow node.", node.0 ))), @@ -639,7 +639,7 @@ pub mod job { async move { fetch_preview.await.and_then(|data| match data { RawData::Flow(data) => Ok(data), - RawData::Script(_) => Err(error::Error::InternalErr(format!( + RawData::Script(_) => Err(error::Error::internal_err(format!( "Job ({job}) isn't a flow job." ))), }) @@ -659,7 +659,7 @@ pub mod job { async move { fetch_preview.await.and_then(|data| match data { RawData::Script(data) => Ok(data), - RawData::Flow(_) => Err(error::Error::InternalErr(format!( + RawData::Flow(_) => Err(error::Error::internal_err(format!( "Job ({job}) isn't a script job." ))), }) @@ -723,7 +723,7 @@ pub mod job { .await .map(|(data, _meta)| data), (AppScript, Some(id)) => app::fetch_script(e, AppScriptId(id)).await, - _ => Err(error::Error::InternalErr(format!( + _ => Err(error::Error::internal_err(format!( "Isn't a script job: {:?}", kind ))), @@ -748,7 +748,7 @@ pub mod job { Ok(raw_flow) => Ok(raw_flow), Err(_) => flow::fetch_version(e, id).await, }, - _ => Err(error::Error::InternalErr(format!( + _ => Err(error::Error::internal_err(format!( "Isn't a flow job {:?}", kind ))), @@ -807,7 +807,7 @@ const _: () = { fn resolve(mut src: Self::Untrusted) -> error::Result { let Some(meta) = src.meta.take() else { - return Err(error::Error::InternalErr("Invalid script src".to_string())); + return Err(error::Error::internal_err("Invalid script src".to_string())); }; Ok(ScriptFull { data: Arc::new(ScriptData { code: src.content, lock: src.lock }), @@ -842,7 +842,7 @@ const _: () = { RawNode { raw_code: Some(code), raw_lock: lock, .. } => { Ok(Self::Script(Arc::new(ScriptData { code, lock }))) } - _ => Err(error::Error::InternalErr( + _ => Err(error::Error::internal_err( "Invalid raw data src".to_string(), )), } diff --git a/backend/windmill-common/src/error.rs b/backend/windmill-common/src/error.rs index 2fe00bc4ab..faf1a72d1b 100644 --- a/backend/windmill-common/src/error.rs +++ b/backend/windmill-common/src/error.rs @@ -54,6 +54,8 @@ pub enum Error { QuotaExceeded(String), #[error("Internal: {0}")] InternalErr(String), + #[error("Internal: {message} @{location}")] + InternalErrLoc { message: String, location: String }, #[error("Internal: {0}: {1}")] InternalErrAt(&'static Location<'static>, String), #[error("HexErr: {error:#} @{location:#}")] @@ -143,10 +145,20 @@ impl Error { pub fn relocate_internal(self, loc: &'static Location<'static>) -> Self { match self { - Self::InternalErr(s) | Self::InternalErrAt(_, s) => Self::InternalErrAt(loc, s), + Self::InternalErrLoc { message, .. } + | Self::InternalErrAt(_, message) + | Self::InternalErr(message) => Self::InternalErrAt(loc, message), _ => self, } } + + #[track_caller] + pub fn internal_err>(msg: T) -> Self { + Self::InternalErrLoc { + message: msg.as_ref().to_string(), + location: prettify_location(std::panic::Location::caller()), + } + } } pub fn relocate_internal(loc: &'static Location<'static>) -> impl FnOnce(Error) -> Error { diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index b4e23bb342..31152aa938 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -736,7 +736,7 @@ pub async fn resolve_value( with_code: bool, ) -> Result<(), Error> { let mut val = serde_json::from_str::(value.get()).map_err(|err| { - Error::InternalErr(format!("resolve: Failed to parse flow value: {}", err)) + Error::internal_err(format!("resolve: Failed to parse flow value: {}", err)) })?; for module in &mut val.modules { resolve_module(e, workspace_id, &mut module.value, with_code).await?; @@ -755,7 +755,7 @@ pub async fn resolve_module( use FlowModuleValue::*; let mut val = serde_json::from_str::(value.get()).map_err(|err| { - Error::InternalErr(format!( + Error::internal_err(format!( "resolve: Failed to parse flow module value: {}", err )) diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index 13a8dd7e8a..326ba08688 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -480,7 +480,7 @@ pub async fn script_hash_to_tag_and_limits<'c>( .fetch_one(&mut **db) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "querying getting tag for hash {script_hash}: {e:#}" )) })?; diff --git a/backend/windmill-common/src/s3_helpers.rs b/backend/windmill-common/src/s3_helpers.rs index 0274d0f38d..01507f5b6a 100644 --- a/backend/windmill-common/src/s3_helpers.rs +++ b/backend/windmill-common/src/s3_helpers.rs @@ -298,7 +298,7 @@ pub async fn build_s3_client(s3_resource_ref: &S3Resource) -> error::Result crate::error::Result String { pub fn decrypt(mc: &MagicCrypt256, value: String) -> error::Result { mc.decrypt_base64_to_string(value).map_err(|e| match e { - MagicCryptError::DecryptError(_) => error::Error::InternalErr( + MagicCryptError::DecryptError(_) => error::Error::internal_err( "Could not decrypt value. The value may have been encrypted with a different key." .to_string(), ), - _ => error::Error::InternalErr(e.to_string()), + _ => error::Error::internal_err(e.to_string()), }) } diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 2405be5b61..8c8b5e4957 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -528,7 +528,7 @@ pub async fn add_completed_job( // add_time!(bench, "add_completed_job start"); if !result.is_valid_json() { - return Err(Error::InternalErr( + return Err(Error::internal_err( "Result of job is invalid json (empty)".to_string(), )); } @@ -643,7 +643,7 @@ pub async fn add_completed_job( ) .fetch_one(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("Could not add completed job {job_id}: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("Could not add completed job {job_id}: {e:#}")))?; if !queued_job.is_flow_step { @@ -835,7 +835,7 @@ pub async fn add_completed_job( .execute(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "Error updating to add ended_at timestamp concurrency_key={concurrency_key}: {e:#}" )) }) { @@ -898,7 +898,7 @@ pub async fn add_completed_job( sqlx::query_scalar!("SELECT premium FROM workspace WHERE id = $1", w_id) .fetch_one(db) .await - .map_err(|e| Error::InternalErr(format!("fetching if {w_id} is premium: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("fetching if {w_id} is premium: {e:#}")))?; let _ = sqlx::query!( "INSERT INTO usage (id, is_workspace, month_, usage) VALUES ($1, TRUE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), $2) @@ -907,7 +907,7 @@ pub async fn add_completed_job( additional_usage as i32) .execute(db) .await - .map_err(|e| Error::InternalErr(format!("updating usage: {e:#}"))); + .map_err(|e| Error::internal_err(format!("updating usage: {e:#}"))); if !premium_workspace { let _ = sqlx::query!( @@ -918,7 +918,7 @@ pub async fn add_completed_job( additional_usage as i32) .execute(db) .await - .map_err(|e| Error::InternalErr(format!("updating usage: {e:#}"))); + .map_err(|e| Error::internal_err(format!("updating usage: {e:#}"))); } } @@ -1213,7 +1213,7 @@ pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync> .fetch_optional(db) .await .context("fetching error handler info from workspace_settings")? - .ok_or_else(|| Error::InternalErr(format!("no workspace settings for id {w_id}")))?; + .ok_or_else(|| Error::internal_err(format!("no workspace settings for id {w_id}")))?; if is_canceled && error_handler_muted_on_cancel { return Ok(()); @@ -1291,7 +1291,7 @@ pub async fn handle_maybe_scheduled_job<'c>( tx.commit().await?; Ok::<(), Error>(()) }) - .map_err(|e| Error::InternalErr(format!("Pushing next scheduled job timedout: {e:#}"))) + .map_err(|e| Error::internal_err(format!("Pushing next scheduled job timedout: {e:#}"))) .unwrap_or_else(|e| Err(e)) }) .retry( @@ -1901,7 +1901,7 @@ pub async fn pull( .fetch_one(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "Error getting concurrency count for script path {job_script_path}: {e:#}" )) })?; @@ -1912,7 +1912,7 @@ pub async fn pull( job_concurrency_key, f64::from(job_custom_concurrency_time_window_s), ).fetch_one(&mut *tx).await.map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "Error getting completed count for key {job_concurrency_key}: {e:#}" )) })?; @@ -1928,7 +1928,7 @@ pub async fn pull( .fetch_one(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "Error getting concurrency count for script path {job_script_path}: {e:#}" )) })?; @@ -1956,7 +1956,7 @@ pub async fn pull( .fetch_one(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "Error decreasing concurrency count for script path {job_script_path}: {e:#}" )) })?; @@ -2033,7 +2033,7 @@ pub async fn pull( ) .fetch_all(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("Could not update and re-queue job {job_uuid}. The job will be marked as running but it is not running: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("Could not update and re-queue job {job_uuid}. The job will be marked as running but it is not running: {e:#}")))?; tx.commit().await? } } @@ -2333,7 +2333,7 @@ pub async fn get_result_and_success_by_id_from_flow( .fetch_optional(db) .await? .ok_or_else(|| { - error::Error::InternalErr(format!("Could not get success from flow job status")) + error::Error::internal_err(format!("Could not get success from flow job status")) })? } }; @@ -2410,7 +2410,7 @@ async fn get_completed_flow_node_result_rec( ) -> error::Result> { for subflow in subflows { let flow_status = subflow.parse_flow_status().ok_or_else(|| { - error::Error::InternalErr(format!("Could not parse flow status of {}", subflow.id)) + error::Error::internal_err(format!("Could not parse flow status of {}", subflow.id)) })?; if let Some(node_status) = flow_status @@ -2761,7 +2761,7 @@ pub async fn push<'c, 'd>( .fetch_one(_db) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "fetching if {workspace_id} is premium and overquota: {e:#}" )) })?; @@ -2780,7 +2780,7 @@ pub async fn push<'c, 'd>( ) .fetch_one(_db) .await - .map_err(|e| Error::InternalErr(format!("updating usage: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("updating usage: {e:#}")))?; let user_usage = if !premium_workspace { Some(sqlx::query_scalar!( @@ -2792,7 +2792,7 @@ pub async fn push<'c, 'd>( ) .fetch_one(_db) .await - .map_err(|e| Error::InternalErr(format!("updating usage: {e:#}")))?) + .map_err(|e| Error::internal_err(format!("updating usage: {e:#}")))?) } else { None }; @@ -3344,7 +3344,7 @@ pub async fn push<'c, 'd>( ) .fetch_optional(&mut *ntx) .await? - .ok_or_else(|| Error::InternalErr(format!("not found flow at path {:?}", path)))?; + .ok_or_else(|| Error::internal_err(format!("not found flow at path {:?}", path)))?; // Do not use the lite version unless all workers are updated. let data = if *DISABLE_FLOW_SCRIPT @@ -3683,12 +3683,12 @@ pub async fn push<'c, 'd>( ) .execute(&mut *tx) .await - .map_err(|e| Error::InternalErr(format!("Could not insert concurrency_key={concurrency_key} for job_id={job_id} script_path={script_path:?} workspace_id={workspace_id}: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("Could not insert concurrency_key={concurrency_key} for job_id={job_id} script_path={script_path:?} workspace_id={workspace_id}: {e:#}")))?; } let stringified_args = if *JOB_ARGS_AUDIT_LOGS { Some(serde_json::to_string(&args).map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "Could not serialize args for audit log of job {job_id}: {e:#}" )) })?) @@ -3762,7 +3762,7 @@ pub async fn push<'c, 'd>( .fetch_one(&mut *tx) .warn_after_seconds(1) .await - .map_err(|e| Error::InternalErr(format!("Could not insert into queue {job_id} with tag {tag}, schedule_path {schedule_path:?}, script_path: {script_path:?}, email {email}, workspace_id {workspace_id}: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("Could not insert into queue {job_id} with tag {tag}, schedule_path {schedule_path:?}, script_path: {script_path:?}, email {email}, workspace_id {workspace_id}: {e:#}")))?; tracing::debug!("Pushed {job_id}"); // TODO: technically the job isn't queued yet, as the transaction can be rolled back. Should be solved when moving these metrics to the queue abstraction. @@ -3791,7 +3791,7 @@ pub async fn push<'c, 'd>( ) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "Could not get permissions directly for job {job_id}: {e:#}" )) })? @@ -3922,7 +3922,7 @@ async fn restarted_flows_resolution( .fetch_one(db) // TODO: should we try to use the passed-in `tx` here? .await .map_err(|err| { - Error::InternalErr(format!( + Error::internal_err(format!( "completed job not found for UUID {} in workspace {}: {}", completed_flow_id, workspace_id, err )) @@ -3936,7 +3936,7 @@ async fn restarted_flows_resolution( .flow_status .as_ref() .and_then(|v| serde_json::from_str::(v.get()).ok()) - .ok_or(Error::InternalErr(format!( + .ok_or(Error::internal_err(format!( "Unable to parse flow status for job {} in workspace {}", completed_flow_id, workspace_id, )))?; @@ -3966,14 +3966,14 @@ async fn restarted_flows_resolution( match module_definition.get_value() { Ok(FlowModuleValue::BranchAll { branches, parallel, .. }) => { if parallel { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "Module {} is a parallel branchall. It can only be restarted at a given branch if it's sequential", restart_step_id, ))); } let total_branch_number = module.flow_jobs().map(|v| v.len()).unwrap_or(0); if total_branch_number <= branch_or_iteration_n { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "Branch-all module {} has only {} branches. It can't be restarted on branch {}", restart_step_id, total_branch_number, @@ -4004,14 +4004,14 @@ async fn restarted_flows_resolution( } Ok(FlowModuleValue::ForloopFlow { parallel, .. }) => { if parallel { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "Module {} is not parallel loop. It can only be restarted at a given iteration if it's sequential", restart_step_id, ))); } let total_iterations = module.flow_jobs().map(|v| v.len()).unwrap_or(0); if total_iterations <= branch_or_iteration_n { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "For-loop module {} doesn't cannot be restarted on iteration number {} as it has only {} iterations", restart_step_id, branch_or_iteration_n, @@ -4041,7 +4041,7 @@ async fn restarted_flows_resolution( }); } _ => { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "Module {} is not a branchall or forloop, unable to restart it at step {:?}", restart_step_id, branch_or_iteration_n @@ -4057,7 +4057,7 @@ async fn restarted_flows_resolution( step_n = step_n + 1; match module.clone() { FlowStatusModule::Success { .. } => Ok(truncated_modules.push(module)), - _ => Err(Error::InternalErr(format!( + _ => Err(Error::internal_err(format!( "Flow cannot be restarted from a non successful module", ))), }?; @@ -4066,7 +4066,7 @@ async fn restarted_flows_resolution( if !dependent_module { // step not found in flow. - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "Flow cannot be restarted from step {} as it could not be found.", restart_step_id ))); diff --git a/backend/windmill-queue/src/schedule.rs b/backend/windmill-queue/src/schedule.rs index ea9f476ab6..aa3cf5984b 100644 --- a/backend/windmill-queue/src/schedule.rs +++ b/backend/windmill-queue/src/schedule.rs @@ -149,7 +149,7 @@ pub async fn push_scheduled_job<'c>( if schedule.retry.is_some() { let parsed_retry = serde_json::from_value::(schedule.retry.clone().unwrap()) .map_err(|err| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "Unable to parse retry information from schedule: {}", err.to_string(), )) diff --git a/backend/windmill-worker/src/bun_executor.rs b/backend/windmill-worker/src/bun_executor.rs index b810c888fc..9d914f5381 100644 --- a/backend/windmill-worker/src/bun_executor.rs +++ b/backend/windmill-worker/src/bun_executor.rs @@ -532,7 +532,7 @@ pub async fn generate_wrapper_mjs( format!("{job_dir}/wrapper.js"), format!("{job_dir}/wrapper.mjs"), ) - .map_err(|e| error::Error::InternalErr(format!("Could not move wrapper to mjs: {e:#}")))?; + .map_err(|e| error::Error::internal_err(format!("Could not move wrapper to mjs: {e:#}")))?; Ok(()) } @@ -674,7 +674,7 @@ pub fn copy_recursively( stack.push((entry.path(), destination, level + 1)); } else { fs::hard_link(&original, &destination).map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "hard linking from {original:?} to {destination:?}: {e:#}" )) })?; @@ -823,7 +823,7 @@ async fn write_lock(splitted_lockb_2: &str, job_dir: &str, is_binary: bool) -> R "bun.lockb", &base64::engine::general_purpose::STANDARD .decode(splitted_lockb_2) - .map_err(|_| error::Error::InternalErr(format!("Could not decode bun.lockb")))?, + .map_err(|_| error::Error::internal_err(format!("Could not decode bun.lockb")))?, ) .await?; } else { @@ -1503,13 +1503,13 @@ try {{ let args = read_file(&format!("{job_dir}/args.json")) .await .map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "error while reading args from preprocessing: {e:#}" )) })?; let args: HashMap> = serde_json::from_str(args.get()).map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "error while deserializing args from preprocessing: {e:#}" )) })?; @@ -1632,7 +1632,7 @@ pub async fn start_worker( &base64::engine::general_purpose::STANDARD .decode(lock) .map_err(|_| { - error::Error::InternalErr("Could not decode bun.lockb".to_string()) + error::Error::internal_err("Could not decode bun.lockb".to_string()) })?, ) .await?; diff --git a/backend/windmill-worker/src/common.rs b/backend/windmill-worker/src/common.rs index 5212d80e11..8c78287be9 100644 --- a/backend/windmill-worker/src/common.rs +++ b/backend/windmill-worker/src/common.rs @@ -149,13 +149,13 @@ pub async fn transform_json<'a>( let inner_vs = v.get(); if (*RE_RES_VAR).is_match(inner_vs) { let value = serde_json::from_str(inner_vs).map_err(|e| { - error::Error::InternalErr(format!("Error while parsing inner arg: {e:#}")) + error::Error::internal_err(format!("Error while parsing inner arg: {e:#}")) })?; let transformed = transform_json_value(&k, &client.get_authed().await, workspace, value, job, db) .await?; let as_raw = serde_json::from_value(transformed).map_err(|e| { - error::Error::InternalErr(format!("Error while parsing inner arg: {e:#}")) + error::Error::internal_err(format!("Error while parsing inner arg: {e:#}")) })?; r.insert(k.to_string(), as_raw); } else { @@ -177,13 +177,13 @@ pub async fn transform_json_as_values<'a>( let inner_vs = v.get(); if (*RE_RES_VAR).is_match(inner_vs) { let value = serde_json::from_str(inner_vs).map_err(|e| { - error::Error::InternalErr(format!("Error while parsing inner arg: {e:#}")) + error::Error::internal_err(format!("Error while parsing inner arg: {e:#}")) })?; let transformed = transform_json_value(&k, &client.get_authed().await, workspace, value, job, db) .await?; let as_raw = serde_json::from_value(transformed).map_err(|e| { - error::Error::InternalErr(format!("Error while parsing inner arg: {e:#}")) + error::Error::internal_err(format!("Error while parsing inner arg: {e:#}")) })?; r.insert(k.to_string(), as_raw); } else { @@ -237,7 +237,7 @@ pub async fn transform_json_value( Value::String(y) if y.starts_with("$res:") => { let path = y.strip_prefix("$res:").unwrap(); if path.split("/").count() < 2 { - return Err(Error::InternalErr(format!( + return Err(Error::internal_err(format!( "Argument `{name}` is an invalid resource path: {path}", ))); } @@ -256,7 +256,7 @@ pub async fn transform_json_value( let mc = build_crypt_with_key_suffix(&db, &job.workspace_id, &job.id.to_string()).await?; decrypt(&mc, encrypted.to_string()).and_then(|x| { - serde_json::from_str(&x).map_err(|e| Error::InternalErr(e.to_string())) + serde_json::from_str(&x).map_err(|e| Error::internal_err(e.to_string())) }) // let path = y.strip_prefix("$res:").unwrap(); @@ -930,7 +930,7 @@ fn tentatively_improve_error(err: Error, executable: &str) -> Error { let err_msg = "program not found"; if err.to_string().contains(&err_msg) { - return Error::InternalErr(format!( + return Error::internal_err(format!( "Executable {executable} not found on worker. PATH: {}", *PATH_ENV )); @@ -967,5 +967,5 @@ pub fn build_http_client(timeout_duration: std::time::Duration) -> error::Result .timeout(timeout_duration) .connect_timeout(std::time::Duration::from_secs(10)) .build() - .map_err(|e| Error::InternalErr(format!("Error building http client: {e:#}"))) + .map_err(|e| Error::internal_err(format!("Error building http client: {e:#}"))) } diff --git a/backend/windmill-worker/src/dedicated_worker.rs b/backend/windmill-worker/src/dedicated_worker.rs index ff6b8cec79..840934260a 100644 --- a/backend/windmill-worker/src/dedicated_worker.rs +++ b/backend/windmill-worker/src/dedicated_worker.rs @@ -470,7 +470,7 @@ pub async fn create_dedicated_worker_map( if let Ok(v) = value { if let Some(v) = v { let value = serde_json::from_str::(v.get()).map_err(|err| { - Error::InternalErr(format!( + Error::internal_err(format!( "could not convert json to flow for {flow_path}: {err:?}" )) }); @@ -622,7 +622,7 @@ async fn spawn_dedicated_worker( .bind(&w_id) .fetch_optional(&db) .await - .map_err(|e| Error::InternalErr(format!("expected content and lock: {e:#}"))) + .map_err(|e| Error::internal_err(format!("expected content and lock: {e:#}"))) .map(|x| x.map(|y| (y.0, y.1, y.2, y.3, if y.4 { y.5.map(|z| z.to_string()) } else { None }))) }; if let Ok(q) = q { diff --git a/backend/windmill-worker/src/deno_executor.rs b/backend/windmill-worker/src/deno_executor.rs index 2385771a15..f27b8de80c 100644 --- a/backend/windmill-worker/src/deno_executor.rs +++ b/backend/windmill-worker/src/deno_executor.rs @@ -432,13 +432,13 @@ try {{ let args = read_file(&format!("{job_dir}/args.json")) .await .map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "error while reading args from preprocessing: {e:#}" )) })?; let args: HashMap> = serde_json::from_str(args.get()).map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "error while deserializing args from preprocessing: {e:#}" )) })?; diff --git a/backend/windmill-worker/src/mysql_executor.rs b/backend/windmill-worker/src/mysql_executor.rs index b98ec365ee..d733901f93 100644 --- a/backend/windmill-worker/src/mysql_executor.rs +++ b/backend/windmill-worker/src/mysql_executor.rs @@ -133,7 +133,7 @@ pub async fn do_mysql( .await?; let as_raw = serde_json::from_value(val).map_err(|e| { - Error::InternalErr(format!("Error while parsing inline resource: {e:#}")) + Error::internal_err(format!("Error while parsing inline resource: {e:#}")) })?; Some(as_raw) diff --git a/backend/windmill-worker/src/oracledb_executor.rs b/backend/windmill-worker/src/oracledb_executor.rs index f6e64a60a1..e6292181b2 100644 --- a/backend/windmill-worker/src/oracledb_executor.rs +++ b/backend/windmill-worker/src/oracledb_executor.rs @@ -328,7 +328,7 @@ pub async fn do_oracledb( .await?; let as_raw = serde_json::from_value(val).map_err(|e| { - Error::InternalErr(format!("Error while parsing inline resource: {e:#}")) + Error::internal_err(format!("Error while parsing inline resource: {e:#}")) })?; Some(as_raw) diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index 98e1483421..3a8c1da760 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -1218,13 +1218,13 @@ mount {{ let args = read_file(&format!("{job_dir}/args.json")) .await .map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "error while reading args from preprocessing: {e:#}" )) })?; let args: HashMap> = serde_json::from_str(args.get()).map_err(|e| { - error::Error::InternalErr(format!( + error::Error::internal_err(format!( "error while deserializing args from preprocessing: {e:#}" )) })?; @@ -1438,7 +1438,7 @@ async fn replace_pip_secret( let capture = PIP_SECRET_VARIABLE.captures(req); let variable = capture.unwrap().get(1).unwrap().as_str(); if !variable.contains("/PIP_SECRET_") { - return Err(error::Error::InternalErr(format!( + return Err(error::Error::internal_err(format!( "invalid secret variable in pip requirements, (last part of path ma): {}", req ))); diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 2c2aefe6b3..23c76173b4 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -208,7 +208,7 @@ pub async fn create_token_for_owner( let jwt_secret = JWT_SECRET.read().await; if jwt_secret.is_empty() { - return Err(Error::InternalErr("No JWT secret found".to_string())); + return Err(Error::internal_err("No JWT secret found".to_string())); } let job_authed = match sqlx::query_as!( @@ -226,7 +226,7 @@ pub async fn create_token_for_owner( fetch_authed_from_permissioned_as(owner.to_string(), email.to_string(), w_id, db) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "Could not get permissions directly for job {job_id}: {e:#}" )) })? @@ -254,7 +254,7 @@ pub async fn create_token_for_owner( &jsonwebtoken::EncodingKey::from_secret(jwt_secret.as_bytes()), ) .map_err(|err| { - Error::InternalErr(format!( + Error::internal_err(format!( "Could not encode JWT token for job {job_id}: {:?}", err )) @@ -1330,7 +1330,9 @@ pub async fn run_worker( .bind(same_worker_job.job_id) .fetch_optional(db) .await - .map_err(|_| Error::InternalErr("Impossible to fetch same_worker job".to_string())); + .map_err(|_| { + Error::internal_err("Impossible to fetch same_worker job".to_string()) + }); if r.is_err() && !same_worker_job.recoverable { tracing::error!( worker = %worker_name, hostname = %hostname, @@ -1994,7 +1996,7 @@ async fn handle_queued_job( db, &job.workspace_id, job.parent_job - .ok_or_else(|| Error::InternalErr(format!("expected parent job")))?, + .ok_or_else(|| Error::internal_err(format!("expected parent job")))?, job.id, ) .warn_after_seconds(5) @@ -2280,7 +2282,7 @@ pub async fn get_hub_script_content_and_requirements( ) -> error::Result { let script_path = script_path .clone() - .ok_or_else(|| Error::InternalErr(format!("expected script path for hub script")))?; + .ok_or_else(|| Error::internal_err(format!("expected script path for hub script")))?; let script = get_full_hub_script_by_path(StripPath(script_path.to_string()), &HTTP_CLIENT, db).await?; @@ -2331,7 +2333,7 @@ async fn handle_code_execution_job( ) -> error::Result> { let script_hash = || { job.script_hash - .ok_or_else(|| Error::InternalErr("expected script hash".into())) + .ok_or_else(|| Error::internal_err("expected script hash")) }; let (arc_data, arc_metadata, data, metadata): ( Arc, @@ -2349,7 +2351,8 @@ async fn handle_code_execution_job( _ => None, }; - arc_data = preview.ok_or_else(|| Error::InternalErr("expected preview".to_string()))?; + arc_data = + preview.ok_or_else(|| Error::internal_err("expected preview".to_string()))?; metadata = ScriptMetadata { language: job.language, codebase, envs: None }; (arc_data.as_ref(), &metadata) } @@ -2378,7 +2381,7 @@ async fn handle_code_execution_job( let script_path = job .script_path .as_ref() - .ok_or_else(|| Error::InternalErr("expected script path".to_string()))?; + .ok_or_else(|| Error::internal_err("expected script path".to_string()))?; if script_path.starts_with("hub/") { let ContentReqLangEnvs { content, lockfile, language, envs, codebase } = get_hub_script_content_and_requirements(Some(script_path), Some(db)).await?; @@ -2394,7 +2397,7 @@ async fn handle_code_execution_job( ) .fetch_optional(db) .await? - .ok_or_else(|| Error::InternalErr("expected script hash".to_string()))?; + .ok_or_else(|| Error::internal_err("expected script hash".to_string()))?; (arc_data, arc_metadata) = cache::script::fetch(db, ScriptHash(hash)).await?; (arc_data.as_ref(), arc_metadata.as_ref()) @@ -2421,7 +2424,7 @@ async fn handle_code_execution_job( .await; } else if language == Some(ScriptLang::Mysql) { #[cfg(not(feature = "mysql"))] - return Err(Error::InternalErr( + return Err(Error::internal_err( "MySQL requires the mysql feature to be enabled".to_string(), )); @@ -2449,7 +2452,7 @@ async fn handle_code_execution_job( #[allow(unreachable_code)] #[cfg(not(feature = "bigquery"))] { - return Err(Error::InternalErr( + return Err(Error::internal_err( "Bigquery requires the bigquery feature to be enabled".to_string(), )); } @@ -2503,7 +2506,7 @@ async fn handle_code_execution_job( #[allow(unreachable_code)] #[cfg(not(feature = "mssql"))] { - return Err(Error::InternalErr( + return Err(Error::internal_err( "Microsoft SQL server requires the mssql feature to be enabled".to_string(), )); } @@ -2533,7 +2536,7 @@ async fn handle_code_execution_job( #[allow(unreachable_code)] #[cfg(not(feature = "oracledb"))] { - return Err(Error::InternalErr( + return Err(Error::internal_err( "Oracle DB requires the oracledb feature to be enabled".to_string(), )); } @@ -2644,7 +2647,7 @@ mount {{ } Some(ScriptLang::Python3) => { #[cfg(not(feature = "python"))] - return Err(Error::InternalErr( + return Err(Error::internal_err( "Python requires the python feature to be enabled".to_string(), )); @@ -2761,7 +2764,7 @@ mount {{ } Some(ScriptLang::Php) => { #[cfg(not(feature = "php"))] - return Err(Error::InternalErr( + return Err(Error::internal_err( "PHP requires the php feature to be enabled".to_string(), )); @@ -2785,7 +2788,7 @@ mount {{ } Some(ScriptLang::Rust) => { #[cfg(not(feature = "rust"))] - return Err(Error::InternalErr( + return Err(Error::internal_err( "Rust requires the rust feature to be enabled".to_string(), )); @@ -2809,7 +2812,7 @@ mount {{ } Some(ScriptLang::Ansible) => { #[cfg(not(feature = "python"))] - return Err(Error::InternalErr( + return Err(Error::internal_err( "Ansible requires the python feature to be enabled".to_string(), )); diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index cf24258b9e..37fc023a8d 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -233,7 +233,7 @@ pub async fn update_flow_status_after_job_completion_internal( .fetch_one(db) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "fetching flow status {flow} while reporting {success} {result:?}: {e:#}" )) }) @@ -242,7 +242,7 @@ pub async fn update_flow_status_after_job_completion_internal( record.job_kind, record.script_hash, serde_json::from_str::(record.flow_status.0.get()).map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "requiring current module to be parsable as FlowStatus: {e:?}" )) })?, @@ -264,12 +264,12 @@ pub async fn update_flow_status_after_job_completion_internal( Step::PreprocessorStep => old_status .preprocessor_module .as_ref() - .ok_or_else(|| Error::InternalErr(format!("preprocessor module not found")))?, + .ok_or_else(|| Error::internal_err(format!("preprocessor module not found")))?, Step::FailureStep => &old_status.failure_module.module_status, Step::Step(i) => old_status .modules .get(i as usize) - .ok_or_else(|| Error::InternalErr(format!("module {i} not found")))?, + .ok_or_else(|| Error::internal_err(format!("module {i} not found")))?, }; // tracing::debug!( @@ -366,7 +366,7 @@ pub async fn update_flow_status_after_job_completion_internal( .fetch_one(db) .await .map_err(|e| { - Error::InternalErr(format!("retrieval of args from state: {e:#}")) + Error::internal_err(format!("retrieval of args from state: {e:#}")) })?; compute_bool_from_expr( &expr, @@ -418,7 +418,7 @@ pub async fn update_flow_status_after_job_completion_internal( job_id_for_status, flow ).execute(db).await.map_err(|e| { - Error::InternalErr(format!("error while updating args in preprocessing step: {e:#}")) + Error::internal_err(format!("error while updating args in preprocessing step: {e:#}")) })?; sqlx::query!( @@ -428,7 +428,7 @@ pub async fn update_flow_status_after_job_completion_internal( .execute(db) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while deleting args of preprocessing step: {e:#}" )) })?; @@ -483,11 +483,11 @@ pub async fn update_flow_status_after_job_completion_internal( } .fetch_one(&mut *tx) .await.map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while fetching iterator index: {e:#}" )) })? - .ok_or_else(|| Error::InternalErr(format!("requiring an index in InProgress")))?; + .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; tracing::info!( "parallel iteration {job_id_for_status} of flow {flow} update nindex: {nindex} len: {len}", nindex = nindex, @@ -528,14 +528,14 @@ pub async fn update_flow_status_after_job_completion_internal( .fetch_one(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while fetching branchall index: {e:#}" )) })? - .ok_or_else(|| Error::InternalErr(format!("requiring an index in InProgress")))?; + .ok_or_else(|| Error::internal_err(format!("requiring an index in InProgress")))?; (nindex, *len as i32) } - _ => Err(Error::InternalErr(format!( + _ => Err(Error::internal_err(format!( "unexpected status for parallel module" )))?, }; @@ -558,7 +558,7 @@ pub async fn update_flow_status_after_job_completion_internal( .fetch_all(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while fetching sucess from completed_jobs: {e:#}" )) })? @@ -591,7 +591,7 @@ pub async fn update_flow_status_after_job_completion_internal( "DELETE FROM parallel_monitor_lock WHERE parent_flow_id = $1 RETURNING last_ping", flow, ).fetch_optional(db).await.map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while deleting parallel_monitor_lock: {e:#}" )) })?; @@ -618,7 +618,7 @@ pub async fn update_flow_status_after_job_completion_internal( .execute(db) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error resuming job at suspend {nindex} and parent {flow}: {e:#}" )) })?; @@ -629,7 +629,7 @@ pub async fn update_flow_status_after_job_completion_internal( flow, job_id_for_status ).fetch_optional(db).await.map_err(|e| { - Error::InternalErr(format!("error while removing parallel_monitor_lock: {e:#}")) + Error::internal_err(format!("error while removing parallel_monitor_lock: {e:#}")) })?; if r.is_some() { tracing::info!( @@ -720,7 +720,9 @@ pub async fn update_flow_status_after_job_completion_internal( ) .fetch_one(db) .await - .map_err(|e| Error::InternalErr(format!("error during skip check: {e:#}")))? + .map_err(|e| { + Error::internal_err(format!("error during skip check: {e:#}")) + })? .unwrap_or(false) } else { false @@ -777,7 +779,7 @@ pub async fn update_flow_status_after_job_completion_internal( .execute(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!("error while setting flow index for {flow}: {e:#}")) + Error::internal_err(format!("error while setting flow index for {flow}: {e:#}")) })?; old_status.step + 1 } else { @@ -797,7 +799,7 @@ pub async fn update_flow_status_after_job_completion_internal( ) .fetch_one(&mut *tx) .await.map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while fetching failure module: {e:#}" )) })?; @@ -815,7 +817,7 @@ pub async fn update_flow_status_after_job_completion_internal( .execute(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while setting flow status in failure step: {e:#}" )) })?; @@ -830,7 +832,7 @@ pub async fn update_flow_status_after_job_completion_internal( .execute(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while setting flow status in preprocessing step: {e:#}" )) })?; @@ -846,7 +848,7 @@ pub async fn update_flow_status_after_job_completion_internal( .execute(&mut *tx) .await .map_err(|e| { - Error::InternalErr(format!("error while setting new flow status: {e:#}")) + Error::internal_err(format!("error while setting new flow status: {e:#}")) })?; if let Some(job_result) = new_status.job_result() { @@ -860,7 +862,7 @@ pub async fn update_flow_status_after_job_completion_internal( ) .execute(&mut *tx) .await.map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while setting leaf jobs: {e:#}" )) })?; @@ -893,7 +895,7 @@ pub async fn update_flow_status_after_job_completion_internal( .fetch_one(db) .await .map_err(|e| { - Error::InternalErr(format!("retrieval of args from state: {e:#}")) + Error::internal_err(format!("retrieval of args from state: {e:#}")) })?; let should_stop = compute_bool_from_expr( @@ -947,7 +949,7 @@ pub async fn update_flow_status_after_job_completion_internal( .fetch_optional(&mut *tx) .await .map_err(Into::::into)? - .ok_or_else(|| Error::InternalErr(format!("requiring flow to be in the queue")))?; + .ok_or_else(|| Error::internal_err(format!("requiring flow to be in the queue")))?; tx.commit().await?; let job_root = flow_job @@ -1044,7 +1046,7 @@ pub async fn update_flow_status_after_job_completion_internal( .execute(db) .await .map_err(|e| { - Error::InternalErr(format!("error while cleaning up completed_job: {e:#}")) + Error::internal_err(format!("error while cleaning up completed_job: {e:#}")) })?; } } @@ -1200,7 +1202,7 @@ async fn set_success_in_flow_job_success<'c>( ) .execute(&mut **tx) .await.map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error while setting flow_jobs_success: {e:#}" )) })?; @@ -1232,7 +1234,7 @@ async fn retrieve_flow_jobs_results( .map(|j| { results .get(j) - .ok_or_else(|| Error::InternalErr(format!("missing job result for {}", j))) + .ok_or_else(|| Error::internal_err(format!("missing job result for {}", j))) }) .collect::, _>>()?; @@ -1252,19 +1254,19 @@ async fn compute_skip_branchall_failure<'c>( .fetch_one(db) .await .map_err(|e| { - Error::InternalErr(format!("error during retrieval of branchall index: {e:#}")) + Error::internal_err(format!("error during retrieval of branchall index: {e:#}")) })? .map(|p| { BRANCHALL_INDEX_RE .captures(&p) .map(|x| x.get(1).unwrap().as_str().parse::().ok()) .flatten() - .ok_or(Error::InternalErr(format!( + .ok_or(Error::internal_err(format!( "could not parse branchall index from path: {p}" ))) }) .ok_or_else(|| { - Error::InternalErr(format!("no branchall script path found for job {job}")) + Error::internal_err(format!("no branchall script path found for job {job}")) })?? } else { branch as i32 @@ -1288,12 +1290,12 @@ async fn compute_skip_branchall_failure<'c>( // ) // .fetch_one(db) // .await -// .map_err(|e| Error::InternalErr(format!("error during retrieval of cleanup module: {e:#}")))?; +// .map_err(|e| Error::internal_err(format!("error during retrieval of cleanup module: {e:#}")))?; // raw_value // .clone() // .and_then(|rv| serde_json::from_value::(rv).ok()) -// .ok_or(Error::InternalErr(format!( +// .ok_or(Error::internal_err(format!( // "Unable to parse flow cleanup module {:?}", // raw_value // ))) @@ -1427,12 +1429,12 @@ pub async fn get_step_of_flow_status(db: &DB, id: Uuid) -> error::Result { ) .fetch_one(db) .await - .map_err(|e| Error::InternalErr(format!("fetching step flow status: {e:#}")))?; + .map_err(|e| Error::internal_err(format!("fetching step flow status: {e:#}")))?; if let Some(step) = r.step { Ok(Step::from_i32_and_len(step, r.len.unwrap_or(0) as usize)) } else { - Err(Error::InternalErr("step is null".to_string())) + Err(Error::internal_err("step is null".to_string())) } } @@ -1666,7 +1668,7 @@ async fn push_next_flow_job( }) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error sending update flow message to job completed channel: {e:#}" )) })?; @@ -1711,7 +1713,7 @@ async fn push_next_flow_job( }) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error sending update flow message to job completed channel: {e:#}" )) })?; @@ -1748,7 +1750,7 @@ async fn push_next_flow_job( }) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error sending update flow message to job completed channel: {e:#}" )) })?; @@ -1897,7 +1899,10 @@ async fn push_next_flow_job( && suspend.continue_on_disapprove_timeout.unwrap_or(false); let audit_author = AuditAuthor { - username: flow_job.permissioned_as.trim_start_matches("u/").to_string(), + username: flow_job + .permissioned_as + .trim_start_matches("u/") + .to_string(), email: flow_job.email.clone(), username_override: None, }; @@ -2038,7 +2043,7 @@ async fn push_next_flow_job( }) .await .map_err(|e| { - Error::InternalErr(format!( + Error::internal_err(format!( "error sending update flow message to job completed channel: {e:#}" )) })?; @@ -2287,7 +2292,7 @@ async fn push_next_flow_job( .unwrap(), ) .map(Marc::new) - .map_err(|e| error::Error::InternalErr(format!("identity: {e:#}"))) + .map_err(|e| error::Error::internal_err(format!("identity: {e:#}"))) } Ok( FlowModuleValue::Script { input_transforms, .. } @@ -2313,7 +2318,7 @@ async fn push_next_flow_job( } Ok(_) => Ok(arc_flow_job_args.clone()), Err(e) => { - return Err(error::Error::InternalErr(format!( + return Err(error::Error::internal_err(format!( "module was not convertible to acceptable value {e:?}" ))) } @@ -2637,7 +2642,7 @@ async fn push_next_flow_job( if payload_tag.delete_after_use { let uuid_singleton_json = serde_json::to_value(&[uuid]).map_err(|e| { - error::Error::InternalErr(format!("Unable to serialize uuid: {e:#}")) + error::Error::internal_err(format!("Unable to serialize uuid: {e:#}")) })?; sqlx::query( diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs index d4df729910..3437d692de 100644 --- a/backend/windmill-worker/src/worker_lockfiles.rs +++ b/backend/windmill-worker/src/worker_lockfiles.rs @@ -265,13 +265,13 @@ pub async fn handle_dependency_job( Some(hash) => &cache::script::fetch(db, hash).await?.0, _ => match preview_data { Some(RawData::Script(data)) => data, - _ => return Err(Error::InternalErr("expected script hash".into())), + _ => return Err(Error::internal_err("expected script hash")), }, }; let content = capture_dependency_job( &job.id, job.language.as_ref().map(|v| Ok(v)).unwrap_or_else(|| { - Err(Error::InternalErr( + Err(Error::internal_err( "Job Language required for dependency jobs".to_owned(), )) })?, @@ -551,7 +551,7 @@ pub async fn handle_flow_dependency_job( occupancy_metrics: &mut OccupancyMetrics, ) -> error::Result> { let job_path = job.script_path.clone().ok_or_else(|| { - error::Error::InternalErr( + error::Error::internal_err( "Cannot resolve flow dependencies for flow without path".to_string(), ) })?; @@ -574,7 +574,7 @@ pub async fn handle_flow_dependency_job( job.script_hash .clone() .ok_or_else(|| { - Error::InternalErr( + Error::internal_err( "Flow Dependency requires script hash (flow version)".to_owned(), ) })? @@ -602,7 +602,7 @@ pub async fn handle_flow_dependency_job( Some(ScriptHash(id)) => cache::flow::fetch_version(db, id).await?, _ => match preview_data { Some(RawData::Flow(data)) => data.clone(), - _ => return Err(Error::InternalErr("expected script hash".into())), + _ => return Err(Error::internal_err("expected script hash")), }, } .value() @@ -649,7 +649,7 @@ pub async fn handle_flow_dependency_job( if !skip_flow_update { let version = version.ok_or_else(|| { - Error::InternalErr("Flow Dependency requires script hash (flow version)".to_owned()) + Error::internal_err("Flow Dependency requires script hash (flow version)".to_owned()) })?; sqlx::query!( @@ -1163,7 +1163,7 @@ async fn reduce_flow<'c>( for module in &mut *modules { let mut val = serde_json::from_str::(module.value.get()).map_err(|err| { - Error::InternalErr(format!( + Error::internal_err(format!( "reduce_flow: Failed to parse flow module value: {}", err )) @@ -1274,7 +1274,7 @@ async fn reduce_app(db: &sqlx::Pool, value: &mut Value, app: i64 // replace `content` with an empty string: let Some(Value::String(code)) = script.get_mut("content").map(std::mem::take) else { - return Err(error::Error::InternalErr( + return Err(error::Error::internal_err( "Missing `content` in inlineScript".to_string(), )); }; @@ -1477,7 +1477,7 @@ pub async fn handle_app_dependency_job( occupancy_metrics: &mut OccupancyMetrics, ) -> error::Result<()> { let job_path = job.script_path.clone().ok_or_else(|| { - error::Error::InternalErr( + error::Error::internal_err( "Cannot resolve app dependencies for app without path".to_string(), ) })?; @@ -1485,7 +1485,7 @@ pub async fn handle_app_dependency_job( let id = job .script_hash .clone() - .ok_or_else(|| Error::InternalErr("App Dependency requires script hash".to_owned()))? + .ok_or_else(|| Error::internal_err("App Dependency requires script hash".to_owned()))? .0; let record = sqlx::query!("SELECT app_id, value FROM app_version WHERE id = $1", id) .fetch_optional(db) @@ -1572,7 +1572,7 @@ pub async fn handle_app_dependency_job( // match tx { // PushIsolationLevel::Transaction(tx) => tx.commit().await?, // _ => { - // return Err(Error::InternalErr( + // return Err(Error::internal_err( // "Expected a transaction here".to_string(), // )); // } @@ -1682,7 +1682,7 @@ async fn capture_dependency_job( match job_language { ScriptLang::Python3 => { #[cfg(not(feature = "python"))] - return Err(Error::InternalErr( + return Err(Error::internal_err( "Python requires the python feature to be enabled".to_string(), )); #[cfg(feature = "python")] @@ -1749,7 +1749,7 @@ async fn capture_dependency_job( } ScriptLang::Ansible => { #[cfg(not(feature = "python"))] - return Err(Error::InternalErr( + return Err(Error::internal_err( "Ansible requires the python feature to be enabled".to_string(), )); @@ -1887,7 +1887,7 @@ async fn capture_dependency_job( } ScriptLang::Php => { #[cfg(not(feature = "php"))] - return Err(Error::InternalErr( + return Err(Error::internal_err( "PHP requires the php feature to be enabled".to_string(), )); @@ -1929,7 +1929,7 @@ async fn capture_dependency_job( } #[cfg(not(feature = "rust"))] - return Err(Error::InternalErr( + return Err(Error::internal_err( "Rust requires the rust feature to be enabled".to_string(), )); From 3b46af36e47f62b5047e28de640c7c9f16a1fa4d Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 5 Feb 2025 16:57:27 +0100 Subject: [PATCH 12/27] nit error handler --- .../windmill-api/src/postgres_triggers/handler.rs | 12 ++++++------ backend/windmill-api/src/postgres_triggers/mod.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/windmill-api/src/postgres_triggers/handler.rs b/backend/windmill-api/src/postgres_triggers/handler.rs index ad23938e5e..760be062f7 100644 --- a/backend/windmill-api/src/postgres_triggers/handler.rs +++ b/backend/windmill-api/src/postgres_triggers/handler.rs @@ -153,7 +153,7 @@ pub async fn get_raw_postgres_connection(db: &Database) -> Result(&sql) .fetch_all(&mut *tx) .await .map_err(|e| { tracing::debug!("Error fetching postgres_trigger: {:#?}", e); - windmill_common::error::Error::internal_err("server error".to_string()) + windmill_common::error::Error::InternalErr("server error".to_string()) })?; tx.commit().await.map_err(|e| { tracing::debug!("Error commiting postgres_trigger: {:#?}", e); - windmill_common::error::Error::internal_err("server error".to_string()) + windmill_common::error::Error::InternalErr("server error".to_string()) })?; Ok(Json(rows)) @@ -651,7 +651,7 @@ pub async fn get_publication_info( let (all_table, transaction_to_track) = match publication_data { Ok(pub_data) => pub_data, - Err(Error::SqlErr(sqlx::Error::RowNotFound)) => { + Err(Error::SqlErr { error: sqlx::Error::RowNotFound, .. }) => { return Err(Error::NotFound( "Publication was not found, please create a new publication".to_string(), )) @@ -1378,7 +1378,7 @@ pub async fn create_template_script( let rows: Vec = sqlx::query_as(&query) .fetch_all(&mut connection) .await - .map_err(error::Error::SqlErr)?; + .map_err(|e| error::Error::SqlErr { error: e, location: "pg_trigger".to_string() })?; let mut mapper: HashMap>> = HashMap::new(); diff --git a/backend/windmill-api/src/postgres_triggers/mod.rs b/backend/windmill-api/src/postgres_triggers/mod.rs index 0bd87ed262..8adb635030 100644 --- a/backend/windmill-api/src/postgres_triggers/mod.rs +++ b/backend/windmill-api/src/postgres_triggers/mod.rs @@ -52,7 +52,7 @@ pub async fn get_database_resource( .map_err(|_| Error::NotFound("Database resource do not exist".to_string()))?; let resource = match resource { - Some(resource) => serde_json::from_value::(resource).map_err(Error::SerdeJson)?, + Some(resource) => serde_json::from_value::(resource)?, None => { return { Err(Error::NotFound( From 645be25bef611474961b369cffe02936c6e6bea3 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 5 Feb 2025 17:15:38 +0100 Subject: [PATCH 13/27] nit --- backend/windmill-worker/src/mssql_executor.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/windmill-worker/src/mssql_executor.rs b/backend/windmill-worker/src/mssql_executor.rs index be5ea1e905..42d9c3aa8c 100644 --- a/backend/windmill-worker/src/mssql_executor.rs +++ b/backend/windmill-worker/src/mssql_executor.rs @@ -120,9 +120,11 @@ pub async fn do_mssql( let tcp = TcpStream::connect(config.get_addr()).await?; tcp.set_nodelay(true)?; - Client::connect(config, tcp.compat_write()).await.map_err(to_anyhow)? + Client::connect(config, tcp.compat_write()) + .await + .map_err(to_anyhow)? } - Err(e) => return Err(Error::Anyhow(to_anyhow(e))), + Err(e) => return Err(to_anyhow(e).into()), }; let sig = parse_mssql_sig(&query) From 1e5cd282d70ad79eaecbdbd8739177e28ae7c58a Mon Sep 17 00:00:00 2001 From: Guilhem Date: Wed, 5 Feb 2025 16:27:11 +0000 Subject: [PATCH 14/27] fix height refresh for flows (#5215) --- frontend/src/lib/components/graph/FlowGraphV2.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index 48f7fc9430..9b142927b2 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -1,7 +1,7 @@