From b211155784135b1377975a2759f2ddca1cffcea2 Mon Sep 17 00:00:00 2001
From: dieriba
Date: Wed, 15 Oct 2025 23:43:42 +0200
Subject: [PATCH 01/33] fix: support dyn select for sub flow (#6835)
* support subflow
* update
* Update frontend/src/lib/common.ts
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
---------
Co-authored-by: Ruben Fiszel
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
---
frontend/src/lib/common.ts | 4 +++-
.../components/flows/content/FlowModuleComponent.svelte | 7 +++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/frontend/src/lib/common.ts b/frontend/src/lib/common.ts
index f3dc46db7a..e5220a2655 100644
--- a/frontend/src/lib/common.ts
+++ b/frontend/src/lib/common.ts
@@ -1,4 +1,4 @@
-import type { Script } from './gen'
+import type { Script, ScriptLang } from './gen'
export type OwnerKind = 'group' | 'user' | 'folder'
@@ -109,6 +109,8 @@ export function modalToSchema(schema: ModalSchemaProperty): SchemaProperty {
export type Schema = {
$schema: string | undefined
type: string
+ "x-windmill-dyn-select-code"?: string
+ "x-windmill-dyn-select-lang"?: ScriptLang
properties: { [name: string]: SchemaProperty }
order?: string[]
required: string[]
diff --git a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte
index 3be61accba..01252b6a46 100644
--- a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte
+++ b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte
@@ -325,6 +325,13 @@
lang: value.language
}
break
+ case 'flow':
+ helperScript = {
+ source: 'deployed',
+ path: value.path,
+ runnable_kind: 'flow'
+ }
+ break
default:
helperScript = undefined
}
From d12c8f34efe5ebbdbbf85ae41bb11307dc5d8ea3 Mon Sep 17 00:00:00 2001
From: dieriba
Date: Thu, 16 Oct 2025 00:08:34 +0200
Subject: [PATCH 02/33] fix: gcp script picker (#6837)
* fix
* remove
---
.../lib/components/triggers/gcp/GcpTriggerEditorInner.svelte | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte
index abb3ab8782..3a14b27522 100644
--- a/frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte
+++ b/frontend/src/lib/components/triggers/gcp/GcpTriggerEditorInner.svelte
@@ -31,7 +31,6 @@
import Toggle from '$lib/components/Toggle.svelte'
let drawer: Drawer | undefined = $state(undefined)
- let is_flow: boolean = $state(false)
let initialPath = $state('')
let edit = $state(true)
let delivery_type: DeliveryType = $state('pull')
@@ -132,7 +131,6 @@
drawerLoading = true
try {
drawer?.openDrawer()
- is_flow = nis_flow
itemKind = nis_flow ? 'flow' : 'script'
initialScriptPath = ''
fixedScriptPath = fixedScriptPath_ ?? ''
@@ -184,7 +182,6 @@
subscription_id = cfg?.subscription_id
delivery_config = cfg?.delivery_config
subscription_mode = cfg?.subscription_mode
- is_flow = cfg?.is_flow
path = cfg?.path
enabled = cfg?.enabled
topic_id = cfg?.topic_id ?? ''
@@ -229,7 +226,7 @@
path,
script_path,
enabled,
- is_flow,
+ is_flow : itemKind === 'flow',
error_handler_path,
error_handler_args,
retry,
From 3d5631938f9dc6da6256868349776b53e1f202d8 Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Wed, 15 Oct 2025 11:46:46 +0000
Subject: [PATCH 03/33] error
---
backend/windmill-common/src/client.rs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/windmill-common/src/client.rs b/backend/windmill-common/src/client.rs
index a95ccbe292..b05fdfa2f7 100644
--- a/backend/windmill-common/src/client.rs
+++ b/backend/windmill-common/src/client.rs
@@ -39,8 +39,8 @@ impl AuthedClient {
.send()
.await
.map_err(|e| {
- tracing::error!("Error executing get request from authed http client to {url} with query {query:?}: {e}");
- anyhow::anyhow!("Error executing get request from authed http client to {url} with query {query:?}: {e}")
+ tracing::error!("Error executing get request from authed http client to {url} with query {query:?}: {e:#?}");
+ anyhow::anyhow!("Error executing get request from authed http client to {url} with query {query:?}: {e:#?}")
})
}
From 892ce64ea8550c22d65180c71f57c90a65583832 Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Wed, 15 Oct 2025 22:24:11 +0000
Subject: [PATCH 04/33] fix: fix concurrency key filter
---
backend/windmill-api/src/concurrency_groups.rs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/backend/windmill-api/src/concurrency_groups.rs b/backend/windmill-api/src/concurrency_groups.rs
index ba85043a73..db1341132c 100644
--- a/backend/windmill-api/src/concurrency_groups.rs
+++ b/backend/windmill-api/src/concurrency_groups.rs
@@ -159,22 +159,22 @@ async fn get_concurrent_intervals(
let lq = ListCompletedQuery { order_desc: Some(true), ..lq };
let lqc = lq.clone();
let lqq: ListQueueQuery = lqc.into();
- let mut sqlb_q = SqlBuilder::select_from("v2_as_queue")
+ let mut sqlb_q = SqlBuilder::select_from("v2_job_queue")
.fields(UnifiedJob::queued_job_fields())
.order_by("created_at", lq.order_desc.unwrap_or(true))
.limit(row_limit)
.clone();
- let mut sqlb_c = SqlBuilder::select_from("v2_as_completed_job")
+ let mut sqlb_c = SqlBuilder::select_from("v2_job_completed")
.fields(UnifiedJob::completed_job_fields())
.order_by("started_at", lq.order_desc.unwrap_or(true))
.limit(row_limit)
.clone();
- let mut sqlb_q_user = SqlBuilder::select_from("v2_as_queue")
+ let mut sqlb_q_user = SqlBuilder::select_from("v2_job_queue")
.fields(&["id"])
.order_by("created_at", lq.order_desc.unwrap_or(true))
.limit(row_limit)
.clone();
- let mut sqlb_c_user = SqlBuilder::select_from("v2_as_completed_job")
+ let mut sqlb_c_user = SqlBuilder::select_from("v2_job_completed")
.fields(&["id"])
.order_by("started_at", lq.order_desc.unwrap_or(true))
.limit(row_limit)
From 48acc57823792c9e795f9735712e1b2ed6d2b4e2 Mon Sep 17 00:00:00 2001
From: Alexander Petric
Date: Wed, 15 Oct 2025 18:30:09 -0400
Subject: [PATCH 05/33] fix: always create instance groups with uuid (#6826)
* fix: always create instance groups with uuid
* Update SQLx metadata
* repo ref
---------
Co-authored-by: windmill-internal-app[bot]
---
...1f7f387f5055c47f493271d26731336257384.json | 10 +--
...cc6d6bf1df758b30e99bd661da866062ef14f.json | 23 -----
...b21270131e9e93ca10d195664e7e5a774fe9e.json | 4 +-
...dbaa7c74ce0cfa1b945b7514e19a70f7a6f1c.json | 15 ----
...ef6bfdecc81700b89962c758c065d8d55f9e2.json | 16 ++++
...94f91df7e588d4d2431bc85f4d8734920c8bf.json | 51 -----------
...c38fc64deb1226aab9dc3bc4465324fce37d1.json | 16 ----
...ca3761d400391f1f46a8294da3e6c9af63887.json | 15 ----
...5ef756b8e5c1955fbe111df9ee171dc262338.json | 89 -------------------
...0cb549a34b96554ae1872355b90304f5dcb76.json | 4 +-
...212a5bd4039b57fab20b163617e33a4c9dd46.json | 14 ---
...77afbd8b3a660b3be27514b517c077c63c238.json | 89 -------------------
backend/ee-repo-ref.txt | 2 +-
...922_backfill_instance_group_uuids.down.sql | 4 +
...81922_backfill_instance_group_uuids.up.sql | 7 ++
backend/windmill-api/src/groups.rs | 6 +-
16 files changed, 41 insertions(+), 324 deletions(-)
delete mode 100644 backend/.sqlx/query-16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f.json
delete mode 100644 backend/.sqlx/query-383b8adaadc6e23952ca8942fe7dbaa7c74ce0cfa1b945b7514e19a70f7a6f1c.json
create mode 100644 backend/.sqlx/query-59c63c5e3ce0b4976133cd65258ef6bfdecc81700b89962c758c065d8d55f9e2.json
delete mode 100644 backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json
delete mode 100644 backend/.sqlx/query-85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1.json
delete mode 100644 backend/.sqlx/query-a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887.json
delete mode 100644 backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json
delete mode 100644 backend/.sqlx/query-b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46.json
delete mode 100644 backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json
create mode 100644 backend/migrations/20251014181922_backfill_instance_group_uuids.down.sql
create mode 100644 backend/migrations/20251014181922_backfill_instance_group_uuids.up.sql
diff --git a/backend/.sqlx/query-07168aaf14cb6beff0ad4274b441f7f387f5055c47f493271d26731336257384.json b/backend/.sqlx/query-07168aaf14cb6beff0ad4274b441f7f387f5055c47f493271d26731336257384.json
index d29a18c691..e7ed0aee65 100644
--- a/backend/.sqlx/query-07168aaf14cb6beff0ad4274b441f7f387f5055c47f493271d26731336257384.json
+++ b/backend/.sqlx/query-07168aaf14cb6beff0ad4274b441f7f387f5055c47f493271d26731336257384.json
@@ -46,11 +46,11 @@
]
},
"nullable": [
- true,
- true,
- true,
- true,
- true,
+ false,
+ false,
+ false,
+ false,
+ false,
true,
true
]
diff --git a/backend/.sqlx/query-16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f.json b/backend/.sqlx/query-16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f.json
deleted file mode 100644
index 1af42ff529..0000000000
--- a/backend/.sqlx/query-16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "SELECT hash FROM script WHERE path = $1 AND workspace_id = $2",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "hash",
- "type_info": "Int8"
- }
- ],
- "parameters": {
- "Left": [
- "Text",
- "Text"
- ]
- },
- "nullable": [
- false
- ]
- },
- "hash": "16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f"
-}
diff --git a/backend/.sqlx/query-1a54356c1e1353950bf6ab1d25ab21270131e9e93ca10d195664e7e5a774fe9e.json b/backend/.sqlx/query-1a54356c1e1353950bf6ab1d25ab21270131e9e93ca10d195664e7e5a774fe9e.json
index 66453d2659..9462f9b48b 100644
--- a/backend/.sqlx/query-1a54356c1e1353950bf6ab1d25ab21270131e9e93ca10d195664e7e5a774fe9e.json
+++ b/backend/.sqlx/query-1a54356c1e1353950bf6ab1d25ab21270131e9e93ca10d195664e7e5a774fe9e.json
@@ -59,9 +59,7 @@
"failure",
"command",
"approval",
- "preprocessor",
- "schedule_handler_old",
- "dynamic_skip"
+ "preprocessor"
]
}
}
diff --git a/backend/.sqlx/query-383b8adaadc6e23952ca8942fe7dbaa7c74ce0cfa1b945b7514e19a70f7a6f1c.json b/backend/.sqlx/query-383b8adaadc6e23952ca8942fe7dbaa7c74ce0cfa1b945b7514e19a70f7a6f1c.json
deleted file mode 100644
index db96fe6009..0000000000
--- a/backend/.sqlx/query-383b8adaadc6e23952ca8942fe7dbaa7c74ce0cfa1b945b7514e19a70f7a6f1c.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO instance_group (name, summary) VALUES ($1, $2) ON CONFLICT DO NOTHING",
- "describe": {
- "columns": [],
- "parameters": {
- "Left": [
- "Varchar",
- "Varchar"
- ]
- },
- "nullable": []
- },
- "hash": "383b8adaadc6e23952ca8942fe7dbaa7c74ce0cfa1b945b7514e19a70f7a6f1c"
-}
diff --git a/backend/.sqlx/query-59c63c5e3ce0b4976133cd65258ef6bfdecc81700b89962c758c065d8d55f9e2.json b/backend/.sqlx/query-59c63c5e3ce0b4976133cd65258ef6bfdecc81700b89962c758c065d8d55f9e2.json
new file mode 100644
index 0000000000..f2c1cd78bf
--- /dev/null
+++ b/backend/.sqlx/query-59c63c5e3ce0b4976133cd65258ef6bfdecc81700b89962c758c065d8d55f9e2.json
@@ -0,0 +1,16 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "INSERT INTO instance_group (name, summary, id) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Varchar",
+ "Varchar",
+ "Varchar"
+ ]
+ },
+ "nullable": []
+ },
+ "hash": "59c63c5e3ce0b4976133cd65258ef6bfdecc81700b89962c758c065d8d55f9e2"
+}
diff --git a/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json b/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json
deleted file mode 100644
index 6f08d98113..0000000000
--- a/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json
+++ /dev/null
@@ -1,51 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO script (summary, description, dedicated_worker, content, workspace_id, path, hash, language, tag, created_by, lock) VALUES ('', '', true, $1, $2, $3, $4, $5, $6, $7, '') ON CONFLICT (workspace_id, hash) DO NOTHING",
- "describe": {
- "columns": [],
- "parameters": {
- "Left": [
- "Text",
- "Varchar",
- "Varchar",
- "Int8",
- {
- "Custom": {
- "name": "script_lang",
- "kind": {
- "Enum": [
- "python3",
- "deno",
- "go",
- "bash",
- "postgresql",
- "nativets",
- "bun",
- "mysql",
- "bigquery",
- "snowflake",
- "graphql",
- "powershell",
- "mssql",
- "php",
- "bunnative",
- "rust",
- "ansible",
- "csharp",
- "oracledb",
- "nu",
- "java",
- "duckdb",
- "ruby"
- ]
- }
- }
- },
- "Varchar",
- "Varchar"
- ]
- },
- "nullable": []
- },
- "hash": "804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf"
-}
diff --git a/backend/.sqlx/query-85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1.json b/backend/.sqlx/query-85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1.json
deleted file mode 100644
index bce7324fb6..0000000000
--- a/backend/.sqlx/query-85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO v2_job_queue (id, workspace_id, scheduled_for, tag) SELECT unnest($1::uuid[]), $2, now(), $3",
- "describe": {
- "columns": [],
- "parameters": {
- "Left": [
- "UuidArray",
- "Varchar",
- "Varchar"
- ]
- },
- "nullable": []
- },
- "hash": "85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1"
-}
diff --git a/backend/.sqlx/query-a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887.json b/backend/.sqlx/query-a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887.json
deleted file mode 100644
index 1afc61978e..0000000000
--- a/backend/.sqlx/query-a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO v2_job_status (id, flow_status) SELECT unnest($1::uuid[]), $2",
- "describe": {
- "columns": [],
- "parameters": {
- "Left": [
- "UuidArray",
- "Jsonb"
- ]
- },
- "nullable": []
- },
- "hash": "a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887"
-}
diff --git a/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json b/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json
deleted file mode 100644
index fe1a67b0d4..0000000000
--- a/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json
+++ /dev/null
@@ -1,89 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO v2_job (id, runnable_id, runnable_path, kind, script_lang, tag, created_by, permissioned_as, permissioned_as_email, workspace_id, raw_flow) (SELECT gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10 FROM generate_series(1, 1)) RETURNING id",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "id",
- "type_info": "Uuid"
- }
- ],
- "parameters": {
- "Left": [
- "Int8",
- "Varchar",
- {
- "Custom": {
- "name": "job_kind",
- "kind": {
- "Enum": [
- "script",
- "preview",
- "flow",
- "dependencies",
- "flowpreview",
- "script_hub",
- "identity",
- "flowdependencies",
- "http",
- "graphql",
- "postgresql",
- "noop",
- "appdependencies",
- "deploymentcallback",
- "singlestepflow",
- "flowscript",
- "flownode",
- "appscript",
- "aiagent"
- ]
- }
- }
- },
- {
- "Custom": {
- "name": "script_lang",
- "kind": {
- "Enum": [
- "python3",
- "deno",
- "go",
- "bash",
- "postgresql",
- "nativets",
- "bun",
- "mysql",
- "bigquery",
- "snowflake",
- "graphql",
- "powershell",
- "mssql",
- "php",
- "bunnative",
- "rust",
- "ansible",
- "csharp",
- "oracledb",
- "nu",
- "java",
- "duckdb",
- "ruby"
- ]
- }
- }
- },
- "Varchar",
- "Varchar",
- "Varchar",
- "Varchar",
- "Varchar",
- "Jsonb"
- ]
- },
- "nullable": [
- false
- ]
- },
- "hash": "ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338"
-}
diff --git a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json
index 99269c9851..54e94cfb8f 100644
--- a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json
+++ b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json
@@ -18,8 +18,8 @@
"Left": []
},
"nullable": [
- true,
- false
+ false,
+ true
]
},
"hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76"
diff --git a/backend/.sqlx/query-b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46.json b/backend/.sqlx/query-b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46.json
deleted file mode 100644
index a49baeefaf..0000000000
--- a/backend/.sqlx/query-b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO v2_job_runtime (id) SELECT unnest($1::uuid[])",
- "describe": {
- "columns": [],
- "parameters": {
- "Left": [
- "UuidArray"
- ]
- },
- "nullable": []
- },
- "hash": "b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46"
-}
diff --git a/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json b/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json
deleted file mode 100644
index 00662e2fd2..0000000000
--- a/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json
+++ /dev/null
@@ -1,89 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO v2_job (id, runnable_id, runnable_path, kind, script_lang, tag, created_by, permissioned_as, permissioned_as_email, workspace_id) (SELECT gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8, $9 FROM generate_series(1, $10)) RETURNING id",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "id",
- "type_info": "Uuid"
- }
- ],
- "parameters": {
- "Left": [
- "Int8",
- "Varchar",
- {
- "Custom": {
- "name": "job_kind",
- "kind": {
- "Enum": [
- "script",
- "preview",
- "flow",
- "dependencies",
- "flowpreview",
- "script_hub",
- "identity",
- "flowdependencies",
- "http",
- "graphql",
- "postgresql",
- "noop",
- "appdependencies",
- "deploymentcallback",
- "singlestepflow",
- "flowscript",
- "flownode",
- "appscript",
- "aiagent"
- ]
- }
- }
- },
- {
- "Custom": {
- "name": "script_lang",
- "kind": {
- "Enum": [
- "python3",
- "deno",
- "go",
- "bash",
- "postgresql",
- "nativets",
- "bun",
- "mysql",
- "bigquery",
- "snowflake",
- "graphql",
- "powershell",
- "mssql",
- "php",
- "bunnative",
- "rust",
- "ansible",
- "csharp",
- "oracledb",
- "nu",
- "java",
- "duckdb",
- "ruby"
- ]
- }
- }
- },
- "Varchar",
- "Varchar",
- "Varchar",
- "Varchar",
- "Varchar",
- "Int4"
- ]
- },
- "nullable": [
- false
- ]
- },
- "hash": "ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238"
-}
diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt
index cac3213232..12cf35dc1b 100644
--- a/backend/ee-repo-ref.txt
+++ b/backend/ee-repo-ref.txt
@@ -1 +1 @@
-19909371503adfa12106dbf261d7f4134989a204
+c7d34190819c83b4dfe62498a47ab9b439a321a9
diff --git a/backend/migrations/20251014181922_backfill_instance_group_uuids.down.sql b/backend/migrations/20251014181922_backfill_instance_group_uuids.down.sql
new file mode 100644
index 0000000000..d130324837
--- /dev/null
+++ b/backend/migrations/20251014181922_backfill_instance_group_uuids.down.sql
@@ -0,0 +1,4 @@
+-- Add down migration script here
+
+-- This migration is irreversible as we cannot safely remove UUIDs
+-- that may already be in use by SCIM clients
diff --git a/backend/migrations/20251014181922_backfill_instance_group_uuids.up.sql b/backend/migrations/20251014181922_backfill_instance_group_uuids.up.sql
new file mode 100644
index 0000000000..d947b80fad
--- /dev/null
+++ b/backend/migrations/20251014181922_backfill_instance_group_uuids.up.sql
@@ -0,0 +1,7 @@
+-- Add up migration script here
+
+-- Backfill UUIDs for instance groups that don't have one
+-- This is needed for SCIM compatibility where groups must have stable UUIDs
+UPDATE instance_group
+SET id = gen_random_uuid()::text
+WHERE id IS NULL;
diff --git a/backend/windmill-api/src/groups.rs b/backend/windmill-api/src/groups.rs
index 4c526ffe26..afe73f3bc8 100644
--- a/backend/windmill-api/src/groups.rs
+++ b/backend/windmill-api/src/groups.rs
@@ -286,13 +286,17 @@ async fn create_igroup(
Extension(db): Extension,
Json(ng): Json,
) -> Result {
+ use uuid::Uuid;
+
require_super_admin(&db, &authed.email).await?;
let mut tx = db.begin().await?;
+ let id = Uuid::new_v4().to_string();
sqlx::query!(
- "INSERT INTO instance_group (name, summary) VALUES ($1, $2) ON CONFLICT DO NOTHING",
+ "INSERT INTO instance_group (name, summary, id) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING",
ng.name,
ng.summary,
+ id,
)
.execute(&mut *tx)
.await?;
From d75e9e3d92d43f449a6296b367018f8fa3da6507 Mon Sep 17 00:00:00 2001
From: Alexander Petric
Date: Wed, 15 Oct 2025 18:32:54 -0400
Subject: [PATCH 06/33] feat: build pydoc for wmill python client and mount in
container image (#6828)
---
Dockerfile | 1 +
python-client/.gitignore | 1 +
python-client/DOCS.md | 128 +
python-client/build.sh | 5 +-
python-client/build_pdoc.sh | 19 +
python-client/docs/index.html | 7 +
python-client/docs/search.js | 46 +
python-client/docs/wmill.html | 240 +
python-client/docs/wmill/client.html | 6459 +++++++++++++++++++++++
python-client/docs/wmill/s3_reader.html | 550 ++
python-client/docs/wmill/s3_types.html | 841 +++
11 files changed, 8296 insertions(+), 1 deletion(-)
create mode 100644 python-client/DOCS.md
create mode 100755 python-client/build_pdoc.sh
create mode 100644 python-client/docs/index.html
create mode 100644 python-client/docs/search.js
create mode 100644 python-client/docs/wmill.html
create mode 100644 python-client/docs/wmill/client.html
create mode 100644 python-client/docs/wmill/s3_reader.html
create mode 100644 python-client/docs/wmill/s3_types.html
diff --git a/Dockerfile b/Dockerfile
index 4f3bb4f7d6..b3f18b4e17 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -48,6 +48,7 @@ COPY /backend/windmill-api/build_openapi.sh /backend/windmill-api/build_openapi.
RUN cd /backend/windmill-api && . ./build_openapi.sh
COPY /backend/parsers/windmill-parser-wasm/pkg/ /backend/parsers/windmill-parser-wasm/pkg/
COPY /typescript-client/docs/ /frontend/static/tsdocs/
+COPY /python-client/docs/ /frontend/static/pydocs/
RUN npm run generate-backend-client
ENV NODE_OPTIONS "--max-old-space-size=8192"
diff --git a/python-client/.gitignore b/python-client/.gitignore
index 0a6e1f82df..3b7cf22936 100644
--- a/python-client/.gitignore
+++ b/python-client/.gitignore
@@ -1 +1,2 @@
windmill-api/
+.venv/
diff --git a/python-client/DOCS.md b/python-client/DOCS.md
new file mode 100644
index 0000000000..58a0a2c6ab
--- /dev/null
+++ b/python-client/DOCS.md
@@ -0,0 +1,128 @@
+# Python Client Documentation
+
+This document describes how the Python client documentation is generated and deployed, similar to the TypeScript client.
+
+## Overview
+
+The Python client uses **pdoc** to automatically generate API documentation from docstrings in the code, similar to how the TypeScript client uses TypeDoc.
+
+## Architecture
+
+Following the same pattern as the TypeScript client:
+
+1. **Documentation Generator**: pdoc (Python) vs TypeDoc (TypeScript)
+2. **Build Script**: `build_pdoc.sh` (similar to `build_typedoc.sh` for TS)
+3. **Output Directory**: `docs/` containing static HTML
+4. **Deployment**: Copied to `/frontend/static/pydocs/` during Docker build
+5. **URL**: Accessible at `https://app.windmill.dev/pydocs/wmill.html`
+
+## Building Documentation
+
+### How It Works
+
+The Python client documentation follows the same pattern as the TypeScript client:
+
+1. **Docs are checked into git** - The `docs/` directory is committed to the repository
+2. **Built during releases** - When `./build.sh` runs (on releases or manually), it calls `./build_pdoc.sh`
+3. **Copied during Docker build** - The Dockerfile copies pre-built docs to the frontend
+
+### Locally
+
+To build/update the documentation:
+
+```bash
+cd /path/to/windmill/python-client
+./build_pdoc.sh
+```
+
+This will:
+1. Create a virtual environment if needed (`.venv/` - gitignored)
+2. Install pdoc and dependencies
+3. Generate HTML documentation in `./docs/`
+4. Documentation will be available at `file://$(pwd)/docs/wmill.html`
+
+After building, you should commit the updated docs:
+```bash
+git add docs/
+git commit -m "Update Python client documentation"
+```
+
+### In CI/CD
+
+The documentation is built automatically during the release process:
+
+1. **On release** (`pypi_on_release.yml` workflow triggers on version tags)
+2. Runs `./publish.sh` → calls `./build.sh` → calls `./build_pdoc.sh`
+3. Docs are generated and should be committed separately or before the release
+
+### In Docker Build
+
+The documentation is copied during Docker build (see `Dockerfile` line 51):
+
+```dockerfile
+COPY /python-client/docs/ /frontend/static/pydocs/
+```
+
+This makes the docs available at `https://app.windmill.dev/pydocs/` in production.
+
+## Documentation Structure
+
+The generated documentation includes:
+
+- **Main Module** (`wmill.html`): Overview and module-level functions
+- **Client Class** (`wmill/client.html`): Full Windmill class API reference
+- **S3 Types** (`wmill/s3_types.html`): S3 integration types and helpers
+- **S3 Reader** (`wmill/s3_reader.html`): S3 file reading utilities
+
+All documentation is automatically generated from:
+- Function/class docstrings
+- Type hints
+- Parameter descriptions
+- Return type annotations
+
+## Writing Good Documentation
+
+To maintain quality documentation:
+
+1. **Use clear docstrings** following Google or NumPy style:
+ ```python
+ def my_function(param1: str, param2: int = 0) -> dict:
+ """Short description of function.
+
+ Longer description with more details about what the function does
+ and any important notes.
+
+ Args:
+ param1: Description of param1
+ param2: Description of param2 (default: 0)
+
+ Returns:
+ Description of return value
+
+ Example:
+ >>> result = my_function("test", 5)
+ >>> print(result)
+ {'status': 'ok'}
+ """
+ ```
+
+2. **Add type hints** - pdoc uses them to generate better documentation
+3. **Include examples** in docstrings where helpful
+4. **Keep descriptions concise** but complete
+
+## Comparison with TypeScript Client
+
+| Aspect | TypeScript | Python |
+|--------|-----------|--------|
+| Generator | TypeDoc | pdoc |
+| Build Script | `build_typedoc.sh` | `build_pdoc.sh` |
+| Output Dir | `docs/` | `docs/` |
+| Hosted At | `/tsdocs/` | `/pydocs/` |
+| Entry Point | `modules.html` | `wmill.html` |
+
+## References
+
+- Windmill Docs: https://windmilldocs/docs/advanced/2_clients/python_client.md
+- TypeScript Client Docs: https://app.windmill.dev/tsdocs/modules.html
+- Python Client Docs: https://app.windmill.dev/pydocs/wmill.html
+- pdoc Documentation: https://pdoc.dev/
diff --git a/python-client/build.sh b/python-client/build.sh
index 780806388e..4224f77bad 100755
--- a/python-client/build.sh
+++ b/python-client/build.sh
@@ -74,4 +74,7 @@ mv windmill-api/README.md.tmp windmill-api/README.md
cd windmill-api && poetry build
cd ../wmill && poetry build
cd ../wmill_pg && poetry build
-cd .. && echo "windmill-api/" >> .gitignore
+cd .. && echo "windmill-api/" >> .gitignore
+
+# Build documentation (similar to typescript-client/build_typedoc.sh)
+./build_pdoc.sh
diff --git a/python-client/build_pdoc.sh b/python-client/build_pdoc.sh
new file mode 100755
index 0000000000..53df696331
--- /dev/null
+++ b/python-client/build_pdoc.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+# Build Python documentation with pdoc
+# Similar to typescript-client/build_typedoc.sh
+
+set -e
+
+# Create/activate virtual environment if needed
+if [ ! -d ".venv" ]; then
+ python3 -m venv .venv
+ .venv/bin/pip install -q pdoc httpx
+fi
+
+# Install the package
+.venv/bin/pip install -q ./wmill
+
+# Generate documentation
+.venv/bin/pdoc wmill -o docs
+
+echo "Python documentation built successfully in ./docs/"
diff --git a/python-client/docs/index.html b/python-client/docs/index.html
new file mode 100644
index 0000000000..6cb034b833
--- /dev/null
+++ b/python-client/docs/index.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/python-client/docs/search.js b/python-client/docs/search.js
new file mode 100644
index 0000000000..960f5fe457
--- /dev/null
+++ b/python-client/docs/search.js
@@ -0,0 +1,46 @@
+window.pdocSearch = (function(){
+/** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o
\n"}, "wmill.client": {"fullname": "wmill.client", "modulename": "wmill.client", "kind": "module", "doc": "
\n"}, "wmill.client.logger": {"fullname": "wmill.client.logger", "modulename": "wmill.client", "qualname": "logger", "kind": "variable", "doc": "
\n", "default_value": "<Logger windmill_client (WARNING)>"}, "wmill.client.JobStatus": {"fullname": "wmill.client.JobStatus", "modulename": "wmill.client", "qualname": "JobStatus", "kind": "variable", "doc": "
\n", "default_value": "typing.Literal['RUNNING', 'WAITING', 'COMPLETED']"}, "wmill.client.Windmill": {"fullname": "wmill.client.Windmill", "modulename": "wmill.client", "qualname": "Windmill", "kind": "class", "doc": "
\n"}, "wmill.client.Windmill.__init__": {"fullname": "wmill.client.Windmill.__init__", "modulename": "wmill.client", "qualname": "Windmill.__init__", "kind": "function", "doc": "
\n", "signature": "(base_url = None , token = None , workspace = None , verify = True ) "}, "wmill.client.Windmill.base_url": {"fullname": "wmill.client.Windmill.base_url", "modulename": "wmill.client", "qualname": "Windmill.base_url", "kind": "variable", "doc": "
\n"}, "wmill.client.Windmill.token": {"fullname": "wmill.client.Windmill.token", "modulename": "wmill.client", "qualname": "Windmill.token", "kind": "variable", "doc": "
\n"}, "wmill.client.Windmill.headers": {"fullname": "wmill.client.Windmill.headers", "modulename": "wmill.client", "qualname": "Windmill.headers", "kind": "variable", "doc": "
\n"}, "wmill.client.Windmill.verify": {"fullname": "wmill.client.Windmill.verify", "modulename": "wmill.client", "qualname": "Windmill.verify", "kind": "variable", "doc": "
\n"}, "wmill.client.Windmill.client": {"fullname": "wmill.client.Windmill.client", "modulename": "wmill.client", "qualname": "Windmill.client", "kind": "variable", "doc": "
\n"}, "wmill.client.Windmill.workspace": {"fullname": "wmill.client.Windmill.workspace", "modulename": "wmill.client", "qualname": "Windmill.workspace", "kind": "variable", "doc": "
\n"}, "wmill.client.Windmill.path": {"fullname": "wmill.client.Windmill.path", "modulename": "wmill.client", "qualname": "Windmill.path", "kind": "variable", "doc": "
\n"}, "wmill.client.Windmill.mocked_api": {"fullname": "wmill.client.Windmill.mocked_api", "modulename": "wmill.client", "qualname": "Windmill.mocked_api", "kind": "variable", "doc": "
\n"}, "wmill.client.Windmill.get_mocked_api": {"fullname": "wmill.client.Windmill.get_mocked_api", "modulename": "wmill.client", "qualname": "Windmill.get_mocked_api", "kind": "function", "doc": "
\n", "signature": "(self ) -> Optional [ dict ] : ", "funcdef": "def"}, "wmill.client.Windmill.get_client": {"fullname": "wmill.client.Windmill.get_client", "modulename": "wmill.client", "qualname": "Windmill.get_client", "kind": "function", "doc": "
\n", "signature": "(self ) -> httpx . Client : ", "funcdef": "def"}, "wmill.client.Windmill.get": {"fullname": "wmill.client.Windmill.get", "modulename": "wmill.client", "qualname": "Windmill.get", "kind": "function", "doc": "
\n", "signature": "(self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response : ", "funcdef": "def"}, "wmill.client.Windmill.post": {"fullname": "wmill.client.Windmill.post", "modulename": "wmill.client", "qualname": "Windmill.post", "kind": "function", "doc": "
\n", "signature": "(self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response : ", "funcdef": "def"}, "wmill.client.Windmill.create_token": {"fullname": "wmill.client.Windmill.create_token", "modulename": "wmill.client", "qualname": "Windmill.create_token", "kind": "function", "doc": "
\n", "signature": "(self , duration = datetime . timedelta ( days = 1 ) ) -> str : ", "funcdef": "def"}, "wmill.client.Windmill.run_script_async": {"fullname": "wmill.client.Windmill.run_script_async", "modulename": "wmill.client", "qualname": "Windmill.run_script_async", "kind": "function", "doc": "Create a script job and return its job id.
\n\nDeprecated since version Use run_script_by_path_async or run_script_by_hash_async instead..
\n", "signature": "(\tself , \tpath : str = None , \thash_ : str = None , \targs : dict = None , \tscheduled_in_secs : int = None ) -> str : ", "funcdef": "def"}, "wmill.client.Windmill.run_script_by_path_async": {"fullname": "wmill.client.Windmill.run_script_by_path_async", "modulename": "wmill.client", "qualname": "Windmill.run_script_by_path_async", "kind": "function", "doc": "Create a script job by path and return its job id.
\n", "signature": "(self , path : str , args : dict = None , scheduled_in_secs : int = None ) -> str : ", "funcdef": "def"}, "wmill.client.Windmill.run_script_by_hash_async": {"fullname": "wmill.client.Windmill.run_script_by_hash_async", "modulename": "wmill.client", "qualname": "Windmill.run_script_by_hash_async", "kind": "function", "doc": "Create a script job by hash and return its job id.
\n", "signature": "(\tself , \thash_ : str , \targs : dict = None , \tscheduled_in_secs : int = None ) -> str : ", "funcdef": "def"}, "wmill.client.Windmill.run_flow_async": {"fullname": "wmill.client.Windmill.run_flow_async", "modulename": "wmill.client", "qualname": "Windmill.run_flow_async", "kind": "function", "doc": "Create a flow job and return its job id.
\n", "signature": "(\tself , \tpath : str , \targs : dict = None , \tscheduled_in_secs : int = None , \tdo_not_track_in_parent : bool = True ) -> str : ", "funcdef": "def"}, "wmill.client.Windmill.run_script": {"fullname": "wmill.client.Windmill.run_script", "modulename": "wmill.client", "qualname": "Windmill.run_script", "kind": "function", "doc": "Run script synchronously and return its result.
\n\nDeprecated since version Use run_script_by_path or run_script_by_hash instead..
\n", "signature": "(\tself , \tpath : str = None , \thash_ : str = None , \targs : dict = None , \ttimeout : datetime . timedelta | int | float | None = None , \tverbose : bool = False , \tcleanup : bool = True , \tassert_result_is_not_none : bool = False ) -> Any : ", "funcdef": "def"}, "wmill.client.Windmill.run_script_by_path": {"fullname": "wmill.client.Windmill.run_script_by_path", "modulename": "wmill.client", "qualname": "Windmill.run_script_by_path", "kind": "function", "doc": "Run script by path synchronously and return its result.
\n", "signature": "(\tself , \tpath : str , \targs : dict = None , \ttimeout : datetime . timedelta | int | float | None = None , \tverbose : bool = False , \tcleanup : bool = True , \tassert_result_is_not_none : bool = False ) -> Any : ", "funcdef": "def"}, "wmill.client.Windmill.run_script_by_hash": {"fullname": "wmill.client.Windmill.run_script_by_hash", "modulename": "wmill.client", "qualname": "Windmill.run_script_by_hash", "kind": "function", "doc": "Run script by hash synchronously and return its result.
\n", "signature": "(\tself , \thash_ : str , \targs : dict = None , \ttimeout : datetime . timedelta | int | float | None = None , \tverbose : bool = False , \tcleanup : bool = True , \tassert_result_is_not_none : bool = False ) -> Any : ", "funcdef": "def"}, "wmill.client.Windmill.wait_job": {"fullname": "wmill.client.Windmill.wait_job", "modulename": "wmill.client", "qualname": "Windmill.wait_job", "kind": "function", "doc": "
\n", "signature": "(\tself , \tjob_id , \ttimeout : datetime . timedelta | int | float | None = None , \tverbose : bool = False , \tcleanup : bool = True , \tassert_result_is_not_none : bool = False ): ", "funcdef": "def"}, "wmill.client.Windmill.cancel_running": {"fullname": "wmill.client.Windmill.cancel_running", "modulename": "wmill.client", "qualname": "Windmill.cancel_running", "kind": "function", "doc": "Cancel currently running executions of the same script.
\n", "signature": "(self ) -> dict : ", "funcdef": "def"}, "wmill.client.Windmill.get_job": {"fullname": "wmill.client.Windmill.get_job", "modulename": "wmill.client", "qualname": "Windmill.get_job", "kind": "function", "doc": "
\n", "signature": "(self , job_id : str ) -> dict : ", "funcdef": "def"}, "wmill.client.Windmill.get_root_job_id": {"fullname": "wmill.client.Windmill.get_root_job_id", "modulename": "wmill.client", "qualname": "Windmill.get_root_job_id", "kind": "function", "doc": "
\n", "signature": "(self , job_id : str | None = None ) -> dict : ", "funcdef": "def"}, "wmill.client.Windmill.get_id_token": {"fullname": "wmill.client.Windmill.get_id_token", "modulename": "wmill.client", "qualname": "Windmill.get_id_token", "kind": "function", "doc": "
\n", "signature": "(self , audience : str ) -> str : ", "funcdef": "def"}, "wmill.client.Windmill.get_job_status": {"fullname": "wmill.client.Windmill.get_job_status", "modulename": "wmill.client", "qualname": "Windmill.get_job_status", "kind": "function", "doc": "
\n", "signature": "(self , job_id : str ) -> Literal [ 'RUNNING' , 'WAITING' , 'COMPLETED' ] : ", "funcdef": "def"}, "wmill.client.Windmill.get_result": {"fullname": "wmill.client.Windmill.get_result", "modulename": "wmill.client", "qualname": "Windmill.get_result", "kind": "function", "doc": "
\n", "signature": "(self , job_id : str , assert_result_is_not_none : bool = True ) -> Any : ", "funcdef": "def"}, "wmill.client.Windmill.get_variable": {"fullname": "wmill.client.Windmill.get_variable", "modulename": "wmill.client", "qualname": "Windmill.get_variable", "kind": "function", "doc": "
\n", "signature": "(self , path : str ) -> str : ", "funcdef": "def"}, "wmill.client.Windmill.set_variable": {"fullname": "wmill.client.Windmill.set_variable", "modulename": "wmill.client", "qualname": "Windmill.set_variable", "kind": "function", "doc": "
\n", "signature": "(self , path : str , value : str , is_secret : bool = False ) -> None : ", "funcdef": "def"}, "wmill.client.Windmill.get_resource": {"fullname": "wmill.client.Windmill.get_resource", "modulename": "wmill.client", "qualname": "Windmill.get_resource", "kind": "function", "doc": "
\n", "signature": "(self , path : str , none_if_undefined : bool = False ) -> dict | None : ", "funcdef": "def"}, "wmill.client.Windmill.set_resource": {"fullname": "wmill.client.Windmill.set_resource", "modulename": "wmill.client", "qualname": "Windmill.set_resource", "kind": "function", "doc": "
\n", "signature": "(self , value : Any , path : str , resource_type : str ): ", "funcdef": "def"}, "wmill.client.Windmill.set_state": {"fullname": "wmill.client.Windmill.set_state", "modulename": "wmill.client", "qualname": "Windmill.set_state", "kind": "function", "doc": "
\n", "signature": "(self , value : Any ): ", "funcdef": "def"}, "wmill.client.Windmill.set_progress": {"fullname": "wmill.client.Windmill.set_progress", "modulename": "wmill.client", "qualname": "Windmill.set_progress", "kind": "function", "doc": "
\n", "signature": "(self , value : int , job_id : Optional [ str ] = None ): ", "funcdef": "def"}, "wmill.client.Windmill.get_progress": {"fullname": "wmill.client.Windmill.get_progress", "modulename": "wmill.client", "qualname": "Windmill.get_progress", "kind": "function", "doc": "
\n", "signature": "(self , job_id : Optional [ str ] = None ) -> Any : ", "funcdef": "def"}, "wmill.client.Windmill.set_flow_user_state": {"fullname": "wmill.client.Windmill.set_flow_user_state", "modulename": "wmill.client", "qualname": "Windmill.set_flow_user_state", "kind": "function", "doc": "Set the user state of a flow at a given key
\n", "signature": "(self , key : str , value : Any ) -> None : ", "funcdef": "def"}, "wmill.client.Windmill.get_flow_user_state": {"fullname": "wmill.client.Windmill.get_flow_user_state", "modulename": "wmill.client", "qualname": "Windmill.get_flow_user_state", "kind": "function", "doc": "Get the user state of a flow at a given key
\n", "signature": "(self , key : str ) -> Any : ", "funcdef": "def"}, "wmill.client.Windmill.version": {"fullname": "wmill.client.Windmill.version", "modulename": "wmill.client", "qualname": "Windmill.version", "kind": "variable", "doc": "
\n"}, "wmill.client.Windmill.get_duckdb_connection_settings": {"fullname": "wmill.client.Windmill.get_duckdb_connection_settings", "modulename": "wmill.client", "qualname": "Windmill.get_duckdb_connection_settings", "kind": "function", "doc": "Convenient helpers that takes an S3 resource as input and returns the settings necessary to\ninitiate an S3 connection from DuckDB
\n", "signature": "(\tself , \ts3_resource_path : str = '' ) -> wmill . s3_types . DuckDbConnectionSettings | None : ", "funcdef": "def"}, "wmill.client.Windmill.get_polars_connection_settings": {"fullname": "wmill.client.Windmill.get_polars_connection_settings", "modulename": "wmill.client", "qualname": "Windmill.get_polars_connection_settings", "kind": "function", "doc": "Convenient helpers that takes an S3 resource as input and returns the settings necessary to\ninitiate an S3 connection from Polars
\n", "signature": "(\tself , \ts3_resource_path : str = '' ) -> wmill . s3_types . PolarsConnectionSettings : ", "funcdef": "def"}, "wmill.client.Windmill.get_boto3_connection_settings": {"fullname": "wmill.client.Windmill.get_boto3_connection_settings", "modulename": "wmill.client", "qualname": "Windmill.get_boto3_connection_settings", "kind": "function", "doc": "Convenient helpers that takes an S3 resource as input and returns the settings necessary to\ninitiate an S3 connection using boto3
\n", "signature": "(\tself , \ts3_resource_path : str = '' ) -> wmill . s3_types . Boto3ConnectionSettings : ", "funcdef": "def"}, "wmill.client.Windmill.load_s3_file": {"fullname": "wmill.client.Windmill.load_s3_file", "modulename": "wmill.client", "qualname": "Windmill.load_s3_file", "kind": "function", "doc": "Load a file from the workspace s3 bucket and returns its content as bytes.
\n\n'''python\nfrom wmill import S3Object
\n\ns3_obj = S3Object(s3=\"/path/to/my_file.txt\")\nmy_obj_content = client.load_s3_file(s3_obj)\nfile_content = my_obj_content.decode(\"utf-8\")\n'''
\n", "signature": "(\tself , \ts3object : wmill . s3_types . S3Object | str , \ts3_resource_path : str | None ) -> bytes : ", "funcdef": "def"}, "wmill.client.Windmill.load_s3_file_reader": {"fullname": "wmill.client.Windmill.load_s3_file_reader", "modulename": "wmill.client", "qualname": "Windmill.load_s3_file_reader", "kind": "function", "doc": "Load a file from the workspace s3 bucket and returns the bytes stream.
\n\n'''python\nfrom wmill import S3Object
\n\ns3_obj = S3Object(s3=\"/path/to/my_file.txt\")\nwith wmill.load_s3_file_reader(s3object, s3_resource_path) as file_reader:\n print(file_reader.read())\n'''
\n", "signature": "(\tself , \ts3object : wmill . s3_types . S3Object | str , \ts3_resource_path : str | None ) -> _io . BufferedReader : ", "funcdef": "def"}, "wmill.client.Windmill.write_s3_file": {"fullname": "wmill.client.Windmill.write_s3_file", "modulename": "wmill.client", "qualname": "Windmill.write_s3_file", "kind": "function", "doc": "Write a file to the workspace S3 bucket
\n\n'''python\nfrom wmill import S3Object
\n\ns3_obj = S3Object(s3=\"/path/to/my_file.txt\")
\n\nfor an in memory bytes array: \n\nfile_content = b'Hello Windmill!'\nclient.write_s3_file(s3_obj, file_content)
\n\nfor a file: \n\nwith open(\"my_file.txt\", \"rb\") as my_file:\n client.write_s3_file(s3_obj, my_file)\n'''
\n", "signature": "(\tself , \ts3object : wmill . s3_types . S3Object | str | None , \tfile_content : _io . BufferedReader | bytes , \ts3_resource_path : str | None , \tcontent_type : str | None = None , \tcontent_disposition : str | None = None ) -> wmill . s3_types . S3Object : ", "funcdef": "def"}, "wmill.client.Windmill.sign_s3_objects": {"fullname": "wmill.client.Windmill.sign_s3_objects", "modulename": "wmill.client", "qualname": "Windmill.sign_s3_objects", "kind": "function", "doc": "
\n", "signature": "(\tself , \ts3_objects : list [ wmill . s3_types . S3Object | str ] ) -> list [ wmill . s3_types . S3Object ] : ", "funcdef": "def"}, "wmill.client.Windmill.sign_s3_object": {"fullname": "wmill.client.Windmill.sign_s3_object", "modulename": "wmill.client", "qualname": "Windmill.sign_s3_object", "kind": "function", "doc": "
\n", "signature": "(\tself , \ts3_object : wmill . s3_types . S3Object | str ) -> wmill . s3_types . S3Object : ", "funcdef": "def"}, "wmill.client.Windmill.whoami": {"fullname": "wmill.client.Windmill.whoami", "modulename": "wmill.client", "qualname": "Windmill.whoami", "kind": "function", "doc": "
\n", "signature": "(self ) -> dict : ", "funcdef": "def"}, "wmill.client.Windmill.user": {"fullname": "wmill.client.Windmill.user", "modulename": "wmill.client", "qualname": "Windmill.user", "kind": "variable", "doc": "
\n", "annotation": ": dict"}, "wmill.client.Windmill.state_path": {"fullname": "wmill.client.Windmill.state_path", "modulename": "wmill.client", "qualname": "Windmill.state_path", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.client.Windmill.state": {"fullname": "wmill.client.Windmill.state", "modulename": "wmill.client", "qualname": "Windmill.state", "kind": "variable", "doc": "
\n", "annotation": ": Any"}, "wmill.client.Windmill.set_shared_state_pickle": {"fullname": "wmill.client.Windmill.set_shared_state_pickle", "modulename": "wmill.client", "qualname": "Windmill.set_shared_state_pickle", "kind": "function", "doc": "Set the state in the shared folder using pickle
\n", "signature": "(value : Any , path : str = 'state.pickle' ) -> None : ", "funcdef": "def"}, "wmill.client.Windmill.get_shared_state_pickle": {"fullname": "wmill.client.Windmill.get_shared_state_pickle", "modulename": "wmill.client", "qualname": "Windmill.get_shared_state_pickle", "kind": "function", "doc": "Get the state in the shared folder using pickle
\n", "signature": "(path : str = 'state.pickle' ) -> Any : ", "funcdef": "def"}, "wmill.client.Windmill.set_shared_state": {"fullname": "wmill.client.Windmill.set_shared_state", "modulename": "wmill.client", "qualname": "Windmill.set_shared_state", "kind": "function", "doc": "Set the state in the shared folder using pickle
\n", "signature": "(value : Any , path : str = 'state.json' ) -> None : ", "funcdef": "def"}, "wmill.client.Windmill.get_shared_state": {"fullname": "wmill.client.Windmill.get_shared_state", "modulename": "wmill.client", "qualname": "Windmill.get_shared_state", "kind": "function", "doc": "Get the state in the shared folder using pickle
\n", "signature": "(path : str = 'state.json' ) -> None : ", "funcdef": "def"}, "wmill.client.Windmill.get_resume_urls": {"fullname": "wmill.client.Windmill.get_resume_urls", "modulename": "wmill.client", "qualname": "Windmill.get_resume_urls", "kind": "function", "doc": "
\n", "signature": "(self , approver : str = None ) -> dict : ", "funcdef": "def"}, "wmill.client.Windmill.request_interactive_slack_approval": {"fullname": "wmill.client.Windmill.request_interactive_slack_approval", "modulename": "wmill.client", "qualname": "Windmill.request_interactive_slack_approval", "kind": "function", "doc": "Sends an interactive approval request via Slack, allowing optional customization of the message, approver, and form fields.
\n\n[Enterprise Edition Only] To include form fields in the Slack approval request, use the \"Advanced -> Suspend -> Form\" functionality.\nLearn more at: https://www.windmill.dev/docs/flows/flow_approval#form
\n\nParameters \n\n\nslack_resource_path : The path to the Slack resource in Windmill. \nchannel_id : The Slack channel ID where the approval request will be sent. \nmessage : Optional custom message to include in the Slack approval request. \napprover : Optional user ID or name of the approver for the request. \ndefault_args_json : Optional dictionary defining or overriding the default arguments for form fields. \ndynamic_enums_json : Optional dictionary overriding the enum default values of enum form fields. \n \n\nRaises \n\n\nException : If the function is not called within a flow or flow preview. \nException : If the required flow job or flow step environment variables are not set. \n \n\nReturns \n\n\n None
\n \n\nUsage Example:
\n\n\n \n \n client.request_interactive_slack_approval(\n ... slack_resource_path=\"/u/alex/my_slack_resource\",\n ... channel_id=\"admins-slack-channel\",\n ... message=\"Please approve this request\",\n ... approver=\"approver123\",\n ... default_args_json={\"key1\": \"value1\", \"key2\": 42},\n ... dynamic_enums_json={\"foo\": [\"choice1\", \"choice2\"], \"bar\": [\"optionA\", \"optionB\"]},\n ... )
\n \n \n \n\nNotes:
\n\n\nThis function must be executed within a Windmill flow or flow preview. \nThe function checks for required environment variables (WM_FLOW_JOB_ID, WM_FLOW_STEP_ID) to ensure it is run in the appropriate context. \n \n", "signature": "(\tself , \tslack_resource_path : str , \tchannel_id : str , \tmessage : str = None , \tapprover : str = None , \tdefault_args_json : dict = None , \tdynamic_enums_json : dict = None ) -> None : ", "funcdef": "def"}, "wmill.client.Windmill.username_to_email": {"fullname": "wmill.client.Windmill.username_to_email", "modulename": "wmill.client", "qualname": "Windmill.username_to_email", "kind": "function", "doc": "Get email from workspace username\nThis method is particularly useful for apps that require the email address of the viewer.\nIndeed, in the viewer context WM_USERNAME is set to the username of the viewer but WM_EMAIL is set to the email of the creator of the app.
\n", "signature": "(self , username : str ) -> str : ", "funcdef": "def"}, "wmill.client.Windmill.send_teams_message": {"fullname": "wmill.client.Windmill.send_teams_message", "modulename": "wmill.client", "qualname": "Windmill.send_teams_message", "kind": "function", "doc": "Send a message to a Microsoft Teams conversation with conversation_id, where success is used to style the message
\n", "signature": "(\tself , \tconversation_id : str , \ttext : str , \tsuccess : bool = True , \tcard_block : dict = None ): ", "funcdef": "def"}, "wmill.client.init_global_client": {"fullname": "wmill.client.init_global_client", "modulename": "wmill.client", "qualname": "init_global_client", "kind": "function", "doc": "
\n", "signature": "(f ): ", "funcdef": "def"}, "wmill.client.deprecate": {"fullname": "wmill.client.deprecate", "modulename": "wmill.client", "qualname": "deprecate", "kind": "function", "doc": "
\n", "signature": "(in_favor_of : str ): ", "funcdef": "def"}, "wmill.client.get_workspace": {"fullname": "wmill.client.get_workspace", "modulename": "wmill.client", "qualname": "get_workspace", "kind": "function", "doc": "
\n", "signature": "() -> str : ", "funcdef": "def"}, "wmill.client.get_root_job_id": {"fullname": "wmill.client.get_root_job_id", "modulename": "wmill.client", "qualname": "get_root_job_id", "kind": "function", "doc": "
\n", "signature": "(job_id : str | None = None ) -> str : ", "funcdef": "def"}, "wmill.client.get_version": {"fullname": "wmill.client.get_version", "modulename": "wmill.client", "qualname": "get_version", "kind": "function", "doc": "
\n", "signature": "() -> str : ", "funcdef": "def"}, "wmill.client.run_script_async": {"fullname": "wmill.client.run_script_async", "modulename": "wmill.client", "qualname": "run_script_async", "kind": "function", "doc": "
\n", "signature": "(\thash_or_path : str , \targs : Dict [ str , Any ] = None , \tscheduled_in_secs : int = None ) -> str : ", "funcdef": "def"}, "wmill.client.run_flow_async": {"fullname": "wmill.client.run_flow_async", "modulename": "wmill.client", "qualname": "run_flow_async", "kind": "function", "doc": "
\n", "signature": "(\tpath : str , \targs : Dict [ str , Any ] = None , \tscheduled_in_secs : int = None , \tdo_not_track_in_parent : bool = True ) -> str : ", "funcdef": "def"}, "wmill.client.run_script_sync": {"fullname": "wmill.client.run_script_sync", "modulename": "wmill.client", "qualname": "run_script_sync", "kind": "function", "doc": "
\n", "signature": "(\thash : str , \targs : Dict [ str , Any ] = None , \tverbose : bool = False , \tassert_result_is_not_none : bool = True , \tcleanup : bool = True , \ttimeout : datetime . timedelta = None ) -> Any : ", "funcdef": "def"}, "wmill.client.run_script_by_path_async": {"fullname": "wmill.client.run_script_by_path_async", "modulename": "wmill.client", "qualname": "run_script_by_path_async", "kind": "function", "doc": "
\n", "signature": "(\tpath : str , \targs : Dict [ str , Any ] = None , \tscheduled_in_secs : Optional [ int ] = None ) -> str : ", "funcdef": "def"}, "wmill.client.run_script_by_hash_async": {"fullname": "wmill.client.run_script_by_hash_async", "modulename": "wmill.client", "qualname": "run_script_by_hash_async", "kind": "function", "doc": "
\n", "signature": "(\thash_ : str , \targs : Dict [ str , Any ] = None , \tscheduled_in_secs : Optional [ int ] = None ) -> str : ", "funcdef": "def"}, "wmill.client.run_script_by_path_sync": {"fullname": "wmill.client.run_script_by_path_sync", "modulename": "wmill.client", "qualname": "run_script_by_path_sync", "kind": "function", "doc": "
\n", "signature": "(\tpath : str , \targs : Dict [ str , Any ] = None , \tverbose : bool = False , \tassert_result_is_not_none : bool = True , \tcleanup : bool = True , \ttimeout : datetime . timedelta = None ) -> Any : ", "funcdef": "def"}, "wmill.client.get_id_token": {"fullname": "wmill.client.get_id_token", "modulename": "wmill.client", "qualname": "get_id_token", "kind": "function", "doc": "Get a JWT token for the given audience for OIDC purposes to login into third parties like AWS, Vault, GCP, etc.
\n", "signature": "(audience : str ) -> str : ", "funcdef": "def"}, "wmill.client.get_job_status": {"fullname": "wmill.client.get_job_status", "modulename": "wmill.client", "qualname": "get_job_status", "kind": "function", "doc": "
\n", "signature": "(job_id : str ) -> Literal [ 'RUNNING' , 'WAITING' , 'COMPLETED' ] : ", "funcdef": "def"}, "wmill.client.get_result": {"fullname": "wmill.client.get_result", "modulename": "wmill.client", "qualname": "get_result", "kind": "function", "doc": "
\n", "signature": "(job_id : str , assert_result_is_not_none = True ) -> Dict [ str , Any ] : ", "funcdef": "def"}, "wmill.client.duckdb_connection_settings": {"fullname": "wmill.client.duckdb_connection_settings", "modulename": "wmill.client", "qualname": "duckdb_connection_settings", "kind": "function", "doc": "Convenient helpers that takes an S3 resource as input and returns the settings necessary to\ninitiate an S3 connection from DuckDB
\n", "signature": "(s3_resource_path : str = '' ) -> wmill . s3_types . DuckDbConnectionSettings : ", "funcdef": "def"}, "wmill.client.polars_connection_settings": {"fullname": "wmill.client.polars_connection_settings", "modulename": "wmill.client", "qualname": "polars_connection_settings", "kind": "function", "doc": "Convenient helpers that takes an S3 resource as input and returns the settings necessary to\ninitiate an S3 connection from Polars
\n", "signature": "(s3_resource_path : str = '' ) -> wmill . s3_types . PolarsConnectionSettings : ", "funcdef": "def"}, "wmill.client.boto3_connection_settings": {"fullname": "wmill.client.boto3_connection_settings", "modulename": "wmill.client", "qualname": "boto3_connection_settings", "kind": "function", "doc": "Convenient helpers that takes an S3 resource as input and returns the settings necessary to\ninitiate an S3 connection using boto3
\n", "signature": "(s3_resource_path : str = '' ) -> wmill . s3_types . Boto3ConnectionSettings : ", "funcdef": "def"}, "wmill.client.load_s3_file": {"fullname": "wmill.client.load_s3_file", "modulename": "wmill.client", "qualname": "load_s3_file", "kind": "function", "doc": "Load the entire content of a file stored in S3 as bytes
\n", "signature": "(\ts3object : wmill . s3_types . S3Object | str , \ts3_resource_path : str | None = None ) -> bytes : ", "funcdef": "def"}, "wmill.client.load_s3_file_reader": {"fullname": "wmill.client.load_s3_file_reader", "modulename": "wmill.client", "qualname": "load_s3_file_reader", "kind": "function", "doc": "Load the content of a file stored in S3
\n", "signature": "(\ts3object : wmill . s3_types . S3Object | str , \ts3_resource_path : str | None = None ) -> _io . BufferedReader : ", "funcdef": "def"}, "wmill.client.write_s3_file": {"fullname": "wmill.client.write_s3_file", "modulename": "wmill.client", "qualname": "write_s3_file", "kind": "function", "doc": "Upload a file to S3
\n\nContent type will be automatically guessed from path extension if left empty
\n\nSee MDN for content_disposition: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition \nand content_type: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
\n", "signature": "(\ts3object : wmill . s3_types . S3Object | str | None , \tfile_content : _io . BufferedReader | bytes , \ts3_resource_path : str | None = None , \tcontent_type : str | None = None , \tcontent_disposition : str | None = None ) -> wmill . s3_types . S3Object : ", "funcdef": "def"}, "wmill.client.sign_s3_objects": {"fullname": "wmill.client.sign_s3_objects", "modulename": "wmill.client", "qualname": "sign_s3_objects", "kind": "function", "doc": "Sign S3 objects to be used by anonymous users in public apps\nReturns a list of signed s3 tokens
\n", "signature": "(\ts3_objects : list [ wmill . s3_types . S3Object | str ] ) -> list [ wmill . s3_types . S3Object ] : ", "funcdef": "def"}, "wmill.client.sign_s3_object": {"fullname": "wmill.client.sign_s3_object", "modulename": "wmill.client", "qualname": "sign_s3_object", "kind": "function", "doc": "Sign S3 object to be used by anonymous users in public apps\nReturns a signed s3 object
\n", "signature": "(s3_object : wmill . s3_types . S3Object | str ) -> wmill . s3_types . S3Object : ", "funcdef": "def"}, "wmill.client.whoami": {"fullname": "wmill.client.whoami", "modulename": "wmill.client", "qualname": "whoami", "kind": "function", "doc": "Returns the current user
\n", "signature": "() -> dict : ", "funcdef": "def"}, "wmill.client.get_state": {"fullname": "wmill.client.get_state", "modulename": "wmill.client", "qualname": "get_state", "kind": "function", "doc": "Get the state
\n", "signature": "() -> Any : ", "funcdef": "def"}, "wmill.client.get_resource": {"fullname": "wmill.client.get_resource", "modulename": "wmill.client", "qualname": "get_resource", "kind": "function", "doc": "Get resource from Windmill
\n", "signature": "(path : str , none_if_undefined : bool = False ) -> dict | None : ", "funcdef": "def"}, "wmill.client.set_resource": {"fullname": "wmill.client.set_resource", "modulename": "wmill.client", "qualname": "set_resource", "kind": "function", "doc": "Set the resource at a given path as a string, creating it if it does not exist
\n", "signature": "(path : str , value : Any , resource_type : str = 'any' ) -> None : ", "funcdef": "def"}, "wmill.client.set_state": {"fullname": "wmill.client.set_state", "modulename": "wmill.client", "qualname": "set_state", "kind": "function", "doc": "Set the state
\n", "signature": "(value : Any ) -> None : ", "funcdef": "def"}, "wmill.client.set_progress": {"fullname": "wmill.client.set_progress", "modulename": "wmill.client", "qualname": "set_progress", "kind": "function", "doc": "Set the progress
\n", "signature": "(value : int , job_id : Optional [ str ] = None ) -> None : ", "funcdef": "def"}, "wmill.client.get_progress": {"fullname": "wmill.client.get_progress", "modulename": "wmill.client", "qualname": "get_progress", "kind": "function", "doc": "Get the progress
\n", "signature": "(job_id : Optional [ str ] = None ) -> Any : ", "funcdef": "def"}, "wmill.client.set_shared_state_pickle": {"fullname": "wmill.client.set_shared_state_pickle", "modulename": "wmill.client", "qualname": "set_shared_state_pickle", "kind": "function", "doc": "Set the state in the shared folder using pickle
\n", "signature": "(value : Any , path = 'state.pickle' ) -> None : ", "funcdef": "def"}, "wmill.client.get_shared_state_pickle": {"fullname": "wmill.client.get_shared_state_pickle", "modulename": "wmill.client", "qualname": "get_shared_state_pickle", "kind": "function", "doc": "Get the state in the shared folder using pickle
\n", "signature": "(path = 'state.pickle' ) -> Any : ", "funcdef": "def"}, "wmill.client.set_shared_state": {"fullname": "wmill.client.set_shared_state", "modulename": "wmill.client", "qualname": "set_shared_state", "kind": "function", "doc": "Set the state in the shared folder using pickle
\n", "signature": "(value : Any , path = 'state.json' ) -> None : ", "funcdef": "def"}, "wmill.client.get_shared_state": {"fullname": "wmill.client.get_shared_state", "modulename": "wmill.client", "qualname": "get_shared_state", "kind": "function", "doc": "Get the state in the shared folder using pickle
\n", "signature": "(path = 'state.json' ) -> None : ", "funcdef": "def"}, "wmill.client.get_variable": {"fullname": "wmill.client.get_variable", "modulename": "wmill.client", "qualname": "get_variable", "kind": "function", "doc": "Returns the variable at a given path as a string
\n", "signature": "(path : str ) -> str : ", "funcdef": "def"}, "wmill.client.set_variable": {"fullname": "wmill.client.set_variable", "modulename": "wmill.client", "qualname": "set_variable", "kind": "function", "doc": "Set the variable at a given path as a string, creating it if it does not exist
\n", "signature": "(path : str , value : str , is_secret : bool = False ) -> None : ", "funcdef": "def"}, "wmill.client.get_flow_user_state": {"fullname": "wmill.client.get_flow_user_state", "modulename": "wmill.client", "qualname": "get_flow_user_state", "kind": "function", "doc": "Get the user state of a flow at a given key
\n", "signature": "(key : str ) -> Any : ", "funcdef": "def"}, "wmill.client.set_flow_user_state": {"fullname": "wmill.client.set_flow_user_state", "modulename": "wmill.client", "qualname": "set_flow_user_state", "kind": "function", "doc": "Set the user state of a flow at a given key
\n", "signature": "(key : str , value : Any ) -> None : ", "funcdef": "def"}, "wmill.client.get_state_path": {"fullname": "wmill.client.get_state_path", "modulename": "wmill.client", "qualname": "get_state_path", "kind": "function", "doc": "
\n", "signature": "() -> str : ", "funcdef": "def"}, "wmill.client.get_resume_urls": {"fullname": "wmill.client.get_resume_urls", "modulename": "wmill.client", "qualname": "get_resume_urls", "kind": "function", "doc": "
\n", "signature": "(approver : str = None ) -> dict : ", "funcdef": "def"}, "wmill.client.request_interactive_slack_approval": {"fullname": "wmill.client.request_interactive_slack_approval", "modulename": "wmill.client", "qualname": "request_interactive_slack_approval", "kind": "function", "doc": "
\n", "signature": "(\tslack_resource_path : str , \tchannel_id : str , \tmessage : str = None , \tapprover : str = None , \tdefault_args_json : dict = None , \tdynamic_enums_json : dict = None ) -> None : ", "funcdef": "def"}, "wmill.client.send_teams_message": {"fullname": "wmill.client.send_teams_message", "modulename": "wmill.client", "qualname": "send_teams_message", "kind": "function", "doc": "
\n", "signature": "(\tconversation_id : str , \ttext : str , \tsuccess : bool , \tcard_block : dict = None ): ", "funcdef": "def"}, "wmill.client.cancel_running": {"fullname": "wmill.client.cancel_running", "modulename": "wmill.client", "qualname": "cancel_running", "kind": "function", "doc": "Cancel currently running executions of the same script.
\n", "signature": "() -> dict : ", "funcdef": "def"}, "wmill.client.run_script": {"fullname": "wmill.client.run_script", "modulename": "wmill.client", "qualname": "run_script", "kind": "function", "doc": "Run script synchronously and return its result.
\n\nDeprecated since version Use run_script_by_path or run_script_by_hash instead..
\n", "signature": "(\tpath : str = None , \thash_ : str = None , \targs : dict = None , \ttimeout : datetime . timedelta | int | float = None , \tverbose : bool = False , \tcleanup : bool = True , \tassert_result_is_not_none : bool = True ) -> Any : ", "funcdef": "def"}, "wmill.client.run_script_by_path": {"fullname": "wmill.client.run_script_by_path", "modulename": "wmill.client", "qualname": "run_script_by_path", "kind": "function", "doc": "Run script by path synchronously and return its result.
\n", "signature": "(\tpath : str , \targs : dict = None , \ttimeout : datetime . timedelta | int | float = None , \tverbose : bool = False , \tcleanup : bool = True , \tassert_result_is_not_none : bool = True ) -> Any : ", "funcdef": "def"}, "wmill.client.run_script_by_hash": {"fullname": "wmill.client.run_script_by_hash", "modulename": "wmill.client", "qualname": "run_script_by_hash", "kind": "function", "doc": "Run script by hash synchronously and return its result.
\n", "signature": "(\thash_ : str , \targs : dict = None , \ttimeout : datetime . timedelta | int | float = None , \tverbose : bool = False , \tcleanup : bool = True , \tassert_result_is_not_none : bool = True ) -> Any : ", "funcdef": "def"}, "wmill.client.username_to_email": {"fullname": "wmill.client.username_to_email", "modulename": "wmill.client", "qualname": "username_to_email", "kind": "function", "doc": "Get email from workspace username\nThis method is particularly useful for apps that require the email address of the viewer.\nIndeed, in the viewer context WM_USERNAME is set to the username of the viewer but WM_EMAIL is set to the email of the creator of the app.
\n", "signature": "(username : str ) -> str : ", "funcdef": "def"}, "wmill.client.task": {"fullname": "wmill.client.task", "modulename": "wmill.client", "qualname": "task", "kind": "function", "doc": "
\n", "signature": "(* args , ** kwargs ): ", "funcdef": "def"}, "wmill.client.parse_resource_syntax": {"fullname": "wmill.client.parse_resource_syntax", "modulename": "wmill.client", "qualname": "parse_resource_syntax", "kind": "function", "doc": "Parse resource syntax from string.
\n", "signature": "(s : str ) -> Optional [ str ] : ", "funcdef": "def"}, "wmill.client.parse_s3_object": {"fullname": "wmill.client.parse_s3_object", "modulename": "wmill.client", "qualname": "parse_s3_object", "kind": "function", "doc": "Parse S3 object from string or S3Object format.
\n", "signature": "(s3_object : wmill . s3_types . S3Object | str ) -> wmill . s3_types . S3Object : ", "funcdef": "def"}, "wmill.client.parse_variable_syntax": {"fullname": "wmill.client.parse_variable_syntax", "modulename": "wmill.client", "qualname": "parse_variable_syntax", "kind": "function", "doc": "Parse variable syntax from string.
\n", "signature": "(s : str ) -> Optional [ str ] : ", "funcdef": "def"}, "wmill.client.append_to_result_stream": {"fullname": "wmill.client.append_to_result_stream", "modulename": "wmill.client", "qualname": "append_to_result_stream", "kind": "function", "doc": "Append a text to the result stream.
\n\nArgs:\n text: text to append to the result stream
\n", "signature": "(text : str ) -> None : ", "funcdef": "def"}, "wmill.client.stream_result": {"fullname": "wmill.client.stream_result", "modulename": "wmill.client", "qualname": "stream_result", "kind": "function", "doc": "Stream to the result stream.
\n\nArgs:\n stream: stream to stream to the result stream
\n", "signature": "(stream ) -> None : ", "funcdef": "def"}, "wmill.s3_reader": {"fullname": "wmill.s3_reader", "modulename": "wmill.s3_reader", "kind": "module", "doc": "
\n"}, "wmill.s3_reader.S3BufferedReader": {"fullname": "wmill.s3_reader.S3BufferedReader", "modulename": "wmill.s3_reader", "qualname": "S3BufferedReader", "kind": "class", "doc": "Create a new buffered reader using the given readable raw IO object.
\n", "bases": "_io.BufferedReader"}, "wmill.s3_reader.S3BufferedReader.__init__": {"fullname": "wmill.s3_reader.S3BufferedReader.__init__", "modulename": "wmill.s3_reader", "qualname": "S3BufferedReader.__init__", "kind": "function", "doc": "
\n", "signature": "(\tworkspace : str , \twindmill_client : httpx . Client , \tfile_key : str , \ts3_resource_path : Optional [ str ] , \tstorage : Optional [ str ] ) "}, "wmill.s3_reader.S3BufferedReader.peek": {"fullname": "wmill.s3_reader.S3BufferedReader.peek", "modulename": "wmill.s3_reader", "qualname": "S3BufferedReader.peek", "kind": "function", "doc": "
\n", "signature": "(self , size = 0 ): ", "funcdef": "def"}, "wmill.s3_reader.S3BufferedReader.read": {"fullname": "wmill.s3_reader.S3BufferedReader.read", "modulename": "wmill.s3_reader", "qualname": "S3BufferedReader.read", "kind": "function", "doc": "Read and return up to n bytes.
\n\nIf the argument is omitted, None, or negative, reads and\nreturns all data until EOF.
\n\nIf the argument is positive, and the underlying raw stream is\nnot 'interactive', multiple raw reads may be issued to satisfy\nthe byte count (unless EOF is reached first). But for\ninteractive raw streams (as well as sockets and pipes), at most\none raw read will be issued, and a short result does not imply\nthat EOF is imminent.
\n\nReturns an empty bytes object on EOF.
\n\nReturns None if the underlying raw stream was open in non-blocking\nmode and no data is available at the moment.
\n", "signature": "(self , size =- 1 ): ", "funcdef": "def"}, "wmill.s3_reader.S3BufferedReader.read1": {"fullname": "wmill.s3_reader.S3BufferedReader.read1", "modulename": "wmill.s3_reader", "qualname": "S3BufferedReader.read1", "kind": "function", "doc": "Read and return up to n bytes, with at most one read() call\nto the underlying raw stream. A short result does not imply\nthat EOF is imminent.
\n\nReturns an empty bytes object on EOF.
\n", "signature": "(self , size =- 1 ): ", "funcdef": "def"}, "wmill.s3_reader.bytes_generator": {"fullname": "wmill.s3_reader.bytes_generator", "modulename": "wmill.s3_reader", "qualname": "bytes_generator", "kind": "function", "doc": "
\n", "signature": "(buffered_reader : Union [ _io . BufferedReader , _io . BytesIO ] ): ", "funcdef": "def"}, "wmill.s3_types": {"fullname": "wmill.s3_types", "modulename": "wmill.s3_types", "kind": "module", "doc": "
\n"}, "wmill.s3_types.S3Object": {"fullname": "wmill.s3_types.S3Object", "modulename": "wmill.s3_types", "qualname": "S3Object", "kind": "class", "doc": "
\n", "bases": "builtins.dict"}, "wmill.s3_types.S3Object.s3": {"fullname": "wmill.s3_types.S3Object.s3", "modulename": "wmill.s3_types", "qualname": "S3Object.s3", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.S3Object.storage": {"fullname": "wmill.s3_types.S3Object.storage", "modulename": "wmill.s3_types", "qualname": "S3Object.storage", "kind": "variable", "doc": "
\n", "annotation": ": Optional[str]"}, "wmill.s3_types.S3Object.presigned": {"fullname": "wmill.s3_types.S3Object.presigned", "modulename": "wmill.s3_types", "qualname": "S3Object.presigned", "kind": "variable", "doc": "
\n", "annotation": ": Optional[str]"}, "wmill.s3_types.S3FsClientKwargs": {"fullname": "wmill.s3_types.S3FsClientKwargs", "modulename": "wmill.s3_types", "qualname": "S3FsClientKwargs", "kind": "class", "doc": "
\n", "bases": "builtins.dict"}, "wmill.s3_types.S3FsClientKwargs.region_name": {"fullname": "wmill.s3_types.S3FsClientKwargs.region_name", "modulename": "wmill.s3_types", "qualname": "S3FsClientKwargs.region_name", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.S3FsArgs": {"fullname": "wmill.s3_types.S3FsArgs", "modulename": "wmill.s3_types", "qualname": "S3FsArgs", "kind": "class", "doc": "
\n", "bases": "builtins.dict"}, "wmill.s3_types.S3FsArgs.endpoint_url": {"fullname": "wmill.s3_types.S3FsArgs.endpoint_url", "modulename": "wmill.s3_types", "qualname": "S3FsArgs.endpoint_url", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.S3FsArgs.key": {"fullname": "wmill.s3_types.S3FsArgs.key", "modulename": "wmill.s3_types", "qualname": "S3FsArgs.key", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.S3FsArgs.secret": {"fullname": "wmill.s3_types.S3FsArgs.secret", "modulename": "wmill.s3_types", "qualname": "S3FsArgs.secret", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.S3FsArgs.use_ssl": {"fullname": "wmill.s3_types.S3FsArgs.use_ssl", "modulename": "wmill.s3_types", "qualname": "S3FsArgs.use_ssl", "kind": "variable", "doc": "
\n", "annotation": ": bool"}, "wmill.s3_types.S3FsArgs.cache_regions": {"fullname": "wmill.s3_types.S3FsArgs.cache_regions", "modulename": "wmill.s3_types", "qualname": "S3FsArgs.cache_regions", "kind": "variable", "doc": "
\n", "annotation": ": bool"}, "wmill.s3_types.S3FsArgs.client_kwargs": {"fullname": "wmill.s3_types.S3FsArgs.client_kwargs", "modulename": "wmill.s3_types", "qualname": "S3FsArgs.client_kwargs", "kind": "variable", "doc": "
\n", "annotation": ": wmill.s3_types.S3FsClientKwargs"}, "wmill.s3_types.StorageOptions": {"fullname": "wmill.s3_types.StorageOptions", "modulename": "wmill.s3_types", "qualname": "StorageOptions", "kind": "class", "doc": "
\n", "bases": "builtins.dict"}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"fullname": "wmill.s3_types.StorageOptions.aws_endpoint_url", "modulename": "wmill.s3_types", "qualname": "StorageOptions.aws_endpoint_url", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"fullname": "wmill.s3_types.StorageOptions.aws_access_key_id", "modulename": "wmill.s3_types", "qualname": "StorageOptions.aws_access_key_id", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"fullname": "wmill.s3_types.StorageOptions.aws_secret_access_key", "modulename": "wmill.s3_types", "qualname": "StorageOptions.aws_secret_access_key", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.StorageOptions.aws_region": {"fullname": "wmill.s3_types.StorageOptions.aws_region", "modulename": "wmill.s3_types", "qualname": "StorageOptions.aws_region", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.StorageOptions.aws_allow_http": {"fullname": "wmill.s3_types.StorageOptions.aws_allow_http", "modulename": "wmill.s3_types", "qualname": "StorageOptions.aws_allow_http", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.PolarsConnectionSettings": {"fullname": "wmill.s3_types.PolarsConnectionSettings", "modulename": "wmill.s3_types", "qualname": "PolarsConnectionSettings", "kind": "class", "doc": "
\n", "bases": "builtins.dict"}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"fullname": "wmill.s3_types.PolarsConnectionSettings.s3fs_args", "modulename": "wmill.s3_types", "qualname": "PolarsConnectionSettings.s3fs_args", "kind": "variable", "doc": "
\n", "annotation": ": wmill.s3_types.S3FsArgs"}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"fullname": "wmill.s3_types.PolarsConnectionSettings.storage_options", "modulename": "wmill.s3_types", "qualname": "PolarsConnectionSettings.storage_options", "kind": "variable", "doc": "
\n", "annotation": ": wmill.s3_types.StorageOptions"}, "wmill.s3_types.Boto3ConnectionSettings": {"fullname": "wmill.s3_types.Boto3ConnectionSettings", "modulename": "wmill.s3_types", "qualname": "Boto3ConnectionSettings", "kind": "class", "doc": "
\n", "bases": "builtins.dict"}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"fullname": "wmill.s3_types.Boto3ConnectionSettings.endpoint_url", "modulename": "wmill.s3_types", "qualname": "Boto3ConnectionSettings.endpoint_url", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"fullname": "wmill.s3_types.Boto3ConnectionSettings.region_name", "modulename": "wmill.s3_types", "qualname": "Boto3ConnectionSettings.region_name", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"fullname": "wmill.s3_types.Boto3ConnectionSettings.use_ssl", "modulename": "wmill.s3_types", "qualname": "Boto3ConnectionSettings.use_ssl", "kind": "variable", "doc": "
\n", "annotation": ": bool"}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"fullname": "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id", "modulename": "wmill.s3_types", "qualname": "Boto3ConnectionSettings.aws_access_key_id", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"fullname": "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key", "modulename": "wmill.s3_types", "qualname": "Boto3ConnectionSettings.aws_secret_access_key", "kind": "variable", "doc": "
\n", "annotation": ": str"}, "wmill.s3_types.DuckDbConnectionSettings": {"fullname": "wmill.s3_types.DuckDbConnectionSettings", "modulename": "wmill.s3_types", "qualname": "DuckDbConnectionSettings", "kind": "class", "doc": "
\n", "bases": "builtins.dict"}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"fullname": "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str", "modulename": "wmill.s3_types", "qualname": "DuckDbConnectionSettings.connection_settings_str", "kind": "variable", "doc": "
\n", "annotation": ": str"}}, "docInfo": {"wmill": {"qualname": 0, "fullname": 1, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.logger": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.JobStatus": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 12, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 45, "bases": 0, "doc": 3}, "wmill.client.Windmill.base_url": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.token": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.headers": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.verify": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.client": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.workspace": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.path": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.mocked_api": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_mocked_api": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_client": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 3}, "wmill.client.Windmill.get": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 43, "bases": 0, "doc": 3}, "wmill.client.Windmill.post": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 43, "bases": 0, "doc": 3}, "wmill.client.Windmill.create_token": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 3}, "wmill.client.Windmill.run_script_async": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 90, "bases": 0, "doc": 33}, "wmill.client.Windmill.run_script_by_path_async": {"qualname": 6, "fullname": 8, "annotation": 0, "default_value": 0, "signature": 60, "bases": 0, "doc": 14}, "wmill.client.Windmill.run_script_by_hash_async": {"qualname": 6, "fullname": 8, "annotation": 0, "default_value": 0, "signature": 65, "bases": 0, "doc": 14}, "wmill.client.Windmill.run_flow_async": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 86, "bases": 0, "doc": 12}, "wmill.client.Windmill.run_script": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 169, "bases": 0, "doc": 29}, "wmill.client.Windmill.run_script_by_path": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 143, "bases": 0, "doc": 12}, "wmill.client.Windmill.run_script_by_hash": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 144, "bases": 0, "doc": 12}, "wmill.client.Windmill.wait_job": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 118, "bases": 0, "doc": 3}, "wmill.client.Windmill.cancel_running": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 11}, "wmill.client.Windmill.get_job": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 25, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_root_job_id": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 38, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_id_token": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_job_status": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 55, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_result": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 46, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_variable": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 3}, "wmill.client.Windmill.set_variable": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 52, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_resource": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 49, "bases": 0, "doc": 3}, "wmill.client.Windmill.set_resource": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 3}, "wmill.client.Windmill.set_state": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 21, "bases": 0, "doc": 3}, "wmill.client.Windmill.set_progress": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 45, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_progress": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 38, "bases": 0, "doc": 3}, "wmill.client.Windmill.set_flow_user_state": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 34, "bases": 0, "doc": 13}, "wmill.client.Windmill.get_flow_user_state": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 13}, "wmill.client.Windmill.version": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.get_duckdb_connection_settings": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 55, "bases": 0, "doc": 23}, "wmill.client.Windmill.get_polars_connection_settings": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 49, "bases": 0, "doc": 23}, "wmill.client.Windmill.get_boto3_connection_settings": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 49, "bases": 0, "doc": 23}, "wmill.client.Windmill.load_s3_file": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 62, "bases": 0, "doc": 52}, "wmill.client.Windmill.load_s3_file_reader": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 68, "bases": 0, "doc": 51}, "wmill.client.Windmill.write_s3_file": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 153, "bases": 0, "doc": 77}, "wmill.client.Windmill.sign_s3_objects": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 67, "bases": 0, "doc": 3}, "wmill.client.Windmill.sign_s3_object": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 55, "bases": 0, "doc": 3}, "wmill.client.Windmill.whoami": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "wmill.client.Windmill.user": {"qualname": 2, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.state_path": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.state": {"qualname": 2, "fullname": 4, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.client.Windmill.set_shared_state_pickle": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 41, "bases": 0, "doc": 11}, "wmill.client.Windmill.get_shared_state_pickle": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 31, "bases": 0, "doc": 11}, "wmill.client.Windmill.set_shared_state": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 41, "bases": 0, "doc": 11}, "wmill.client.Windmill.get_shared_state": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 31, "bases": 0, "doc": 11}, "wmill.client.Windmill.get_resume_urls": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 31, "bases": 0, "doc": 3}, "wmill.client.Windmill.request_interactive_slack_approval": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 116, "bases": 0, "doc": 344}, "wmill.client.Windmill.username_to_email": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 52}, "wmill.client.Windmill.send_teams_message": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 72, "bases": 0, "doc": 21}, "wmill.client.init_global_client": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 3}, "wmill.client.deprecate": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 18, "bases": 0, "doc": 3}, "wmill.client.get_workspace": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 10, "bases": 0, "doc": 3}, "wmill.client.get_root_job_id": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 33, "bases": 0, "doc": 3}, "wmill.client.get_version": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 10, "bases": 0, "doc": 3}, "wmill.client.run_script_async": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 72, "bases": 0, "doc": 3}, "wmill.client.run_flow_async": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 92, "bases": 0, "doc": 3}, "wmill.client.run_script_sync": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 131, "bases": 0, "doc": 3}, "wmill.client.run_script_by_path_async": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 76, "bases": 0, "doc": 3}, "wmill.client.run_script_by_hash_async": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 77, "bases": 0, "doc": 3}, "wmill.client.run_script_by_path_sync": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 131, "bases": 0, "doc": 3}, "wmill.client.get_id_token": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 24}, "wmill.client.get_job_status": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 50, "bases": 0, "doc": 3}, "wmill.client.get_result": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 46, "bases": 0, "doc": 3}, "wmill.client.duckdb_connection_settings": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 23}, "wmill.client.polars_connection_settings": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 23}, "wmill.client.boto3_connection_settings": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 23}, "wmill.client.load_s3_file": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 63, "bases": 0, "doc": 14}, "wmill.client.load_s3_file_reader": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 69, "bases": 0, "doc": 11}, "wmill.client.write_s3_file": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 154, "bases": 0, "doc": 45}, "wmill.client.sign_s3_objects": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 61, "bases": 0, "doc": 21}, "wmill.client.sign_s3_object": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 48, "bases": 0, "doc": 19}, "wmill.client.whoami": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 10, "bases": 0, "doc": 6}, "wmill.client.get_state": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 10, "bases": 0, "doc": 5}, "wmill.client.get_resource": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 44, "bases": 0, "doc": 6}, "wmill.client.set_resource": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 51, "bases": 0, "doc": 19}, "wmill.client.set_state": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 5}, "wmill.client.set_progress": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 43, "bases": 0, "doc": 5}, "wmill.client.get_progress": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 33, "bases": 0, "doc": 5}, "wmill.client.set_shared_state_pickle": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 34, "bases": 0, "doc": 11}, "wmill.client.get_shared_state_pickle": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 11}, "wmill.client.set_shared_state": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 34, "bases": 0, "doc": 11}, "wmill.client.get_shared_state": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 24, "bases": 0, "doc": 11}, "wmill.client.get_variable": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 12}, "wmill.client.set_variable": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 47, "bases": 0, "doc": 19}, "wmill.client.get_flow_user_state": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 13}, "wmill.client.set_flow_user_state": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 29, "bases": 0, "doc": 13}, "wmill.client.get_state_path": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 10, "bases": 0, "doc": 3}, "wmill.client.get_resume_urls": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 26, "bases": 0, "doc": 3}, "wmill.client.request_interactive_slack_approval": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 110, "bases": 0, "doc": 3}, "wmill.client.send_teams_message": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 59, "bases": 0, "doc": 3}, "wmill.client.cancel_running": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 10, "bases": 0, "doc": 11}, "wmill.client.run_script": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 157, "bases": 0, "doc": 29}, "wmill.client.run_script_by_path": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 131, "bases": 0, "doc": 12}, "wmill.client.run_script_by_hash": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 132, "bases": 0, "doc": 12}, "wmill.client.username_to_email": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 52}, "wmill.client.task": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "wmill.client.parse_resource_syntax": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 25, "bases": 0, "doc": 8}, "wmill.client.parse_s3_object": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 48, "bases": 0, "doc": 11}, "wmill.client.parse_variable_syntax": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 25, "bases": 0, "doc": 8}, "wmill.client.append_to_result_stream": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 19, "bases": 0, "doc": 21}, "wmill.client.stream_result": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 19}, "wmill.s3_reader": {"qualname": 0, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_reader.S3BufferedReader": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 3, "doc": 15}, "wmill.s3_reader.S3BufferedReader.__init__": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 80, "bases": 0, "doc": 3}, "wmill.s3_reader.S3BufferedReader.peek": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 21, "bases": 0, "doc": 3}, "wmill.s3_reader.S3BufferedReader.read": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 21, "bases": 0, "doc": 124}, "wmill.s3_reader.S3BufferedReader.read1": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 21, "bases": 0, "doc": 41}, "wmill.s3_reader.bytes_generator": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 41, "bases": 0, "doc": 3}, "wmill.s3_types": {"qualname": 0, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3Object": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 3}, "wmill.s3_types.S3Object.s3": {"qualname": 2, "fullname": 5, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3Object.storage": {"qualname": 2, "fullname": 5, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3Object.presigned": {"qualname": 2, "fullname": 5, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3FsClientKwargs": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 3}, "wmill.s3_types.S3FsClientKwargs.region_name": {"qualname": 3, "fullname": 6, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3FsArgs": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 3}, "wmill.s3_types.S3FsArgs.endpoint_url": {"qualname": 3, "fullname": 6, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3FsArgs.key": {"qualname": 2, "fullname": 5, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3FsArgs.secret": {"qualname": 2, "fullname": 5, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3FsArgs.use_ssl": {"qualname": 3, "fullname": 6, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3FsArgs.cache_regions": {"qualname": 3, "fullname": 6, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.S3FsArgs.client_kwargs": {"qualname": 3, "fullname": 6, "annotation": 5, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.StorageOptions": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 3}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"qualname": 4, "fullname": 7, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"qualname": 5, "fullname": 8, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"qualname": 5, "fullname": 8, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.StorageOptions.aws_region": {"qualname": 3, "fullname": 6, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.StorageOptions.aws_allow_http": {"qualname": 4, "fullname": 7, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.PolarsConnectionSettings": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 3}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"qualname": 3, "fullname": 6, "annotation": 5, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"qualname": 3, "fullname": 6, "annotation": 5, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.Boto3ConnectionSettings": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 3}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"qualname": 3, "fullname": 6, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"qualname": 3, "fullname": 6, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"qualname": 3, "fullname": 6, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"qualname": 5, "fullname": 8, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"qualname": 5, "fullname": 8, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "wmill.s3_types.DuckDbConnectionSettings": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 3}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"qualname": 4, "fullname": 7, "annotation": 2, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}}, "length": 153, "save": true}, "index": {"qualname": {"root": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 2, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.logger": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}}, "df": 4}}}}, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {"wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}}, "df": 6, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.JobStatus": {"tf": 1}}, "df": 1}}}}}}}}}, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill": {"tf": 1}, "wmill.client.Windmill.__init__": {"tf": 1}, "wmill.client.Windmill.base_url": {"tf": 1}, "wmill.client.Windmill.token": {"tf": 1}, "wmill.client.Windmill.headers": {"tf": 1}, "wmill.client.Windmill.verify": {"tf": 1}, "wmill.client.Windmill.client": {"tf": 1}, "wmill.client.Windmill.workspace": {"tf": 1}, "wmill.client.Windmill.path": {"tf": 1}, "wmill.client.Windmill.mocked_api": {"tf": 1}, "wmill.client.Windmill.get_mocked_api": {"tf": 1}, "wmill.client.Windmill.get_client": {"tf": 1}, "wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}, "wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.version": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.Windmill.whoami": {"tf": 1}, "wmill.client.Windmill.user": {"tf": 1}, "wmill.client.Windmill.state_path": {"tf": 1}, "wmill.client.Windmill.state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}}, "df": 59}}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.workspace": {"tf": 1}, "wmill.client.get_workspace": {"tf": 1}}, "df": 2}}}}}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.wait_job": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}}, "df": 2}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {"wmill.client.Windmill.whoami": {"tf": 1}, "wmill.client.whoami": {"tf": 1}}, "df": 2}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}, "wmill.client.init_global_client": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 3}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}}}}}}, "d": {"docs": {"wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}}, "df": 6}}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.base_url": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 9, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"3": {"docs": {"wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.Boto3ConnectionSettings": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 6}}}}}}}}}}}}}}}}}}}, "docs": {}, "df": 0}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.base_url": {"tf": 1}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}}, "df": 4, "s": {"docs": {"wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}}, "df": 2}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}}, "df": 2, "r": {"docs": {"wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.user": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 5, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}, "wmill.client.append_to_result_stream": {"tf": 1}}, "df": 3, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.token": {"tf": 1}, "wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}}, "df": 4}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"wmill.client.task": {"tf": 1}}, "df": 1}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.headers": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 4}}}, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {"wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.verify": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.version": {"tf": 1}, "wmill.client.get_version": {"tf": 1}}, "df": 2}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 5}}}}}}}}, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.client": {"tf": 1}, "wmill.client.Windmill.get_client": {"tf": 1}, "wmill.client.init_global_client": {"tf": 1}, "wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}}, "df": 4}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.create_token": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}}, "df": 2}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 7}}}}}}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"wmill.client.Windmill.path": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.state_path": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_state_path": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}}, "df": 8}}, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 3}}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.post": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 3}}}}}}}}}}}}}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}}, "df": 4}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.s3_types.S3Object.presigned": {"tf": 1}}, "df": 1}}}}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}}, "df": 4}}}}}, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "k": {"docs": {"wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.mocked_api": {"tf": 1}, "wmill.client.Windmill.get_mocked_api": {"tf": 1}}, "df": 2}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}}}}}}, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {"wmill.client.Windmill.mocked_api": {"tf": 1}, "wmill.client.Windmill.get_mocked_api": {"tf": 1}}, "df": 2}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.append_to_result_stream": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}}, "df": 8}}}}, "w": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 7}}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 4}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}}, "df": 1}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_mocked_api": {"tf": 1}, "wmill.client.Windmill.get_client": {"tf": 1}, "wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.get_workspace": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_version": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.get_state_path": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}}, "df": 33}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 1}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.init_global_client": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 16, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}}, "df": 2}}}}}}, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.append_to_result_stream": {"tf": 1}, "wmill.client.stream_result": {"tf": 1}}, "df": 4}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}}, "df": 2}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.parse_resource_syntax": {"tf": 1}}, "df": 5}}}}}}, "a": {"docs": {}, "df": 0, "d": {"1": {"docs": {"wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 1}, "docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}}, "df": 2}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}}, "df": 3, "s": {"docs": {"wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}}, "df": 1}}}}}}}, "s": {"3": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.s3_types.S3Object.s3": {"tf": 1}}, "df": 12, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_types.S3Object": {"tf": 1}, "wmill.s3_types.S3Object.s3": {"tf": 1}, "wmill.s3_types.S3Object.storage": {"tf": 1}, "wmill.s3_types.S3Object.presigned": {"tf": 1}}, "df": 4}}}}}}, "f": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}}, "df": 1, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.S3FsClientKwargs": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}}, "df": 2}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.S3FsArgs": {"tf": 1}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.S3FsArgs.key": {"tf": 1}, "wmill.s3_types.S3FsArgs.secret": {"tf": 1}, "wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}, "wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}}, "df": 7}}}}}}}, "docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 14}}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}}, "df": 2}}, "e": {"docs": {"wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.state_path": {"tf": 1}, "wmill.client.Windmill.state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.get_state_path": {"tf": 1}}, "df": 18}}}, "r": {"docs": {"wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"wmill.client.append_to_result_stream": {"tf": 1}, "wmill.client.stream_result": {"tf": 1}}, "df": 2}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_types.S3Object.storage": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 2, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.StorageOptions": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}}, "df": 6}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 14, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 7}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_types.S3FsArgs.secret": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 3}}}}}, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}}, "df": 4}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}}, "df": 8}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}}, "df": 2}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "x": {"docs": {"wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 2}}}}}, "s": {"docs": {}, "df": 0, "l": {"docs": {"wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}}, "df": 2}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 6}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}}, "df": 6}}}}, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "b": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.DuckDbConnectionSettings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.deprecate": {"tf": 1}}, "df": 1}}}}}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}}, "df": 3, "s": {"docs": {"wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}}, "df": 2}}}}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}}, "df": 3}}}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}}, "df": 2}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {"wmill.s3_types.S3FsArgs.key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 5}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}}, "df": 1}}}}}}}}, "fullname": {"root": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 2, "w": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill": {"tf": 1}, "wmill.client": {"tf": 1}, "wmill.client.logger": {"tf": 1}, "wmill.client.JobStatus": {"tf": 1}, "wmill.client.Windmill": {"tf": 1}, "wmill.client.Windmill.__init__": {"tf": 1}, "wmill.client.Windmill.base_url": {"tf": 1}, "wmill.client.Windmill.token": {"tf": 1}, "wmill.client.Windmill.headers": {"tf": 1}, "wmill.client.Windmill.verify": {"tf": 1}, "wmill.client.Windmill.client": {"tf": 1}, "wmill.client.Windmill.workspace": {"tf": 1}, "wmill.client.Windmill.path": {"tf": 1}, "wmill.client.Windmill.mocked_api": {"tf": 1}, "wmill.client.Windmill.get_mocked_api": {"tf": 1}, "wmill.client.Windmill.get_client": {"tf": 1}, "wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}, "wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.version": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.Windmill.whoami": {"tf": 1}, "wmill.client.Windmill.user": {"tf": 1}, "wmill.client.Windmill.state_path": {"tf": 1}, "wmill.client.Windmill.state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.init_global_client": {"tf": 1}, "wmill.client.deprecate": {"tf": 1}, "wmill.client.get_workspace": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_version": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.whoami": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.get_state_path": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}, "wmill.client.task": {"tf": 1}, "wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}, "wmill.client.append_to_result_stream": {"tf": 1}, "wmill.client.stream_result": {"tf": 1}, "wmill.s3_reader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}, "wmill.s3_reader.bytes_generator": {"tf": 1}, "wmill.s3_types": {"tf": 1}, "wmill.s3_types.S3Object": {"tf": 1}, "wmill.s3_types.S3Object.s3": {"tf": 1}, "wmill.s3_types.S3Object.storage": {"tf": 1}, "wmill.s3_types.S3Object.presigned": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}, "wmill.s3_types.S3FsArgs": {"tf": 1}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.S3FsArgs.key": {"tf": 1}, "wmill.s3_types.S3FsArgs.secret": {"tf": 1}, "wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}, "wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}, "wmill.s3_types.StorageOptions": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 153}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill": {"tf": 1}, "wmill.client.Windmill.__init__": {"tf": 1}, "wmill.client.Windmill.base_url": {"tf": 1}, "wmill.client.Windmill.token": {"tf": 1}, "wmill.client.Windmill.headers": {"tf": 1}, "wmill.client.Windmill.verify": {"tf": 1}, "wmill.client.Windmill.client": {"tf": 1}, "wmill.client.Windmill.workspace": {"tf": 1}, "wmill.client.Windmill.path": {"tf": 1}, "wmill.client.Windmill.mocked_api": {"tf": 1}, "wmill.client.Windmill.get_mocked_api": {"tf": 1}, "wmill.client.Windmill.get_client": {"tf": 1}, "wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}, "wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.version": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.Windmill.whoami": {"tf": 1}, "wmill.client.Windmill.user": {"tf": 1}, "wmill.client.Windmill.state_path": {"tf": 1}, "wmill.client.Windmill.state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}}, "df": 59}}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.workspace": {"tf": 1}, "wmill.client.get_workspace": {"tf": 1}}, "df": 2}}}}}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.wait_job": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}}, "df": 2}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {"wmill.client.Windmill.whoami": {"tf": 1}, "wmill.client.whoami": {"tf": 1}}, "df": 2}}}}}}, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client": {"tf": 1}, "wmill.client.logger": {"tf": 1}, "wmill.client.JobStatus": {"tf": 1}, "wmill.client.Windmill": {"tf": 1}, "wmill.client.Windmill.__init__": {"tf": 1}, "wmill.client.Windmill.base_url": {"tf": 1}, "wmill.client.Windmill.token": {"tf": 1}, "wmill.client.Windmill.headers": {"tf": 1}, "wmill.client.Windmill.verify": {"tf": 1}, "wmill.client.Windmill.client": {"tf": 1.4142135623730951}, "wmill.client.Windmill.workspace": {"tf": 1}, "wmill.client.Windmill.path": {"tf": 1}, "wmill.client.Windmill.mocked_api": {"tf": 1}, "wmill.client.Windmill.get_mocked_api": {"tf": 1}, "wmill.client.Windmill.get_client": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}, "wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.version": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.Windmill.whoami": {"tf": 1}, "wmill.client.Windmill.user": {"tf": 1}, "wmill.client.Windmill.state_path": {"tf": 1}, "wmill.client.Windmill.state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.init_global_client": {"tf": 1.4142135623730951}, "wmill.client.deprecate": {"tf": 1}, "wmill.client.get_workspace": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_version": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.whoami": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.get_state_path": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}, "wmill.client.task": {"tf": 1}, "wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}, "wmill.client.append_to_result_stream": {"tf": 1}, "wmill.client.stream_result": {"tf": 1}, "wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}}, "df": 115}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.create_token": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}}, "df": 2}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 7}}}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.logger": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}}, "df": 4}}}}, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {"wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}}, "df": 6, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.JobStatus": {"tf": 1}}, "df": 1}}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}, "wmill.client.init_global_client": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 3}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}}}}}}, "d": {"docs": {"wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}}, "df": 6}}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.base_url": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 9, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"3": {"docs": {"wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.Boto3ConnectionSettings": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 6}}}}}}}}}}}}}}}}}}}, "docs": {}, "df": 0}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.base_url": {"tf": 1}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}}, "df": 4, "s": {"docs": {"wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}}, "df": 2}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}}, "df": 2, "r": {"docs": {"wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.user": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 5, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}, "wmill.client.append_to_result_stream": {"tf": 1}}, "df": 3, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.token": {"tf": 1}, "wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}}, "df": 4}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"wmill.client.task": {"tf": 1}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types": {"tf": 1}, "wmill.s3_types.S3Object": {"tf": 1}, "wmill.s3_types.S3Object.s3": {"tf": 1}, "wmill.s3_types.S3Object.storage": {"tf": 1}, "wmill.s3_types.S3Object.presigned": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}, "wmill.s3_types.S3FsArgs": {"tf": 1}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.S3FsArgs.key": {"tf": 1}, "wmill.s3_types.S3FsArgs.secret": {"tf": 1}, "wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}, "wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}, "wmill.s3_types.StorageOptions": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 31}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.headers": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 4}}}, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {"wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.verify": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.version": {"tf": 1}, "wmill.client.get_version": {"tf": 1}}, "df": 2}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 5}}}}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"wmill.client.Windmill.path": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.state_path": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_state_path": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}}, "df": 8}}, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 3}}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.post": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 3}}}}}}}}}}}}}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}}, "df": 4}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.s3_types.S3Object.presigned": {"tf": 1}}, "df": 1}}}}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}}, "df": 4}}}}}, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "k": {"docs": {"wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.mocked_api": {"tf": 1}, "wmill.client.Windmill.get_mocked_api": {"tf": 1}}, "df": 2}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}}}}}}, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {"wmill.client.Windmill.mocked_api": {"tf": 1}, "wmill.client.Windmill.get_mocked_api": {"tf": 1}}, "df": 2}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.append_to_result_stream": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}}, "df": 8}}}}, "w": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 7}}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 4}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}}, "df": 1}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_mocked_api": {"tf": 1}, "wmill.client.Windmill.get_client": {"tf": 1}, "wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.get_workspace": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_version": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.get_state_path": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}}, "df": 33}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 1}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.init_global_client": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 16, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}}, "df": 2}}}}}}, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}}, "df": 2}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.append_to_result_stream": {"tf": 1}, "wmill.client.stream_result": {"tf": 1}}, "df": 4}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}}, "df": 2}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.parse_resource_syntax": {"tf": 1}}, "df": 5}}}}}}, "a": {"docs": {}, "df": 0, "d": {"1": {"docs": {"wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 1}, "docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.s3_reader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}, "wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 9}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}}, "df": 3, "s": {"docs": {"wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}}, "df": 1}}}}}}}, "s": {"3": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.s3_reader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}, "wmill.s3_reader.bytes_generator": {"tf": 1}, "wmill.s3_types": {"tf": 1}, "wmill.s3_types.S3Object": {"tf": 1}, "wmill.s3_types.S3Object.s3": {"tf": 1.4142135623730951}, "wmill.s3_types.S3Object.storage": {"tf": 1}, "wmill.s3_types.S3Object.presigned": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}, "wmill.s3_types.S3FsArgs": {"tf": 1}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.S3FsArgs.key": {"tf": 1}, "wmill.s3_types.S3FsArgs.secret": {"tf": 1}, "wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}, "wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}, "wmill.s3_types.StorageOptions": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 49, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_types.S3Object": {"tf": 1}, "wmill.s3_types.S3Object.s3": {"tf": 1}, "wmill.s3_types.S3Object.storage": {"tf": 1}, "wmill.s3_types.S3Object.presigned": {"tf": 1}}, "df": 4}}}}}}, "f": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}}, "df": 1, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.S3FsClientKwargs": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}}, "df": 2}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.S3FsArgs": {"tf": 1}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.S3FsArgs.key": {"tf": 1}, "wmill.s3_types.S3FsArgs.secret": {"tf": 1}, "wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}, "wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}}, "df": 7}}}}}}}, "docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 14}}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}}, "df": 2}}, "e": {"docs": {"wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.state_path": {"tf": 1}, "wmill.client.Windmill.state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.get_state_path": {"tf": 1}}, "df": 18}}}, "r": {"docs": {"wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"wmill.client.append_to_result_stream": {"tf": 1}, "wmill.client.stream_result": {"tf": 1}}, "df": 2}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_types.S3Object.storage": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 2, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.StorageOptions": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}}, "df": 6}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 14, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 7}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_types.S3FsArgs.secret": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 3}}}}}, "i": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}}, "df": 4}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}}, "df": 8}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}}, "df": 2}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "x": {"docs": {"wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 2}}}}}, "s": {"docs": {}, "df": 0, "l": {"docs": {"wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}}, "df": 2}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 6}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}}, "df": 6}}}}, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "b": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.DuckDbConnectionSettings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.deprecate": {"tf": 1}}, "df": 1}}}}}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}}, "df": 3, "s": {"docs": {"wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}}, "df": 2}}}}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}}, "df": 3}}}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}}, "df": 2}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {"wmill.s3_types.S3FsArgs.key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}}, "df": 5}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}}, "df": 1}}}}}}}}, "annotation": {"root": {"docs": {"wmill.client.Windmill.user": {"tf": 1}, "wmill.client.Windmill.state_path": {"tf": 1}, "wmill.client.Windmill.state": {"tf": 1}, "wmill.s3_types.S3Object.s3": {"tf": 1}, "wmill.s3_types.S3Object.storage": {"tf": 1}, "wmill.s3_types.S3Object.presigned": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.S3FsArgs.key": {"tf": 1}, "wmill.s3_types.S3FsArgs.secret": {"tf": 1}, "wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}, "wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 26, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.user": {"tf": 1}}, "df": 1}}}}, "s": {"3": {"docs": {"wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 3, "f": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}}, "df": 1}}}}}}}, "docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.state_path": {"tf": 1}, "wmill.s3_types.S3Object.s3": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1}, "wmill.s3_types.S3FsArgs.key": {"tf": 1}, "wmill.s3_types.S3FsArgs.secret": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1}}, "df": 16}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.state": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "[": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"wmill.s3_types.S3Object.storage": {"tf": 1}, "wmill.s3_types.S3Object.presigned": {"tf": 1}}, "df": 2}}}}}}}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1}, "wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1}}, "df": 3}}}}, "w": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 3}}}}}, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1}}, "df": 3}}}}}}}, "default_value": {"root": {"docs": {"wmill.client.logger": {"tf": 1.4142135623730951}, "wmill.client.JobStatus": {"tf": 1}}, "df": 2, "l": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.logger": {"tf": 1}}, "df": 1}, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.logger": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.JobStatus": {"tf": 1}}, "df": 1}}}}}}}, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.logger": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.logger": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.JobStatus": {"tf": 1}}, "df": 1}}}}}}}, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.logger": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.JobStatus": {"tf": 1}}, "df": 1}}}}}}}}}, "g": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.logger": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.JobStatus": {"tf": 1}}, "df": 1}}}}}}, "x": {"2": {"7": {"docs": {"wmill.client.JobStatus": {"tf": 2.449489742783178}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.JobStatus": {"tf": 1}}, "df": 1}}}}}}}}}, "signature": {"root": {"0": {"docs": {"wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}}, "df": 1}, "1": {"docs": {"wmill.client.Windmill.create_token": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 3}, "3": {"9": {"docs": {"wmill.client.Windmill.get_job_status": {"tf": 2.449489742783178}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.Windmill.set_shared_state": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_shared_state": {"tf": 1.4142135623730951}, "wmill.client.get_job_status": {"tf": 2.449489742783178}, "wmill.client.duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.set_resource": {"tf": 1.4142135623730951}, "wmill.client.set_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.get_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.set_shared_state": {"tf": 1.4142135623730951}, "wmill.client.get_shared_state": {"tf": 1.4142135623730951}}, "df": 17}, "docs": {}, "df": 0}, "docs": {"wmill.client.Windmill.__init__": {"tf": 6}, "wmill.client.Windmill.get_mocked_api": {"tf": 4.123105625617661}, "wmill.client.Windmill.get_client": {"tf": 4}, "wmill.client.Windmill.get": {"tf": 5.830951894845301}, "wmill.client.Windmill.post": {"tf": 5.830951894845301}, "wmill.client.Windmill.create_token": {"tf": 5.916079783099616}, "wmill.client.Windmill.run_script_async": {"tf": 8.602325267042627}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 6.928203230275509}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 7.280109889280518}, "wmill.client.Windmill.run_flow_async": {"tf": 8.18535277187245}, "wmill.client.Windmill.run_script": {"tf": 11.74734012447073}, "wmill.client.Windmill.run_script_by_path": {"tf": 10.770329614269007}, "wmill.client.Windmill.run_script_by_hash": {"tf": 10.816653826391969}, "wmill.client.Windmill.wait_job": {"tf": 9.746794344808963}, "wmill.client.Windmill.cancel_running": {"tf": 3.4641016151377544}, "wmill.client.Windmill.get_job": {"tf": 4.47213595499958}, "wmill.client.Windmill.get_root_job_id": {"tf": 5.5677643628300215}, "wmill.client.Windmill.get_id_token": {"tf": 4.47213595499958}, "wmill.client.Windmill.get_job_status": {"tf": 6.4031242374328485}, "wmill.client.Windmill.get_result": {"tf": 5.830951894845301}, "wmill.client.Windmill.get_variable": {"tf": 4.47213595499958}, "wmill.client.Windmill.set_variable": {"tf": 6.48074069840786}, "wmill.client.Windmill.get_resource": {"tf": 6.244997998398398}, "wmill.client.Windmill.set_resource": {"tf": 5.830951894845301}, "wmill.client.Windmill.set_state": {"tf": 4.242640687119285}, "wmill.client.Windmill.set_progress": {"tf": 6.082762530298219}, "wmill.client.Windmill.get_progress": {"tf": 5.5677643628300215}, "wmill.client.Windmill.set_flow_user_state": {"tf": 5.291502622129181}, "wmill.client.Windmill.get_flow_user_state": {"tf": 4.47213595499958}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 6.557438524302}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 6.164414002968976}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 6.164414002968976}, "wmill.client.Windmill.load_s3_file": {"tf": 7}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 7.3484692283495345}, "wmill.client.Windmill.write_s3_file": {"tf": 11}, "wmill.client.Windmill.sign_s3_objects": {"tf": 7.280109889280518}, "wmill.client.Windmill.sign_s3_object": {"tf": 6.557438524302}, "wmill.client.Windmill.whoami": {"tf": 3.4641016151377544}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 5.656854249492381}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 4.898979485566356}, "wmill.client.Windmill.set_shared_state": {"tf": 5.656854249492381}, "wmill.client.Windmill.get_shared_state": {"tf": 4.898979485566356}, "wmill.client.Windmill.get_resume_urls": {"tf": 5.0990195135927845}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 9.539392014169456}, "wmill.client.Windmill.username_to_email": {"tf": 4.47213595499958}, "wmill.client.Windmill.send_teams_message": {"tf": 7.681145747868608}, "wmill.client.init_global_client": {"tf": 3.1622776601683795}, "wmill.client.deprecate": {"tf": 3.7416573867739413}, "wmill.client.get_workspace": {"tf": 3}, "wmill.client.get_root_job_id": {"tf": 5.196152422706632}, "wmill.client.get_version": {"tf": 3}, "wmill.client.run_script_async": {"tf": 7.54983443527075}, "wmill.client.run_flow_async": {"tf": 8.48528137423857}, "wmill.client.run_script_sync": {"tf": 10.295630140987}, "wmill.client.run_script_by_path_async": {"tf": 7.874007874011811}, "wmill.client.run_script_by_hash_async": {"tf": 7.937253933193772}, "wmill.client.run_script_by_path_sync": {"tf": 10.295630140987}, "wmill.client.get_id_token": {"tf": 4}, "wmill.client.get_job_status": {"tf": 6.082762530298219}, "wmill.client.get_result": {"tf": 5.830951894845301}, "wmill.client.duckdb_connection_settings": {"tf": 5.656854249492381}, "wmill.client.polars_connection_settings": {"tf": 5.656854249492381}, "wmill.client.boto3_connection_settings": {"tf": 5.656854249492381}, "wmill.client.load_s3_file": {"tf": 7.0710678118654755}, "wmill.client.load_s3_file_reader": {"tf": 7.416198487095663}, "wmill.client.write_s3_file": {"tf": 11.045361017187261}, "wmill.client.sign_s3_objects": {"tf": 6.928203230275509}, "wmill.client.sign_s3_object": {"tf": 6.082762530298219}, "wmill.client.whoami": {"tf": 3}, "wmill.client.get_state": {"tf": 3}, "wmill.client.get_resource": {"tf": 5.916079783099616}, "wmill.client.set_resource": {"tf": 6.324555320336759}, "wmill.client.set_state": {"tf": 4}, "wmill.client.set_progress": {"tf": 5.916079783099616}, "wmill.client.get_progress": {"tf": 5.196152422706632}, "wmill.client.set_shared_state_pickle": {"tf": 5.0990195135927845}, "wmill.client.get_shared_state_pickle": {"tf": 4.242640687119285}, "wmill.client.set_shared_state": {"tf": 5.0990195135927845}, "wmill.client.get_shared_state": {"tf": 4.242640687119285}, "wmill.client.get_variable": {"tf": 4}, "wmill.client.set_variable": {"tf": 6.164414002968976}, "wmill.client.get_flow_user_state": {"tf": 4}, "wmill.client.set_flow_user_state": {"tf": 4.898979485566356}, "wmill.client.get_state_path": {"tf": 3}, "wmill.client.get_resume_urls": {"tf": 4.69041575982343}, "wmill.client.request_interactive_slack_approval": {"tf": 9.273618495495704}, "wmill.client.send_teams_message": {"tf": 6.928203230275509}, "wmill.client.cancel_running": {"tf": 3}, "wmill.client.run_script": {"tf": 11.313708498984761}, "wmill.client.run_script_by_path": {"tf": 10.295630140987}, "wmill.client.run_script_by_hash": {"tf": 10.344080432788601}, "wmill.client.username_to_email": {"tf": 4}, "wmill.client.task": {"tf": 4.242640687119285}, "wmill.client.parse_resource_syntax": {"tf": 4.58257569495584}, "wmill.client.parse_s3_object": {"tf": 6.082762530298219}, "wmill.client.parse_variable_syntax": {"tf": 4.58257569495584}, "wmill.client.append_to_result_stream": {"tf": 4}, "wmill.client.stream_result": {"tf": 3.4641016151377544}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 7.937253933193772}, "wmill.s3_reader.S3BufferedReader.peek": {"tf": 4.242640687119285}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 4.242640687119285}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 4.242640687119285}, "wmill.s3_reader.bytes_generator": {"tf": 5.830951894845301}}, "df": 103, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_by_path": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1.7320508075688772}, "wmill.client.Windmill.wait_job": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_path_sync": {"tf": 1.7320508075688772}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}, "wmill.client.run_script": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_path": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_hash": {"tf": 1.7320508075688772}}, "df": 18}}, "t": {"docs": {}, "df": 0, "o": {"3": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}}}}}, "docs": {}, "df": 0}}}, "y": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}}, "df": 4, "i": {"docs": {}, "df": 0, "o": {"docs": {"wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 1, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 5}}}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}}, "df": 1}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}}, "df": 2}}}}}}}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}}}}}}, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_async": {"tf": 2}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_flow_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script": {"tf": 2.449489742783178}, "wmill.client.Windmill.run_script_by_path": {"tf": 2}, "wmill.client.Windmill.run_script_by_hash": {"tf": 2}, "wmill.client.Windmill.wait_job": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_root_job_id": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1.4142135623730951}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 2.449489742783178}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2.23606797749979}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1.4142135623730951}, "wmill.client.run_script_async": {"tf": 1.4142135623730951}, "wmill.client.run_flow_async": {"tf": 1.4142135623730951}, "wmill.client.run_script_sync": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_path_async": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_hash_async": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_path_sync": {"tf": 1.7320508075688772}, "wmill.client.get_result": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.write_s3_file": {"tf": 2.6457513110645907}, "wmill.client.get_resource": {"tf": 1.4142135623730951}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_progress": {"tf": 1.4142135623730951}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 2.23606797749979}, "wmill.client.send_teams_message": {"tf": 1}, "wmill.client.run_script": {"tf": 2.23606797749979}, "wmill.client.run_script_by_path": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_hash": {"tf": 1.7320508075688772}, "wmill.client.append_to_result_stream": {"tf": 1}, "wmill.client.stream_result": {"tf": 1}}, "df": 55}}, "t": {"docs": {"wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 13}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}, "wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_path_sync": {"tf": 1.4142135623730951}, "wmill.client.get_result": {"tf": 1}, "wmill.client.run_script": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_path": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_hash": {"tf": 1.4142135623730951}}, "df": 17}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}}, "df": 2}}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 10}}}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 9}}}}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}}, "df": 4, "s": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1.4142135623730951}, "wmill.client.Windmill.sign_s3_object": {"tf": 1.4142135623730951}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1.4142135623730951}, "wmill.client.sign_s3_objects": {"tf": 1.4142135623730951}, "wmill.client.sign_s3_object": {"tf": 1.4142135623730951}, "wmill.client.parse_s3_object": {"tf": 1.4142135623730951}}, "df": 17}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}, "wmill.client.append_to_result_stream": {"tf": 1}}, "df": 3}}}}, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 2}}}}}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}}, "df": 2}}}}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1.4142135623730951}, "wmill.client.Windmill.sign_s3_object": {"tf": 1.4142135623730951}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1.4142135623730951}, "wmill.client.sign_s3_objects": {"tf": 1.4142135623730951}, "wmill.client.sign_s3_object": {"tf": 1.4142135623730951}, "wmill.client.parse_s3_object": {"tf": 1.4142135623730951}}, "df": 17}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.__init__": {"tf": 1}}, "df": 1}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 9}}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 14}}}}}, "s": {"3": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.Windmill.write_s3_file": {"tf": 1.7320508075688772}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1.7320508075688772}, "wmill.client.Windmill.sign_s3_object": {"tf": 1.7320508075688772}, "wmill.client.duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.write_s3_file": {"tf": 1.7320508075688772}, "wmill.client.sign_s3_objects": {"tf": 1.7320508075688772}, "wmill.client.sign_s3_object": {"tf": 1.7320508075688772}, "wmill.client.parse_s3_object": {"tf": 1.7320508075688772}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 18, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.Windmill.write_s3_file": {"tf": 1.7320508075688772}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1.4142135623730951}, "wmill.client.Windmill.sign_s3_object": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.write_s3_file": {"tf": 1.7320508075688772}, "wmill.client.sign_s3_objects": {"tf": 1.4142135623730951}, "wmill.client.sign_s3_object": {"tf": 1.4142135623730951}, "wmill.client.parse_s3_object": {"tf": 1.4142135623730951}}, "df": 11}}}}}}}, "docs": {"wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"wmill.client.Windmill.get_mocked_api": {"tf": 1}, "wmill.client.Windmill.get_client": {"tf": 1}, "wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}, "wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.Windmill.whoami": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 44}}, "c": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}}, "df": 8}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}}, "df": 2}}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}}, "df": 2}}, "e": {"docs": {"wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}}, "df": 8}}}, "r": {"docs": {"wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.run_script_async": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_flow_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_id_token": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.get_variable": {"tf": 1.4142135623730951}, "wmill.client.Windmill.set_variable": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1.4142135623730951}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.Windmill.write_s3_file": {"tf": 2}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}, "wmill.client.Windmill.username_to_email": {"tf": 1.4142135623730951}, "wmill.client.Windmill.send_teams_message": {"tf": 1.4142135623730951}, "wmill.client.deprecate": {"tf": 1}, "wmill.client.get_workspace": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1.4142135623730951}, "wmill.client.get_version": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1.7320508075688772}, "wmill.client.run_flow_async": {"tf": 1.7320508075688772}, "wmill.client.run_script_sync": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_path_async": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_hash_async": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_path_sync": {"tf": 1.4142135623730951}, "wmill.client.get_id_token": {"tf": 1.4142135623730951}, "wmill.client.get_job_status": {"tf": 1}, "wmill.client.get_result": {"tf": 1.4142135623730951}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.write_s3_file": {"tf": 2}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.set_resource": {"tf": 1.4142135623730951}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.get_variable": {"tf": 1.4142135623730951}, "wmill.client.set_variable": {"tf": 1.4142135623730951}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.get_state_path": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 2}, "wmill.client.send_teams_message": {"tf": 1.4142135623730951}, "wmill.client.run_script": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1.4142135623730951}, "wmill.client.parse_resource_syntax": {"tf": 1.4142135623730951}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1.4142135623730951}, "wmill.client.append_to_result_stream": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 2}}, "df": 79, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"wmill.client.stream_result": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}}, "df": 8}}}}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}}}}}, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_reader.S3BufferedReader.peek": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 3}}}}, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.get_mocked_api": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1.4142135623730951}}, "df": 10}}}}}}}, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.sign_s3_object": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}}, "df": 3, "s": {"docs": {"wmill.client.Windmill.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}}, "df": 2}}}}}}, "f": {"docs": {"wmill.client.deprecate": {"tf": 1}}, "df": 1}, "r": {"docs": {"wmill.client.run_script_async": {"tf": 1}}, "df": 1}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_mocked_api": {"tf": 1}, "wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.whoami": {"tf": 1}, "wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.whoami": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.send_teams_message": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 32}}, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}}, "df": 2}}}}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.create_token": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.create_token": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 10}}}}}}, "y": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.create_token": {"tf": 1}}, "df": 1}}}, "o": {"docs": {"wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}}, "df": 2}, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}}}, "h": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "x": {"docs": {"wmill.client.Windmill.get_client": {"tf": 1}, "wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 4}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 9}}}}, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_client": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1.4142135623730951}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 9}}}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}}, "df": 2}}}}}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1.7320508075688772}, "wmill.client.write_s3_file": {"tf": 1.7320508075688772}}, "df": 2}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}}}}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}}, "df": 2}}}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}}, "df": 2}}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 11}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 17}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.s3_reader.bytes_generator": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}}, "df": 2}}}}}}}, "f": {"docs": {"wmill.client.init_global_client": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}}, "df": 2}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 7}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script_by_path": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1.4142135623730951}, "wmill.client.Windmill.wait_job": {"tf": 1.4142135623730951}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 13}}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.deprecate": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 3}}}}, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get": {"tf": 1}, "wmill.client.Windmill.post": {"tf": 1}, "wmill.client.task": {"tf": 1}}, "df": 3}}}}}, "e": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 5}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.get_variable": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1}}, "df": 42}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}}, "df": 2}}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}}, "df": 2}}}}}}}}}}}}}}}}}}}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}}, "df": 4}}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}, "wmill.client.task": {"tf": 1}}, "df": 19}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 11}}}}}, "n": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.set_resource": {"tf": 1}, "wmill.client.Windmill.set_state": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1.4142135623730951}, "wmill.client.get_result": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.set_resource": {"tf": 1.4142135623730951}, "wmill.client.set_state": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 31}}, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get_id_token": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}}, "df": 2}}}}}}}, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.get_resume_urls": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.get_resume_urls": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 4}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1.4142135623730951}, "wmill.client.deprecate": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}}, "df": 9, "t": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.run_script_async": {"tf": 1}, "wmill.client.run_flow_async": {"tf": 1}, "wmill.client.run_script_by_path_async": {"tf": 1}, "wmill.client.run_script_by_hash_async": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 17}}, "s": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.set_variable": {"tf": 1}, "wmill.client.run_script_sync": {"tf": 1}, "wmill.client.run_script_by_path_sync": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 13}, "d": {"docs": {"wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}, "wmill.client.send_teams_message": {"tf": 1}}, "df": 16}, "f": {"docs": {"wmill.client.Windmill.get_resource": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}}, "df": 2}, "o": {"docs": {"wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.s3_reader.bytes_generator": {"tf": 1.4142135623730951}}, "df": 5}}, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {"wmill.client.Windmill.wait_job": {"tf": 1}, "wmill.client.Windmill.get_job": {"tf": 1}, "wmill.client.Windmill.get_root_job_id": {"tf": 1}, "wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.Windmill.get_result": {"tf": 1}, "wmill.client.Windmill.set_progress": {"tf": 1}, "wmill.client.Windmill.get_progress": {"tf": 1}, "wmill.client.get_root_job_id": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}, "wmill.client.get_result": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}}, "df": 12}}, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 6}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.get_job_status": {"tf": 1}, "wmill.client.get_job_status": {"tf": 1}}, "df": 2}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.sign_s3_objects": {"tf": 1.4142135623730951}, "wmill.client.sign_s3_objects": {"tf": 1.4142135623730951}}, "df": 2}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.request_interactive_slack_approval": {"tf": 1}}, "df": 2}}}}}}}}}, "bases": {"root": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "o": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 1}}, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_types.S3Object": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs": {"tf": 1}, "wmill.s3_types.S3FsArgs": {"tf": 1}, "wmill.s3_types.StorageOptions": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings": {"tf": 1}}, "df": 7}}}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_types.S3Object": {"tf": 1}, "wmill.s3_types.S3FsClientKwargs": {"tf": 1}, "wmill.s3_types.S3FsArgs": {"tf": 1}, "wmill.s3_types.StorageOptions": {"tf": 1}, "wmill.s3_types.PolarsConnectionSettings": {"tf": 1}, "wmill.s3_types.Boto3ConnectionSettings": {"tf": 1}, "wmill.s3_types.DuckDbConnectionSettings": {"tf": 1}}, "df": 7}}}}}}, "doc": {"root": {"4": {"2": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "8": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}}, "df": 1}, "docs": {"wmill": {"tf": 1.7320508075688772}, "wmill.client": {"tf": 1.7320508075688772}, "wmill.client.logger": {"tf": 1.7320508075688772}, "wmill.client.JobStatus": {"tf": 1.7320508075688772}, "wmill.client.Windmill": {"tf": 1.7320508075688772}, "wmill.client.Windmill.__init__": {"tf": 1.7320508075688772}, "wmill.client.Windmill.base_url": {"tf": 1.7320508075688772}, "wmill.client.Windmill.token": {"tf": 1.7320508075688772}, "wmill.client.Windmill.headers": {"tf": 1.7320508075688772}, "wmill.client.Windmill.verify": {"tf": 1.7320508075688772}, "wmill.client.Windmill.client": {"tf": 1.7320508075688772}, "wmill.client.Windmill.workspace": {"tf": 1.7320508075688772}, "wmill.client.Windmill.path": {"tf": 1.7320508075688772}, "wmill.client.Windmill.mocked_api": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_mocked_api": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_client": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get": {"tf": 1.7320508075688772}, "wmill.client.Windmill.post": {"tf": 1.7320508075688772}, "wmill.client.Windmill.create_token": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_async": {"tf": 2.8284271247461903}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_flow_async": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script": {"tf": 2.8284271247461903}, "wmill.client.Windmill.run_script_by_path": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1.7320508075688772}, "wmill.client.Windmill.wait_job": {"tf": 1.7320508075688772}, "wmill.client.Windmill.cancel_running": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_job": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_root_job_id": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_id_token": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_job_status": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_result": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_variable": {"tf": 1.7320508075688772}, "wmill.client.Windmill.set_variable": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_resource": {"tf": 1.7320508075688772}, "wmill.client.Windmill.set_resource": {"tf": 1.7320508075688772}, "wmill.client.Windmill.set_state": {"tf": 1.7320508075688772}, "wmill.client.Windmill.set_progress": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_progress": {"tf": 1.7320508075688772}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1.4142135623730951}, "wmill.client.Windmill.version": {"tf": 1.7320508075688772}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file": {"tf": 3}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 3}, "wmill.client.Windmill.write_s3_file": {"tf": 4.242640687119285}, "wmill.client.Windmill.sign_s3_objects": {"tf": 1.7320508075688772}, "wmill.client.Windmill.sign_s3_object": {"tf": 1.7320508075688772}, "wmill.client.Windmill.whoami": {"tf": 1.7320508075688772}, "wmill.client.Windmill.user": {"tf": 1.7320508075688772}, "wmill.client.Windmill.state_path": {"tf": 1.7320508075688772}, "wmill.client.Windmill.state": {"tf": 1.7320508075688772}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.Windmill.set_shared_state": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_shared_state": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_resume_urls": {"tf": 1.7320508075688772}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 10.862780491200215}, "wmill.client.Windmill.username_to_email": {"tf": 1.7320508075688772}, "wmill.client.Windmill.send_teams_message": {"tf": 1.4142135623730951}, "wmill.client.init_global_client": {"tf": 1.7320508075688772}, "wmill.client.deprecate": {"tf": 1.7320508075688772}, "wmill.client.get_workspace": {"tf": 1.7320508075688772}, "wmill.client.get_root_job_id": {"tf": 1.7320508075688772}, "wmill.client.get_version": {"tf": 1.7320508075688772}, "wmill.client.run_script_async": {"tf": 1.7320508075688772}, "wmill.client.run_flow_async": {"tf": 1.7320508075688772}, "wmill.client.run_script_sync": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_path_async": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_hash_async": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_path_sync": {"tf": 1.7320508075688772}, "wmill.client.get_id_token": {"tf": 1.7320508075688772}, "wmill.client.get_job_status": {"tf": 1.7320508075688772}, "wmill.client.get_result": {"tf": 1.7320508075688772}, "wmill.client.duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.write_s3_file": {"tf": 3.1622776601683795}, "wmill.client.sign_s3_objects": {"tf": 1.4142135623730951}, "wmill.client.sign_s3_object": {"tf": 1.4142135623730951}, "wmill.client.whoami": {"tf": 1.4142135623730951}, "wmill.client.get_state": {"tf": 1.4142135623730951}, "wmill.client.get_resource": {"tf": 1.4142135623730951}, "wmill.client.set_resource": {"tf": 1.4142135623730951}, "wmill.client.set_state": {"tf": 1.4142135623730951}, "wmill.client.set_progress": {"tf": 1.4142135623730951}, "wmill.client.get_progress": {"tf": 1.4142135623730951}, "wmill.client.set_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.get_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.set_shared_state": {"tf": 1.4142135623730951}, "wmill.client.get_shared_state": {"tf": 1.4142135623730951}, "wmill.client.get_variable": {"tf": 1.4142135623730951}, "wmill.client.set_variable": {"tf": 1.4142135623730951}, "wmill.client.get_flow_user_state": {"tf": 1.4142135623730951}, "wmill.client.set_flow_user_state": {"tf": 1.4142135623730951}, "wmill.client.get_state_path": {"tf": 1.7320508075688772}, "wmill.client.get_resume_urls": {"tf": 1.7320508075688772}, "wmill.client.request_interactive_slack_approval": {"tf": 1.7320508075688772}, "wmill.client.send_teams_message": {"tf": 1.7320508075688772}, "wmill.client.cancel_running": {"tf": 1.7320508075688772}, "wmill.client.run_script": {"tf": 2.8284271247461903}, "wmill.client.run_script_by_path": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_hash": {"tf": 1.7320508075688772}, "wmill.client.username_to_email": {"tf": 1.7320508075688772}, "wmill.client.task": {"tf": 1.7320508075688772}, "wmill.client.parse_resource_syntax": {"tf": 1.7320508075688772}, "wmill.client.parse_s3_object": {"tf": 1.7320508075688772}, "wmill.client.parse_variable_syntax": {"tf": 1.7320508075688772}, "wmill.client.append_to_result_stream": {"tf": 2.23606797749979}, "wmill.client.stream_result": {"tf": 2.23606797749979}, "wmill.s3_reader": {"tf": 1.7320508075688772}, "wmill.s3_reader.S3BufferedReader": {"tf": 1.7320508075688772}, "wmill.s3_reader.S3BufferedReader.__init__": {"tf": 1.7320508075688772}, "wmill.s3_reader.S3BufferedReader.peek": {"tf": 1.7320508075688772}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 3.872983346207417}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 2.449489742783178}, "wmill.s3_reader.bytes_generator": {"tf": 1.7320508075688772}, "wmill.s3_types": {"tf": 1.7320508075688772}, "wmill.s3_types.S3Object": {"tf": 1.7320508075688772}, "wmill.s3_types.S3Object.s3": {"tf": 1.7320508075688772}, "wmill.s3_types.S3Object.storage": {"tf": 1.7320508075688772}, "wmill.s3_types.S3Object.presigned": {"tf": 1.7320508075688772}, "wmill.s3_types.S3FsClientKwargs": {"tf": 1.7320508075688772}, "wmill.s3_types.S3FsClientKwargs.region_name": {"tf": 1.7320508075688772}, "wmill.s3_types.S3FsArgs": {"tf": 1.7320508075688772}, "wmill.s3_types.S3FsArgs.endpoint_url": {"tf": 1.7320508075688772}, "wmill.s3_types.S3FsArgs.key": {"tf": 1.7320508075688772}, "wmill.s3_types.S3FsArgs.secret": {"tf": 1.7320508075688772}, "wmill.s3_types.S3FsArgs.use_ssl": {"tf": 1.7320508075688772}, "wmill.s3_types.S3FsArgs.cache_regions": {"tf": 1.7320508075688772}, "wmill.s3_types.S3FsArgs.client_kwargs": {"tf": 1.7320508075688772}, "wmill.s3_types.StorageOptions": {"tf": 1.7320508075688772}, "wmill.s3_types.StorageOptions.aws_endpoint_url": {"tf": 1.7320508075688772}, "wmill.s3_types.StorageOptions.aws_access_key_id": {"tf": 1.7320508075688772}, "wmill.s3_types.StorageOptions.aws_secret_access_key": {"tf": 1.7320508075688772}, "wmill.s3_types.StorageOptions.aws_region": {"tf": 1.7320508075688772}, "wmill.s3_types.StorageOptions.aws_allow_http": {"tf": 1.7320508075688772}, "wmill.s3_types.PolarsConnectionSettings": {"tf": 1.7320508075688772}, "wmill.s3_types.PolarsConnectionSettings.s3fs_args": {"tf": 1.7320508075688772}, "wmill.s3_types.PolarsConnectionSettings.storage_options": {"tf": 1.7320508075688772}, "wmill.s3_types.Boto3ConnectionSettings": {"tf": 1.7320508075688772}, "wmill.s3_types.Boto3ConnectionSettings.endpoint_url": {"tf": 1.7320508075688772}, "wmill.s3_types.Boto3ConnectionSettings.region_name": {"tf": 1.7320508075688772}, "wmill.s3_types.Boto3ConnectionSettings.use_ssl": {"tf": 1.7320508075688772}, "wmill.s3_types.Boto3ConnectionSettings.aws_access_key_id": {"tf": 1.7320508075688772}, "wmill.s3_types.Boto3ConnectionSettings.aws_secret_access_key": {"tf": 1.7320508075688772}, "wmill.s3_types.DuckDbConnectionSettings": {"tf": 1.7320508075688772}, "wmill.s3_types.DuckDbConnectionSettings.connection_settings_str": {"tf": 1.7320508075688772}}, "df": 153, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 5}, "o": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.set_resource": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}}, "df": 2}}}}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}}, "df": 2}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.whoami": {"tf": 1}}, "df": 1, "l": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}}, "df": 2}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 6}}}}}, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 6}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 2}, "wmill.client.Windmill.write_s3_file": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1.7320508075688772}}, "df": 5}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 3}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 3}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"1": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}, "2": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}}}}, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.Windmill.send_teams_message": {"tf": 1.4142135623730951}, "wmill.client.get_id_token": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.set_resource": {"tf": 1.4142135623730951}, "wmill.client.get_variable": {"tf": 1.4142135623730951}, "wmill.client.set_variable": {"tf": 1.4142135623730951}, "wmill.client.get_flow_user_state": {"tf": 1.4142135623730951}, "wmill.client.set_flow_user_state": {"tf": 1.4142135623730951}, "wmill.client.append_to_result_stream": {"tf": 1}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 26, "n": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 10, "d": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 2.449489742783178}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 22}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}}, "df": 2}}}}}}}}, "s": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}}, "df": 14, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1.4142135623730951}}, "df": 1}}}}, "t": {"docs": {"wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 10}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 1}}}, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.append_to_result_stream": {"tf": 1}, "wmill.client.stream_result": {"tf": 1}}, "df": 3}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}}, "df": 1, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}, "p": {"docs": {}, "df": 0, "p": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2.23606797749979}}, "df": 1, "#": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1, "r": {"1": {"2": {"3": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}}, "df": 1}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 4}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.append_to_result_stream": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}, "d": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}}}}, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.write_s3_file": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "w": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}}}}, "s": {"3": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file": {"tf": 2.23606797749979}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 2.23606797749979}, "wmill.client.Windmill.write_s3_file": {"tf": 2.6457513110645907}, "wmill.client.duckdb_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.polars_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.boto3_connection_settings": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1.4142135623730951}, "wmill.client.sign_s3_object": {"tf": 1.4142135623730951}, "wmill.client.parse_s3_object": {"tf": 1}}, "df": 15, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1.7320508075688772}, "wmill.client.Windmill.write_s3_file": {"tf": 1.4142135623730951}, "wmill.client.parse_s3_object": {"tf": 1}}, "df": 4}}}}}}}, "docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}, "wmill.client.run_script": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 11}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.run_script": {"tf": 1}}, "df": 3}}}, "g": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}}, "df": 2}}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 6}}}}}}}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "x": {"docs": {"wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 2}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}}, "df": 2}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "y": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1.4142135623730951}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1.4142135623730951}}, "df": 13, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 6}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}}, "df": 1, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}, "e": {"docs": {"wmill.client.write_s3_file": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 14}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.append_to_result_stream": {"tf": 1.4142135623730951}, "wmill.client.stream_result": {"tf": 2.449489742783178}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 5, "s": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.set_resource": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 6}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}, "y": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}}, "df": 2}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}}, "df": 8}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 2}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 3.1622776601683795}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}}, "df": 1}}}}}}, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}}, "j": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_flow_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 5}}, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}}, "df": 1}}}, "w": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 12, "s": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.whoami": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.7320508075688772}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 15}}}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}, "wmill.client.append_to_result_stream": {"tf": 1.4142135623730951}, "wmill.client.stream_result": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 10}}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.parse_resource_syntax": {"tf": 1}}, "df": 11}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1.4142135623730951}}, "df": 3, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.load_s3_file_reader": {"tf": 1.7320508075688772}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 2}}, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}}, "df": 1}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2.6457513110645907}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2, "d": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script": {"tf": 1.7320508075688772}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.run_script": {"tf": 1.7320508075688772}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 8, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}}, "df": 2}}}}}}, "b": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 1}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}, "w": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 2.23606797749979}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 3}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.set_resource": {"tf": 1.4142135623730951}, "wmill.client.set_variable": {"tf": 1.4142135623730951}}, "df": 3, "s": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 11}}, "d": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2.449489742783178}, "wmill.client.Windmill.send_teams_message": {"tf": 1}}, "df": 6}, "n": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 17, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.run_script": {"tf": 1}}, "df": 3}}}}}, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 6}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 6}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}}, "o": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}}}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 3}}}, "l": {"docs": {}, "df": 0, "y": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 2}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 2}}}}}}}, "f": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.7320508075688772}}, "df": 5}, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.Windmill.username_to_email": {"tf": 1.7320508075688772}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1.7320508075688772}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 2.449489742783178}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 6, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "o": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 1}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.run_script": {"tf": 1}}, "df": 3}}}}}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "b": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.write_s3_file": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}, "y": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "o": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.set_resource": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 4}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.run_script": {"tf": 1}}, "df": 3}}}}}}, "i": {"docs": {}, "df": 0, "a": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}, "e": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1.7320508075688772}, "wmill.client.username_to_email": {"tf": 1.7320508075688772}}, "df": 2}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"1": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 3, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.run_script": {"tf": 1}}, "df": 4, "r": {"docs": {"wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.whoami": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 6, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1.7320508075688772}, "wmill.client.username_to_email": {"tf": 1.7320508075688772}}, "df": 2}}}}, "s": {"docs": {"wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}}, "df": 2}}, "f": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}, "d": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}}, "df": 3}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 11}}}, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}, "/": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.write_s3_file": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}, "t": {"docs": {}, "df": 0, "f": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}}, "df": 1}}, "/": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}}, "p": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 2, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.write_s3_file": {"tf": 1}}, "df": 1}}}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 2}}}}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 1, "y": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1.4142135623730951}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.run_script": {"tf": 1.4142135623730951}, "wmill.client.run_script_by_path": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 11, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1, "s": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1.4142135623730951}}, "df": 6}}}}, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"3": {"docs": {"wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 2}, "docs": {}, "df": 0}}}, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 3}}}}, "t": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 3}, "f": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}}, "df": 5}, "a": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_path_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_path": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.7320508075688772}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_path": {"tf": 1}}, "df": 12, "/": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 3}}}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 3}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}}, "df": 2}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}}, "y": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 3}}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.load_s3_file_reader": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "w": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}}, "df": 2}}}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}}, "df": 8}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}}, "df": 2}}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2.23606797749979}, "wmill.client.run_script": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 6, "g": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.write_s3_file": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "f": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.7320508075688772}, "wmill.client.Windmill.username_to_email": {"tf": 2}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}, "wmill.client.username_to_email": {"tf": 2}}, "df": 12}, "b": {"docs": {}, "df": 0, "j": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 2}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1.7320508075688772}}, "df": 3, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.sign_s3_object": {"tf": 1.4142135623730951}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 5, "s": {"docs": {"wmill.client.sign_s3_objects": {"tf": 1}}, "df": 1}}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 2}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1, "l": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2.23606797749979}}, "df": 1}}, "b": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 2, "l": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}, "e": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 2}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {"wmill.client.Windmill.run_script_async": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash_async": {"tf": 1}, "wmill.client.Windmill.run_script": {"tf": 1}, "wmill.client.Windmill.run_script_by_hash": {"tf": 1}, "wmill.client.run_script": {"tf": 1}, "wmill.client.run_script_by_hash": {"tf": 1}}, "df": 6}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 6}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, ":": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "w": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.write_s3_file": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}}}}}}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {"wmill.client.Windmill.run_flow_async": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2.8284271247461903}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 6}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}, "wmill.client.parse_resource_syntax": {"tf": 1}, "wmill.client.parse_s3_object": {"tf": 1}, "wmill.client.parse_variable_syntax": {"tf": 1}}, "df": 14}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 2}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 2.23606797749979}, "wmill.client.Windmill.write_s3_file": {"tf": 3.1622776601683795}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}}, "df": 6}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.7320508075688772}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1.4142135623730951}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 7, "m": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2.23606797749979}}, "df": 1, "a": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.parse_s3_object": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"wmill.client.Windmill.set_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.set_shared_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.set_shared_state": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}}, "df": 8}}}}, "o": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.7320508075688772}}, "df": 1, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.write_s3_file": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.set_resource": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.load_s3_file": {"tf": 1}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 2}, "wmill.client.username_to_email": {"tf": 2}}, "df": 2}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.write_s3_file": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 3}}}}, "t": {"docs": {}, "df": 0, "c": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}, "o": {"docs": {}, "df": 0, "f": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 2}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1.4142135623730951}}, "df": 2}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.cancel_running": {"tf": 1}, "wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.set_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.Windmill.set_shared_state": {"tf": 1.4142135623730951}, "wmill.client.Windmill.get_shared_state": {"tf": 1.4142135623730951}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 4}, "wmill.client.Windmill.username_to_email": {"tf": 2.8284271247461903}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}, "wmill.client.whoami": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_state": {"tf": 1}, "wmill.client.set_progress": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.set_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.get_shared_state_pickle": {"tf": 1.4142135623730951}, "wmill.client.set_shared_state": {"tf": 1.4142135623730951}, "wmill.client.get_shared_state": {"tf": 1.4142135623730951}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.client.cancel_running": {"tf": 1}, "wmill.client.username_to_email": {"tf": 2.8284271247461903}, "wmill.client.append_to_result_stream": {"tf": 1.4142135623730951}, "wmill.client.stream_result": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 2.449489742783178}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 43}, "a": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 10}}, "i": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 3}, "r": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 6}}}}, "o": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}, "wmill.client.Windmill.username_to_email": {"tf": 1.4142135623730951}, "wmill.client.Windmill.send_teams_message": {"tf": 1.4142135623730951}, "wmill.client.get_id_token": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.client.sign_s3_objects": {"tf": 1}, "wmill.client.sign_s3_object": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1.4142135623730951}, "wmill.client.append_to_result_stream": {"tf": 1.7320508075688772}, "wmill.client.stream_result": {"tf": 1.7320508075688772}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1.4142135623730951}}, "df": 19, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1, "s": {"docs": {"wmill.client.sign_s3_objects": {"tf": 1}}, "df": 1}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1.4142135623730951}}, "df": 3}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}}, "df": 1}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.append_to_result_stream": {"tf": 1.7320508075688772}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.write_s3_file": {"tf": 1.7320508075688772}}, "df": 1}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.get_variable": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}, "wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 9}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_shared_state_pickle": {"tf": 1}, "wmill.client.Windmill.get_shared_state": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.get_id_token": {"tf": 1}, "wmill.client.get_state": {"tf": 1}, "wmill.client.get_resource": {"tf": 1}, "wmill.client.get_progress": {"tf": 1}, "wmill.client.get_shared_state_pickle": {"tf": 1}, "wmill.client.get_shared_state": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 12}}, "c": {"docs": {}, "df": 0, "p": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.write_s3_file": {"tf": 1}}, "df": 1}}}}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"1": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}, "2": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}, "docs": {"wmill.client.Windmill.set_flow_user_state": {"tf": 1}, "wmill.client.Windmill.get_flow_user_state": {"tf": 1}, "wmill.client.get_flow_user_state": {"tf": 1}, "wmill.client.set_flow_user_state": {"tf": 1}}, "df": 4}}}, "n": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.get_duckdb_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_polars_connection_settings": {"tf": 1}, "wmill.client.Windmill.get_boto3_connection_settings": {"tf": 1}, "wmill.client.duckdb_connection_settings": {"tf": 1}, "wmill.client.polars_connection_settings": {"tf": 1}, "wmill.client.boto3_connection_settings": {"tf": 1}}, "df": 6}}}}}}}, "w": {"docs": {"wmill.s3_reader.S3BufferedReader": {"tf": 1}}, "df": 1}, "g": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}, "o": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.set_resource": {"tf": 1}, "wmill.client.set_variable": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 5, "e": {"docs": {}, "df": 0, "s": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}, "n": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1.4142135623730951}}, "df": 2}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.load_s3_file": {"tf": 1}, "wmill.client.load_s3_file_reader": {"tf": 1}}, "df": 4}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}}, "f": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.write_s3_file": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.get_id_token": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.sign_s3_objects": {"tf": 1}}, "df": 1}}}}, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 5}}}}}}}}, "m": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}, "wmill.client.Windmill.username_to_email": {"tf": 1.4142135623730951}, "wmill.client.username_to_email": {"tf": 1.4142135623730951}}, "df": 3, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1}, "wmill.client.Windmill.load_s3_file_reader": {"tf": 1.4142135623730951}, "wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 3}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"wmill.client.Windmill.load_s3_file_reader": {"tf": 1}, "wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 4, "i": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.4142135623730951}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}, "wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1.7320508075688772}, "wmill.client.get_resource": {"tf": 1}}, "df": 3}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.write_s3_file": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 3}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1.7320508075688772}}, "df": 1}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}, "wmill.client.Windmill.send_teams_message": {"tf": 1}}, "df": 2}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}, "m": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.load_s3_file": {"tf": 1.4142135623730951}, "wmill.client.Windmill.write_s3_file": {"tf": 1.7320508075688772}}, "df": 2}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"wmill.client.Windmill.write_s3_file": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 2}, "wmill.client.Windmill.send_teams_message": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"wmill.client.Windmill.username_to_email": {"tf": 1}, "wmill.client.username_to_email": {"tf": 1}}, "df": 2}}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}, "z": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {"wmill.client.write_s3_file": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}, "wmill.s3_reader.S3BufferedReader.read1": {"tf": 1}}, "df": 2}}, "d": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.request_interactive_slack_approval": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "t": {"docs": {"wmill.client.Windmill.send_teams_message": {"tf": 1}}, "df": 1}}}}}}}}, "d": {"docs": {}, "df": 0, "n": {"docs": {"wmill.client.write_s3_file": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "y": {"docs": {"wmill.s3_reader.S3BufferedReader.read": {"tf": 1}}, "df": 1}}}}}}, "pipeline": ["trimmer"], "_isPrebuiltIndex": true};
+
+ // mirrored in build-search-index.js (part 1)
+ // Also split on html tags. this is a cheap heuristic, but good enough.
+ elasticlunr.tokenizer.setSeperator(/[\s\-.;&_'"=,()]+|<[^>]*>/);
+
+ let searchIndex;
+ if (docs._isPrebuiltIndex) {
+ console.info("using precompiled search index");
+ searchIndex = elasticlunr.Index.load(docs);
+ } else {
+ console.time("building search index");
+ // mirrored in build-search-index.js (part 2)
+ searchIndex = elasticlunr(function () {
+ this.pipeline.remove(elasticlunr.stemmer);
+ this.pipeline.remove(elasticlunr.stopWordFilter);
+ this.addField("qualname");
+ this.addField("fullname");
+ this.addField("annotation");
+ this.addField("default_value");
+ this.addField("signature");
+ this.addField("bases");
+ this.addField("doc");
+ this.setRef("fullname");
+ });
+ for (let doc of docs) {
+ searchIndex.addDoc(doc);
+ }
+ console.timeEnd("building search index");
+ }
+
+ return (term) => searchIndex.search(term, {
+ fields: {
+ qualname: {boost: 4},
+ fullname: {boost: 2},
+ annotation: {boost: 2},
+ default_value: {boost: 2},
+ signature: {boost: 2},
+ bases: {boost: 2},
+ doc: {boost: 1},
+ },
+ expand: true
+ });
+})();
\ No newline at end of file
diff --git a/python-client/docs/wmill.html b/python-client/docs/wmill.html
new file mode 100644
index 0000000000..fafe32ad0b
--- /dev/null
+++ b/python-client/docs/wmill.html
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+ wmill API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python-client/docs/wmill/client.html b/python-client/docs/wmill/client.html
new file mode 100644
index 0000000000..aea20f915b
--- /dev/null
+++ b/python-client/docs/wmill/client.html
@@ -0,0 +1,6459 @@
+
+
+
+
+
+
+ wmill.client API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ View Source
+
+ 1 from __future__ import annotations
+ 2
+ 3 import atexit
+ 4 import datetime as dt
+ 5 import functools
+ 6 from io import BufferedReader , BytesIO
+ 7 import logging
+ 8 import os
+ 9 import random
+ 10 import time
+ 11 import warnings
+ 12 import json
+ 13 from json import JSONDecodeError
+ 14 from typing import Dict , Any , Union , Literal , Optional
+ 15 import re
+ 16
+ 17 import httpx
+ 18
+ 19 from .s3_reader import S3BufferedReader , bytes_generator
+ 20 from .s3_types import (
+ 21 Boto3ConnectionSettings ,
+ 22 DuckDbConnectionSettings ,
+ 23 PolarsConnectionSettings ,
+ 24 S3Object ,
+ 25 )
+ 26
+ 27 _client : "Windmill | None" = None
+ 28
+ 29 logger = logging . getLogger ( "windmill_client" )
+ 30
+ 31 JobStatus = Literal [ "RUNNING" , "WAITING" , "COMPLETED" ]
+ 32
+ 33
+ 34 class Windmill :
+ 35 def __init__ ( self , base_url = None , token = None , workspace = None , verify = True ):
+ 36 base = (
+ 37 base_url
+ 38 or os . environ . get ( "BASE_INTERNAL_URL" )
+ 39 or os . environ . get ( "WM_BASE_URL" )
+ 40 )
+ 41
+ 42 self . base_url = f " { base } /api"
+ 43 self . token = token or os . environ . get ( "WM_TOKEN" )
+ 44 self . headers = {
+ 45 "Content-Type" : "application/json" ,
+ 46 "Authorization" : f "Bearer { self . token } " ,
+ 47 }
+ 48 self . verify = verify
+ 49 self . client = self . get_client ()
+ 50 self . workspace = workspace or os . environ . get ( "WM_WORKSPACE" )
+ 51 self . path = os . environ . get ( "WM_JOB_PATH" )
+ 52
+ 53 self . mocked_api = self . get_mocked_api ()
+ 54
+ 55 assert self . workspace , (
+ 56 f "workspace required as an argument or as WM_WORKSPACE environment variable"
+ 57 )
+ 58
+ 59 def get_mocked_api ( self ) -> Optional [ dict ]:
+ 60 mocked_path = os . environ . get ( "WM_MOCKED_API_FILE" )
+ 61 if not mocked_path :
+ 62 return None
+ 63 logger . info ( "Using mocked API from %s " , mocked_path )
+ 64 mocked_api = { "variables" : {}, "resources" : {}}
+ 65 try :
+ 66 with open ( mocked_path , "r" ) as f :
+ 67 incoming_mocked_api = json . load ( f )
+ 68 mocked_api = { ** mocked_api , ** incoming_mocked_api }
+ 69 except Exception as e :
+ 70 logger . warning (
+ 71 "Error parsing mocked API file at path %s Using empty mocked API." ,
+ 72 mocked_path ,
+ 73 )
+ 74 logger . debug ( e )
+ 75 return mocked_api
+ 76
+ 77 def get_client ( self ) -> httpx . Client :
+ 78 return httpx . Client (
+ 79 base_url = self . base_url ,
+ 80 headers = self . headers ,
+ 81 verify = self . verify ,
+ 82 )
+ 83
+ 84 def get ( self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response :
+ 85 endpoint = endpoint . lstrip ( "/" )
+ 86 resp = self . client . get ( f "/ { endpoint } " , ** kwargs )
+ 87 if raise_for_status :
+ 88 try :
+ 89 resp . raise_for_status ()
+ 90 except httpx . HTTPStatusError as err :
+ 91 error = f " { err . request . url } : { err . response . status_code } , { err . response . text } "
+ 92 logger . error ( error )
+ 93 raise Exception ( error )
+ 94 return resp
+ 95
+ 96 def post ( self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response :
+ 97 endpoint = endpoint . lstrip ( "/" )
+ 98 resp = self . client . post ( f "/ { endpoint } " , ** kwargs )
+ 99 if raise_for_status :
+ 100 try :
+ 101 resp . raise_for_status ()
+ 102 except httpx . HTTPStatusError as err :
+ 103 error = f " { err . request . url } : { err . response . status_code } , { err . response . text } "
+ 104 logger . error ( error )
+ 105 raise Exception ( error )
+ 106 return resp
+ 107
+ 108 def create_token ( self , duration = dt . timedelta ( days = 1 )) -> str :
+ 109 endpoint = "/users/tokens/create"
+ 110 payload = {
+ 111 "label" : f "refresh { time . time () } " ,
+ 112 "expiration" : ( dt . datetime . now () + duration ) . strftime ( "%Y-%m- %d T%H:%M:%SZ" ),
+ 113 }
+ 114 return self . post ( endpoint , json = payload ) . text
+ 115
+ 116 def run_script_async (
+ 117 self ,
+ 118 path : str = None ,
+ 119 hash_ : str = None ,
+ 120 args : dict = None ,
+ 121 scheduled_in_secs : int = None ,
+ 122 ) -> str :
+ 123 """Create a script job and return its job id.
+ 124
+ 125 .. deprecated:: Use run_script_by_path_async or run_script_by_hash_async instead.
+ 126 """
+ 127 logging . warning (
+ 128 "run_script_async is deprecated. Use run_script_by_path_async or run_script_by_hash_async instead." ,
+ 129 )
+ 130 assert not ( path and hash_ ), "path and hash_ are mutually exclusive"
+ 131 return self . _run_script_async_internal ( path = path , hash_ = hash_ , args = args , scheduled_in_secs = scheduled_in_secs )
+ 132
+ 133 def _run_script_async_internal (
+ 134 self ,
+ 135 path : str = None ,
+ 136 hash_ : str = None ,
+ 137 args : dict = None ,
+ 138 scheduled_in_secs : int = None ,
+ 139 ) -> str :
+ 140 """Internal helper for running scripts asynchronously."""
+ 141 args = args or {}
+ 142 params = { "scheduled_in_secs" : scheduled_in_secs } if scheduled_in_secs else {}
+ 143 if os . environ . get ( "WM_JOB_ID" ):
+ 144 params [ "parent_job" ] = os . environ . get ( "WM_JOB_ID" )
+ 145 if os . environ . get ( "WM_ROOT_FLOW_JOB_ID" ):
+ 146 params [ "root_job" ] = os . environ . get ( "WM_ROOT_FLOW_JOB_ID" )
+ 147
+ 148 if path :
+ 149 endpoint = f "/w/ { self . workspace } /jobs/run/p/ { path } "
+ 150 elif hash_ :
+ 151 endpoint = f "/w/ { self . workspace } /jobs/run/h/ { hash_ } "
+ 152 else :
+ 153 raise Exception ( "path or hash_ must be provided" )
+ 154
+ 155 return self . post ( endpoint , json = args , params = params ) . text
+ 156
+ 157 def run_script_by_path_async (
+ 158 self ,
+ 159 path : str ,
+ 160 args : dict = None ,
+ 161 scheduled_in_secs : int = None ,
+ 162 ) -> str :
+ 163 """Create a script job by path and return its job id."""
+ 164 return self . _run_script_async_internal ( path = path , args = args , scheduled_in_secs = scheduled_in_secs )
+ 165
+ 166 def run_script_by_hash_async (
+ 167 self ,
+ 168 hash_ : str ,
+ 169 args : dict = None ,
+ 170 scheduled_in_secs : int = None ,
+ 171 ) -> str :
+ 172 """Create a script job by hash and return its job id."""
+ 173 return self . _run_script_async_internal ( hash_ = hash_ , args = args , scheduled_in_secs = scheduled_in_secs )
+ 174
+ 175 def run_flow_async (
+ 176 self ,
+ 177 path : str ,
+ 178 args : dict = None ,
+ 179 scheduled_in_secs : int = None ,
+ 180 # can only be set to false if this the job will be fully await and not concurrent with any other job
+ 181 # as otherwise the child flow and its own child will store their state in the parent job which will
+ 182 # lead to incorrectness and failures
+ 183 do_not_track_in_parent : bool = True ,
+ 184 ) -> str :
+ 185 """Create a flow job and return its job id."""
+ 186 args = args or {}
+ 187 params = { "scheduled_in_secs" : scheduled_in_secs } if scheduled_in_secs else {}
+ 188 if not do_not_track_in_parent :
+ 189 if os . environ . get ( "WM_JOB_ID" ):
+ 190 params [ "parent_job" ] = os . environ . get ( "WM_JOB_ID" )
+ 191 if os . environ . get ( "WM_ROOT_FLOW_JOB_ID" ):
+ 192 params [ "root_job" ] = os . environ . get ( "WM_ROOT_FLOW_JOB_ID" )
+ 193 if path :
+ 194 endpoint = f "/w/ { self . workspace } /jobs/run/f/ { path } "
+ 195 else :
+ 196 raise Exception ( "path must be provided" )
+ 197 return self . post ( endpoint , json = args , params = params ) . text
+ 198
+ 199 def run_script (
+ 200 self ,
+ 201 path : str = None ,
+ 202 hash_ : str = None ,
+ 203 args : dict = None ,
+ 204 timeout : dt . timedelta | int | float | None = None ,
+ 205 verbose : bool = False ,
+ 206 cleanup : bool = True ,
+ 207 assert_result_is_not_none : bool = False ,
+ 208 ) -> Any :
+ 209 """Run script synchronously and return its result.
+ 210
+ 211 .. deprecated:: Use run_script_by_path or run_script_by_hash instead.
+ 212 """
+ 213 logging . warning (
+ 214 "run_script is deprecated. Use run_script_by_path or run_script_by_hash instead." ,
+ 215 )
+ 216 assert not ( path and hash_ ), "path and hash_ are mutually exclusive"
+ 217 return self . _run_script_internal (
+ 218 path = path , hash_ = hash_ , args = args , timeout = timeout , verbose = verbose ,
+ 219 cleanup = cleanup , assert_result_is_not_none = assert_result_is_not_none
+ 220 )
+ 221
+ 222 def _run_script_internal (
+ 223 self ,
+ 224 path : str = None ,
+ 225 hash_ : str = None ,
+ 226 args : dict = None ,
+ 227 timeout : dt . timedelta | int | float | None = None ,
+ 228 verbose : bool = False ,
+ 229 cleanup : bool = True ,
+ 230 assert_result_is_not_none : bool = False ,
+ 231 ) -> Any :
+ 232 """Internal helper for running scripts synchronously."""
+ 233 args = args or {}
+ 234
+ 235 if verbose :
+ 236 if path :
+ 237 logger . info ( f "running ` { path } ` synchronously with { args = } " )
+ 238 elif hash_ :
+ 239 logger . info ( f "running script with hash ` { hash_ } ` synchronously with { args = } " )
+ 240
+ 241 if isinstance ( timeout , dt . timedelta ):
+ 242 timeout = timeout . total_seconds ()
+ 243
+ 244 job_id = self . _run_script_async_internal ( path = path , hash_ = hash_ , args = args )
+ 245 return self . wait_job (
+ 246 job_id , timeout , verbose , cleanup , assert_result_is_not_none
+ 247 )
+ 248
+ 249 def run_script_by_path (
+ 250 self ,
+ 251 path : str ,
+ 252 args : dict = None ,
+ 253 timeout : dt . timedelta | int | float | None = None ,
+ 254 verbose : bool = False ,
+ 255 cleanup : bool = True ,
+ 256 assert_result_is_not_none : bool = False ,
+ 257 ) -> Any :
+ 258 """Run script by path synchronously and return its result."""
+ 259 return self . _run_script_internal (
+ 260 path = path , args = args , timeout = timeout , verbose = verbose ,
+ 261 cleanup = cleanup , assert_result_is_not_none = assert_result_is_not_none
+ 262 )
+ 263
+ 264 def run_script_by_hash (
+ 265 self ,
+ 266 hash_ : str ,
+ 267 args : dict = None ,
+ 268 timeout : dt . timedelta | int | float | None = None ,
+ 269 verbose : bool = False ,
+ 270 cleanup : bool = True ,
+ 271 assert_result_is_not_none : bool = False ,
+ 272 ) -> Any :
+ 273 """Run script by hash synchronously and return its result."""
+ 274 return self . _run_script_internal (
+ 275 hash_ = hash_ , args = args , timeout = timeout , verbose = verbose ,
+ 276 cleanup = cleanup , assert_result_is_not_none = assert_result_is_not_none
+ 277 )
+ 278
+ 279 def wait_job (
+ 280 self ,
+ 281 job_id ,
+ 282 timeout : dt . timedelta | int | float | None = None ,
+ 283 verbose : bool = False ,
+ 284 cleanup : bool = True ,
+ 285 assert_result_is_not_none : bool = False ,
+ 286 ):
+ 287 def cancel_job ():
+ 288 logger . warning ( f "cancelling job: { job_id } " )
+ 289 self . post (
+ 290 f "/w/ { self . workspace } /jobs_u/queue/cancel/ { job_id } " ,
+ 291 json = { "reason" : "parent script cancelled" },
+ 292 ) . raise_for_status ()
+ 293
+ 294 if cleanup :
+ 295 atexit . register ( cancel_job )
+ 296
+ 297 start_time = time . time ()
+ 298
+ 299 if isinstance ( timeout , dt . timedelta ):
+ 300 timeout = timeout . total_seconds ()
+ 301
+ 302 while True :
+ 303 result_res = self . get (
+ 304 f "/w/ { self . workspace } /jobs_u/completed/get_result_maybe/ { job_id } " , True
+ 305 ) . json ()
+ 306
+ 307 started = result_res [ "started" ]
+ 308 completed = result_res [ "completed" ]
+ 309 success = result_res [ "success" ]
+ 310
+ 311 if not started and verbose :
+ 312 logger . info ( f "job { job_id } has not started yet" )
+ 313
+ 314 if cleanup and completed :
+ 315 atexit . unregister ( cancel_job )
+ 316
+ 317 if completed :
+ 318 result = result_res [ "result" ]
+ 319 if success :
+ 320 if result is None and assert_result_is_not_none :
+ 321 raise Exception ( "Result was none" )
+ 322 return result
+ 323 else :
+ 324 error = result [ "error" ]
+ 325 raise Exception ( f "Job { job_id } was not successful: { str ( error ) } " )
+ 326
+ 327 if timeout and (( time . time () - start_time ) > timeout ):
+ 328 msg = "reached timeout"
+ 329 logger . warning ( msg )
+ 330 self . post (
+ 331 f "/w/ { self . workspace } /jobs_u/queue/cancel/ { job_id } " ,
+ 332 json = { "reason" : msg },
+ 333 )
+ 334 raise TimeoutError ( msg )
+ 335 if verbose :
+ 336 logger . info ( f "sleeping 0.5 seconds for { job_id = } " )
+ 337
+ 338 time . sleep ( 0.5 )
+ 339
+ 340 def cancel_running ( self ) -> dict :
+ 341 """Cancel currently running executions of the same script."""
+ 342 logger . info ( "canceling running executions of this script" )
+ 343
+ 344 jobs = self . get (
+ 345 f "/w/ { self . workspace } /jobs/list" ,
+ 346 params = {
+ 347 "running" : "true" ,
+ 348 "script_path_exact" : self . path ,
+ 349 },
+ 350 ) . json ()
+ 351
+ 352 current_job_id = os . environ . get ( "WM_JOB_ID" )
+ 353
+ 354 logger . debug ( f " { current_job_id = } " )
+ 355
+ 356 job_ids = [ j [ "id" ] for j in jobs if j [ "id" ] != current_job_id ]
+ 357
+ 358 if job_ids :
+ 359 logger . info ( f "cancelling the following job ids: { job_ids } " )
+ 360 else :
+ 361 logger . info ( "no previous executions to cancel" )
+ 362
+ 363 result = {}
+ 364
+ 365 for id_ in job_ids :
+ 366 result [ id_ ] = self . post (
+ 367 f "/w/ { self . workspace } /jobs_u/queue/cancel/ { id_ } " ,
+ 368 json = { "reason" : "killed by `cancel_running` method" },
+ 369 )
+ 370
+ 371 return result
+ 372
+ 373 def get_job ( self , job_id : str ) -> dict :
+ 374 return self . get ( f "/w/ { self . workspace } /jobs_u/get/ { job_id } " ) . json ()
+ 375
+ 376 def get_root_job_id ( self , job_id : str | None = None ) -> dict :
+ 377 job_id = job_id or os . environ . get ( "WM_JOB_ID" )
+ 378 return self . get ( f "/w/ { self . workspace } /jobs_u/get_root_job_id/ { job_id } " ) . json ()
+ 379
+ 380 def get_id_token ( self , audience : str ) -> str :
+ 381 return self . post ( f "/w/ { self . workspace } /oidc/token/ { audience } " ) . text
+ 382
+ 383 def get_job_status ( self , job_id : str ) -> JobStatus :
+ 384 job = self . get_job ( job_id )
+ 385 job_type = job . get ( "type" , "" )
+ 386 assert job_type , f " { job } is not a valid job"
+ 387 if job_type . lower () == "completedjob" :
+ 388 return "COMPLETED"
+ 389 if job . get ( "running" ):
+ 390 return "RUNNING"
+ 391 return "WAITING"
+ 392
+ 393 def get_result (
+ 394 self ,
+ 395 job_id : str ,
+ 396 assert_result_is_not_none : bool = True ,
+ 397 ) -> Any :
+ 398 result = self . get ( f "/w/ { self . workspace } /jobs_u/completed/get_result/ { job_id } " )
+ 399 result_text = result . text
+ 400 if assert_result_is_not_none and result_text is None :
+ 401 raise Exception ( f "result is None for { job_id = } " )
+ 402 try :
+ 403 return result . json ()
+ 404 except JSONDecodeError :
+ 405 return result_text
+ 406
+ 407 def get_variable ( self , path : str ) -> str :
+ 408 path = parse_variable_syntax ( path ) or path
+ 409 if self . mocked_api is not None :
+ 410 variables = self . mocked_api [ "variables" ]
+ 411 try :
+ 412 result = variables [ path ]
+ 413 return result
+ 414 except KeyError :
+ 415 logger . info (
+ 416 f "MockedAPI present, but variable not found at { path } , falling back to real API"
+ 417 )
+ 418
+ 419 """Get variable from Windmill"""
+ 420 return self . get ( f "/w/ { self . workspace } /variables/get_value/ { path } " ) . json ()
+ 421
+ 422 def set_variable ( self , path : str , value : str , is_secret : bool = False ) -> None :
+ 423 path = parse_variable_syntax ( path ) or path
+ 424 if self . mocked_api is not None :
+ 425 self . mocked_api [ "variables" ][ path ] = value
+ 426 return
+ 427
+ 428 """Set variable from Windmill"""
+ 429 # check if variable exists
+ 430 r = self . get (
+ 431 f "/w/ { self . workspace } /variables/get/ { path } " , raise_for_status = False
+ 432 )
+ 433 if r . status_code == 404 :
+ 434 # create variable
+ 435 self . post (
+ 436 f "/w/ { self . workspace } /variables/create" ,
+ 437 json = {
+ 438 "path" : path ,
+ 439 "value" : value ,
+ 440 "is_secret" : is_secret ,
+ 441 "description" : "" ,
+ 442 },
+ 443 )
+ 444 else :
+ 445 # update variable
+ 446 self . post (
+ 447 f "/w/ { self . workspace } /variables/update/ { path } " ,
+ 448 json = { "value" : value },
+ 449 )
+ 450
+ 451 def get_resource (
+ 452 self ,
+ 453 path : str ,
+ 454 none_if_undefined : bool = False ,
+ 455 ) -> dict | None :
+ 456 path = parse_resource_syntax ( path ) or path
+ 457 if self . mocked_api is not None :
+ 458 resources = self . mocked_api [ "resources" ]
+ 459 try :
+ 460 result = resources [ path ]
+ 461 return result
+ 462 except KeyError :
+ 463 # NOTE: should mocked_api respect `none_if_undefined`?
+ 464 if none_if_undefined :
+ 465 logger . info (
+ 466 f "resource not found at $ { path } , but none_if_undefined is True, so returning None"
+ 467 )
+ 468 return None
+ 469 logger . info (
+ 470 f "MockedAPI present, but resource not found at $ { path } , falling back to real API"
+ 471 )
+ 472
+ 473 """Get resource from Windmill"""
+ 474 try :
+ 475 return self . get (
+ 476 f "/w/ { self . workspace } /resources/get_value_interpolated/ { path } "
+ 477 ) . json ()
+ 478 except Exception as e :
+ 479 if none_if_undefined :
+ 480 return None
+ 481 logger . error ( e )
+ 482 raise e
+ 483
+ 484 def set_resource (
+ 485 self ,
+ 486 value : Any ,
+ 487 path : str ,
+ 488 resource_type : str ,
+ 489 ):
+ 490 path = parse_resource_syntax ( path ) or path
+ 491 if self . mocked_api is not None :
+ 492 self . mocked_api [ "resources" ][ path ] = value
+ 493 return
+ 494
+ 495 # check if resource exists
+ 496 r = self . get (
+ 497 f "/w/ { self . workspace } /resources/get/ { path } " , raise_for_status = False
+ 498 )
+ 499 if r . status_code == 404 :
+ 500 # create resource
+ 501 self . post (
+ 502 f "/w/ { self . workspace } /resources/create" ,
+ 503 json = {
+ 504 "path" : path ,
+ 505 "value" : value ,
+ 506 "resource_type" : resource_type ,
+ 507 },
+ 508 )
+ 509 else :
+ 510 # update resource
+ 511 self . post (
+ 512 f "/w/ { self . workspace } /resources/update_value/ { path } " ,
+ 513 json = { "value" : value },
+ 514 )
+ 515
+ 516 def set_state ( self , value : Any ):
+ 517 self . set_resource ( value , path = self . state_path , resource_type = "state" )
+ 518
+ 519 def set_progress ( self , value : int , job_id : Optional [ str ] = None ):
+ 520 workspace = get_workspace ()
+ 521 flow_id = os . environ . get ( "WM_FLOW_JOB_ID" )
+ 522 job_id = job_id or os . environ . get ( "WM_JOB_ID" )
+ 523
+ 524 if job_id != None :
+ 525 job = self . get_job ( job_id )
+ 526 flow_id = job . get ( "parent_job" )
+ 527
+ 528 self . post (
+ 529 f "/w/ { workspace } /job_metrics/set_progress/ { job_id } " ,
+ 530 json = {
+ 531 "percent" : value ,
+ 532 "flow_job_id" : flow_id or None ,
+ 533 },
+ 534 )
+ 535
+ 536 def get_progress ( self , job_id : Optional [ str ] = None ) -> Any :
+ 537 workspace = get_workspace ()
+ 538 job_id = job_id or os . environ . get ( "WM_JOB_ID" )
+ 539
+ 540 r = self . get (
+ 541 f "/w/ { workspace } /job_metrics/get_progress/ { job_id } " ,
+ 542 )
+ 543 if r . status_code == 404 :
+ 544 print ( f "Job { job_id } does not exist" )
+ 545 return None
+ 546 else :
+ 547 return r . json ()
+ 548
+ 549 def set_flow_user_state ( self , key : str , value : Any ) -> None :
+ 550 """Set the user state of a flow at a given key"""
+ 551 flow_id = self . get_root_job_id ()
+ 552 r = self . post (
+ 553 f "/w/ { self . workspace } /jobs/flow/user_states/ { flow_id } / { key } " ,
+ 554 json = value ,
+ 555 raise_for_status = False ,
+ 556 )
+ 557 if r . status_code == 404 :
+ 558 print ( f "Job { flow_id } does not exist or is not a flow" )
+ 559
+ 560 def get_flow_user_state ( self , key : str ) -> Any :
+ 561 """Get the user state of a flow at a given key"""
+ 562 flow_id = self . get_root_job_id ()
+ 563 r = self . get (
+ 564 f "/w/ { self . workspace } /jobs/flow/user_states/ { flow_id } / { key } " ,
+ 565 raise_for_status = False ,
+ 566 )
+ 567 if r . status_code == 404 :
+ 568 print ( f "Job { flow_id } does not exist or is not a flow" )
+ 569 return None
+ 570 else :
+ 571 return r . json ()
+ 572
+ 573 @property
+ 574 def version ( self ):
+ 575 return self . get ( "version" ) . text
+ 576
+ 577 def get_duckdb_connection_settings (
+ 578 self ,
+ 579 s3_resource_path : str = "" ,
+ 580 ) -> DuckDbConnectionSettings | None :
+ 581 """
+ 582 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+ 583 initiate an S3 connection from DuckDB
+ 584 """
+ 585 s3_resource_path = parse_resource_syntax ( s3_resource_path ) or s3_resource_path
+ 586 try :
+ 587 raw_obj = self . post (
+ 588 f "/w/ { self . workspace } /job_helpers/v2/duckdb_connection_settings" ,
+ 589 json = {}
+ 590 if s3_resource_path == ""
+ 591 else { "s3_resource_path" : s3_resource_path },
+ 592 ) . json ()
+ 593 return DuckDbConnectionSettings ( raw_obj )
+ 594 except JSONDecodeError as e :
+ 595 raise Exception (
+ 596 "Could not generate DuckDB S3 connection settings from the provided resource"
+ 597 ) from e
+ 598
+ 599 def get_polars_connection_settings (
+ 600 self ,
+ 601 s3_resource_path : str = "" ,
+ 602 ) -> PolarsConnectionSettings :
+ 603 """
+ 604 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+ 605 initiate an S3 connection from Polars
+ 606 """
+ 607 s3_resource_path = parse_resource_syntax ( s3_resource_path ) or s3_resource_path
+ 608 try :
+ 609 raw_obj = self . post (
+ 610 f "/w/ { self . workspace } /job_helpers/v2/polars_connection_settings" ,
+ 611 json = {}
+ 612 if s3_resource_path == ""
+ 613 else { "s3_resource_path" : s3_resource_path },
+ 614 ) . json ()
+ 615 return PolarsConnectionSettings ( raw_obj )
+ 616 except JSONDecodeError as e :
+ 617 raise Exception (
+ 618 "Could not generate Polars S3 connection settings from the provided resource"
+ 619 ) from e
+ 620
+ 621 def get_boto3_connection_settings (
+ 622 self ,
+ 623 s3_resource_path : str = "" ,
+ 624 ) -> Boto3ConnectionSettings :
+ 625 """
+ 626 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+ 627 initiate an S3 connection using boto3
+ 628 """
+ 629 s3_resource_path = parse_resource_syntax ( s3_resource_path ) or s3_resource_path
+ 630 try :
+ 631 s3_resource = self . post (
+ 632 f "/w/ { self . workspace } /job_helpers/v2/s3_resource_info" ,
+ 633 json = {}
+ 634 if s3_resource_path == ""
+ 635 else { "s3_resource_path" : s3_resource_path },
+ 636 ) . json ()
+ 637 return self . __boto3_connection_settings ( s3_resource )
+ 638 except JSONDecodeError as e :
+ 639 raise Exception (
+ 640 "Could not generate Boto3 S3 connection settings from the provided resource"
+ 641 ) from e
+ 642
+ 643 def load_s3_file ( self , s3object : S3Object | str , s3_resource_path : str | None ) -> bytes :
+ 644 """
+ 645 Load a file from the workspace s3 bucket and returns its content as bytes.
+ 646
+ 647 '''python
+ 648 from wmill import S3Object
+ 649
+ 650 s3_obj = S3Object(s3="/path/to/my_file.txt")
+ 651 my_obj_content = client.load_s3_file(s3_obj)
+ 652 file_content = my_obj_content.decode("utf-8")
+ 653 '''
+ 654 """
+ 655 s3object = parse_s3_object ( s3object )
+ 656 with self . load_s3_file_reader ( s3object , s3_resource_path ) as file_reader :
+ 657 return file_reader . read ()
+ 658
+ 659 def load_s3_file_reader (
+ 660 self , s3object : S3Object | str , s3_resource_path : str | None
+ 661 ) -> BufferedReader :
+ 662 """
+ 663 Load a file from the workspace s3 bucket and returns the bytes stream.
+ 664
+ 665 '''python
+ 666 from wmill import S3Object
+ 667
+ 668 s3_obj = S3Object(s3="/path/to/my_file.txt")
+ 669 with wmill.load_s3_file_reader(s3object, s3_resource_path) as file_reader:
+ 670 print(file_reader.read())
+ 671 '''
+ 672 """
+ 673 s3object = parse_s3_object ( s3object )
+ 674 reader = S3BufferedReader (
+ 675 f " { self . workspace } " ,
+ 676 self . client ,
+ 677 s3object [ "s3" ],
+ 678 s3_resource_path ,
+ 679 s3object [ "storage" ] if "storage" in s3object else None ,
+ 680 )
+ 681 return reader
+ 682
+ 683 def write_s3_file (
+ 684 self ,
+ 685 s3object : S3Object | str | None ,
+ 686 file_content : BufferedReader | bytes ,
+ 687 s3_resource_path : str | None ,
+ 688 content_type : str | None = None ,
+ 689 content_disposition : str | None = None ,
+ 690 ) -> S3Object :
+ 691 """
+ 692 Write a file to the workspace S3 bucket
+ 693
+ 694 '''python
+ 695 from wmill import S3Object
+ 696
+ 697 s3_obj = S3Object(s3="/path/to/my_file.txt")
+ 698
+ 699 # for an in memory bytes array:
+ 700 file_content = b'Hello Windmill!'
+ 701 client.write_s3_file(s3_obj, file_content)
+ 702
+ 703 # for a file:
+ 704 with open("my_file.txt", "rb") as my_file:
+ 705 client.write_s3_file(s3_obj, my_file)
+ 706 '''
+ 707 """
+ 708 s3object = parse_s3_object ( s3object )
+ 709 # httpx accepts either bytes or "a bytes generator" as content. If it's a BufferedReader, we need to convert it to a generator
+ 710 if isinstance ( file_content , BufferedReader ):
+ 711 content_payload = bytes_generator ( file_content )
+ 712 elif isinstance ( file_content , bytes ):
+ 713 content_payload = file_content
+ 714 else :
+ 715 raise Exception ( "Type of file_content not supported" )
+ 716
+ 717 query_params = {}
+ 718 if s3object is not None and s3object [ "s3" ] != "" :
+ 719 query_params [ "file_key" ] = s3object [ "s3" ]
+ 720 if s3_resource_path is not None and s3_resource_path != "" :
+ 721 query_params [ "s3_resource_path" ] = s3_resource_path
+ 722 if (
+ 723 s3object is not None
+ 724 and "storage" in s3object
+ 725 and s3object [ "storage" ] is not None
+ 726 ):
+ 727 query_params [ "storage" ] = s3object [ "storage" ]
+ 728 if content_type is not None :
+ 729 query_params [ "content_type" ] = content_type
+ 730 if content_disposition is not None :
+ 731 query_params [ "content_disposition" ] = content_disposition
+ 732
+ 733 try :
+ 734 # need a vanilla client b/c content-type is not application/json here
+ 735 response = httpx . post (
+ 736 f " { self . base_url } /w/ { self . workspace } /job_helpers/upload_s3_file" ,
+ 737 headers = {
+ 738 "Authorization" : f "Bearer { self . token } " ,
+ 739 "Content-Type" : "application/octet-stream" ,
+ 740 },
+ 741 params = query_params ,
+ 742 content = content_payload ,
+ 743 verify = self . verify ,
+ 744 timeout = None ,
+ 745 ) . json ()
+ 746 except Exception as e :
+ 747 raise Exception ( "Could not write file to S3" ) from e
+ 748 return S3Object ( s3 = response [ "file_key" ])
+ 749
+ 750 def sign_s3_objects ( self , s3_objects : list [ S3Object | str ]) -> list [ S3Object ]:
+ 751 return self . post (
+ 752 f "/w/ { self . workspace } /apps/sign_s3_objects" , json = { "s3_objects" : list ( map ( parse_s3_object , s3_objects ))}
+ 753 ) . json ()
+ 754
+ 755 def sign_s3_object ( self , s3_object : S3Object | str ) -> S3Object :
+ 756 return self . post (
+ 757 f "/w/ { self . workspace } /apps/sign_s3_objects" ,
+ 758 json = { "s3_objects" : [ s3_object ]},
+ 759 ) . json ()[ 0 ]
+ 760
+ 761 def __boto3_connection_settings ( self , s3_resource ) -> Boto3ConnectionSettings :
+ 762 endpoint_url_prefix = "https://" if s3_resource [ "useSSL" ] else "http://"
+ 763 return Boto3ConnectionSettings (
+ 764 {
+ 765 "endpoint_url" : " {}{} " . format (
+ 766 endpoint_url_prefix , s3_resource [ "endPoint" ]
+ 767 ),
+ 768 "region_name" : s3_resource [ "region" ],
+ 769 "use_ssl" : s3_resource [ "useSSL" ],
+ 770 "aws_access_key_id" : s3_resource [ "accessKey" ],
+ 771 "aws_secret_access_key" : s3_resource [ "secretKey" ],
+ 772 # no need for path_style here as boto3 is clever enough to determine which one to use
+ 773 }
+ 774 )
+ 775
+ 776 def whoami ( self ) -> dict :
+ 777 return self . get ( "/users/whoami" ) . json ()
+ 778
+ 779 @property
+ 780 def user ( self ) -> dict :
+ 781 return self . whoami ()
+ 782
+ 783 @property
+ 784 def state_path ( self ) -> str :
+ 785 state_path = os . environ . get (
+ 786 "WM_STATE_PATH_NEW" , os . environ . get ( "WM_STATE_PATH" )
+ 787 )
+ 788 if state_path is None :
+ 789 raise Exception ( "State path not found" )
+ 790 return state_path
+ 791
+ 792 @property
+ 793 def state ( self ) -> Any :
+ 794 return self . get_resource ( path = self . state_path , none_if_undefined = True )
+ 795
+ 796 @state . setter
+ 797 def state ( self , value : Any ) -> None :
+ 798 self . set_state ( value )
+ 799
+ 800 @staticmethod
+ 801 def set_shared_state_pickle ( value : Any , path : str = "state.pickle" ) -> None :
+ 802 """
+ 803 Set the state in the shared folder using pickle
+ 804 """
+ 805 import pickle
+ 806
+ 807 with open ( f "/shared/ { path } " , "wb" ) as handle :
+ 808 pickle . dump ( value , handle , protocol = pickle . HIGHEST_PROTOCOL )
+ 809
+ 810 @staticmethod
+ 811 def get_shared_state_pickle ( path : str = "state.pickle" ) -> Any :
+ 812 """
+ 813 Get the state in the shared folder using pickle
+ 814 """
+ 815 import pickle
+ 816
+ 817 with open ( f "/shared/ { path } " , "rb" ) as handle :
+ 818 return pickle . load ( handle )
+ 819
+ 820 @staticmethod
+ 821 def set_shared_state ( value : Any , path : str = "state.json" ) -> None :
+ 822 """
+ 823 Set the state in the shared folder using pickle
+ 824 """
+ 825 import json
+ 826
+ 827 with open ( f "/shared/ { path } " , "w" , encoding = "utf-8" ) as f :
+ 828 json . dump ( value , f , ensure_ascii = False , indent = 4 )
+ 829
+ 830 @staticmethod
+ 831 def get_shared_state ( path : str = "state.json" ) -> None :
+ 832 """
+ 833 Get the state in the shared folder using pickle
+ 834 """
+ 835 import json
+ 836
+ 837 with open ( f "/shared/ { path } " , "r" , encoding = "utf-8" ) as f :
+ 838 return json . load ( f )
+ 839
+ 840 def get_resume_urls ( self , approver : str = None ) -> dict :
+ 841 nonce = random . randint ( 0 , 1000000000 )
+ 842 job_id = os . environ . get ( "WM_JOB_ID" ) or "NO_ID"
+ 843 return self . get (
+ 844 f "/w/ { self . workspace } /jobs/resume_urls/ { job_id } / { nonce } " ,
+ 845 params = { "approver" : approver },
+ 846 ) . json ()
+ 847
+ 848 def request_interactive_slack_approval (
+ 849 self ,
+ 850 slack_resource_path : str ,
+ 851 channel_id : str ,
+ 852 message : str = None ,
+ 853 approver : str = None ,
+ 854 default_args_json : dict = None ,
+ 855 dynamic_enums_json : dict = None ,
+ 856 ) -> None :
+ 857 """
+ 858 Sends an interactive approval request via Slack, allowing optional customization of the message, approver, and form fields.
+ 859
+ 860 **[Enterprise Edition Only]** To include form fields in the Slack approval request, use the "Advanced -> Suspend -> Form" functionality.
+ 861 Learn more at: https://www.windmill.dev/docs/flows/flow_approval#form
+ 862
+ 863 :param slack_resource_path: The path to the Slack resource in Windmill.
+ 864 :type slack_resource_path: str
+ 865 :param channel_id: The Slack channel ID where the approval request will be sent.
+ 866 :type channel_id: str
+ 867 :param message: Optional custom message to include in the Slack approval request.
+ 868 :type message: str, optional
+ 869 :param approver: Optional user ID or name of the approver for the request.
+ 870 :type approver: str, optional
+ 871 :param default_args_json: Optional dictionary defining or overriding the default arguments for form fields.
+ 872 :type default_args_json: dict, optional
+ 873 :param dynamic_enums_json: Optional dictionary overriding the enum default values of enum form fields.
+ 874 :type dynamic_enums_json: dict, optional
+ 875
+ 876 :raises Exception: If the function is not called within a flow or flow preview.
+ 877 :raises Exception: If the required flow job or flow step environment variables are not set.
+ 878
+ 879 :return: None
+ 880
+ 881 **Usage Example:**
+ 882 >>> client.request_interactive_slack_approval(
+ 883 ... slack_resource_path="/u/alex/my_slack_resource",
+ 884 ... channel_id="admins-slack-channel",
+ 885 ... message="Please approve this request",
+ 886 ... approver="approver123",
+ 887 ... default_args_json={"key1": "value1", "key2": 42},
+ 888 ... dynamic_enums_json={"foo": ["choice1", "choice2"], "bar": ["optionA", "optionB"]},
+ 889 ... )
+ 890
+ 891 **Notes:**
+ 892 - This function must be executed within a Windmill flow or flow preview.
+ 893 - The function checks for required environment variables (`WM_FLOW_JOB_ID`, `WM_FLOW_STEP_ID`) to ensure it is run in the appropriate context.
+ 894 """
+ 895 workspace = self . workspace
+ 896 flow_job_id = os . environ . get ( "WM_FLOW_JOB_ID" )
+ 897
+ 898 if not flow_job_id :
+ 899 raise Exception (
+ 900 "You can't use 'request_interactive_slack_approval' function in a standalone script or flow step preview. Please use it in a flow or a flow preview."
+ 901 )
+ 902
+ 903 # Only include non-empty parameters
+ 904 params = {}
+ 905 if message :
+ 906 params [ "message" ] = message
+ 907 if approver :
+ 908 params [ "approver" ] = approver
+ 909 if slack_resource_path :
+ 910 params [ "slack_resource_path" ] = slack_resource_path
+ 911 if channel_id :
+ 912 params [ "channel_id" ] = channel_id
+ 913 if os . environ . get ( "WM_FLOW_STEP_ID" ):
+ 914 params [ "flow_step_id" ] = os . environ . get ( "WM_FLOW_STEP_ID" )
+ 915 if default_args_json :
+ 916 params [ "default_args_json" ] = json . dumps ( default_args_json )
+ 917 if dynamic_enums_json :
+ 918 params [ "dynamic_enums_json" ] = json . dumps ( dynamic_enums_json )
+ 919
+ 920 self . get (
+ 921 f "/w/ { workspace } /jobs/slack_approval/ { os . environ . get ( 'WM_JOB_ID' , 'NO_JOB_ID' ) } " ,
+ 922 params = params ,
+ 923 )
+ 924
+ 925 def username_to_email ( self , username : str ) -> str :
+ 926 """
+ 927 Get email from workspace username
+ 928 This method is particularly useful for apps that require the email address of the viewer.
+ 929 Indeed, in the viewer context WM_USERNAME is set to the username of the viewer but WM_EMAIL is set to the email of the creator of the app.
+ 930 """
+ 931 return self . get ( f "/w/ { self . workspace } /users/username_to_email/ { username } " ) . text
+ 932
+ 933 def send_teams_message (
+ 934 self ,
+ 935 conversation_id : str ,
+ 936 text : str ,
+ 937 success : bool = True ,
+ 938 card_block : dict = None ,
+ 939 ):
+ 940 """
+ 941 Send a message to a Microsoft Teams conversation with conversation_id, where success is used to style the message
+ 942 """
+ 943 return self . post (
+ 944 f "/teams/activities" ,
+ 945 json = {
+ 946 "conversation_id" : conversation_id ,
+ 947 "text" : text ,
+ 948 "success" : success ,
+ 949 "card_block" : card_block ,
+ 950 },
+ 951 )
+ 952
+ 953
+ 954 def init_global_client ( f ):
+ 955 @functools . wraps ( f )
+ 956 def wrapper ( * args , ** kwargs ):
+ 957 global _client
+ 958 if _client is None :
+ 959 _client = Windmill ()
+ 960 return f ( * args , ** kwargs )
+ 961
+ 962 return wrapper
+ 963
+ 964
+ 965 def deprecate ( in_favor_of : str ):
+ 966 def decorator ( f ):
+ 967 @functools . wraps ( f )
+ 968 def wrapper ( * args , ** kwargs ):
+ 969 warnings . warn (
+ 970 (
+ 971 f "The ' { f . __name__ } ' method is deprecated and may be removed in the future. "
+ 972 f "Consider { in_favor_of } "
+ 973 ),
+ 974 DeprecationWarning ,
+ 975 )
+ 976 return f ( * args , ** kwargs )
+ 977
+ 978 return wrapper
+ 979
+ 980 return decorator
+ 981
+ 982
+ 983 @init_global_client
+ 984 def get_workspace () -> str :
+ 985 return _client . workspace
+ 986
+ 987
+ 988 @init_global_client
+ 989 def get_root_job_id ( job_id : str | None = None ) -> str :
+ 990 return _client . get_root_job_id ( job_id )
+ 991
+ 992
+ 993 @init_global_client
+ 994 @deprecate ( "Windmill().version" )
+ 995 def get_version () -> str :
+ 996 return _client . version
+ 997
+ 998
+ 999 @init_global_client
+1000 def run_script_async (
+1001 hash_or_path : str ,
+1002 args : Dict [ str , Any ] = None ,
+1003 scheduled_in_secs : int = None ,
+1004 ) -> str :
+1005 is_path = "/" in hash_or_path
+1006 hash_ = None if is_path else hash_or_path
+1007 path = hash_or_path if is_path else None
+1008 return _client . run_script_async (
+1009 hash_ = hash_ ,
+1010 path = path ,
+1011 args = args ,
+1012 scheduled_in_secs = scheduled_in_secs ,
+1013 )
+1014
+1015
+1016 @init_global_client
+1017 def run_flow_async (
+1018 path : str ,
+1019 args : Dict [ str , Any ] = None ,
+1020 scheduled_in_secs : int = None ,
+1021 # can only be set to false if this the job will be fully await and not concurrent with any other job
+1022 # as otherwise the child flow and its own child will store their state in the parent job which will
+1023 # lead to incorrectness and failures
+1024 do_not_track_in_parent : bool = True ,
+1025 ) -> str :
+1026 return _client . run_flow_async (
+1027 path = path ,
+1028 args = args ,
+1029 scheduled_in_secs = scheduled_in_secs ,
+1030 do_not_track_in_parent = do_not_track_in_parent ,
+1031 )
+1032
+1033
+1034 @init_global_client
+1035 def run_script_sync (
+1036 hash : str ,
+1037 args : Dict [ str , Any ] = None ,
+1038 verbose : bool = False ,
+1039 assert_result_is_not_none : bool = True ,
+1040 cleanup : bool = True ,
+1041 timeout : dt . timedelta = None ,
+1042 ) -> Any :
+1043 return _client . run_script (
+1044 hash_ = hash ,
+1045 args = args ,
+1046 verbose = verbose ,
+1047 assert_result_is_not_none = assert_result_is_not_none ,
+1048 cleanup = cleanup ,
+1049 timeout = timeout ,
+1050 )
+1051
+1052
+1053 @init_global_client
+1054 def run_script_by_path_async (
+1055 path : str ,
+1056 args : Dict [ str , Any ] = None ,
+1057 scheduled_in_secs : Union [ None , int ] = None ,
+1058 ) -> str :
+1059 return _client . run_script_by_path_async (
+1060 path = path ,
+1061 args = args ,
+1062 scheduled_in_secs = scheduled_in_secs ,
+1063 )
+1064
+1065
+1066 @init_global_client
+1067 def run_script_by_hash_async (
+1068 hash_ : str ,
+1069 args : Dict [ str , Any ] = None ,
+1070 scheduled_in_secs : Union [ None , int ] = None ,
+1071 ) -> str :
+1072 return _client . run_script_by_hash_async (
+1073 hash_ = hash_ ,
+1074 args = args ,
+1075 scheduled_in_secs = scheduled_in_secs ,
+1076 )
+1077
+1078
+1079 @init_global_client
+1080 def run_script_by_path_sync (
+1081 path : str ,
+1082 args : Dict [ str , Any ] = None ,
+1083 verbose : bool = False ,
+1084 assert_result_is_not_none : bool = True ,
+1085 cleanup : bool = True ,
+1086 timeout : dt . timedelta = None ,
+1087 ) -> Any :
+1088 return _client . run_script (
+1089 path = path ,
+1090 args = args ,
+1091 verbose = verbose ,
+1092 assert_result_is_not_none = assert_result_is_not_none ,
+1093 cleanup = cleanup ,
+1094 timeout = timeout ,
+1095 )
+1096
+1097
+1098 @init_global_client
+1099 def get_id_token ( audience : str ) -> str :
+1100 """
+1101 Get a JWT token for the given audience for OIDC purposes to login into third parties like AWS, Vault, GCP, etc.
+1102 """
+1103 return _client . get_id_token ( audience )
+1104
+1105
+1106 @init_global_client
+1107 def get_job_status ( job_id : str ) -> JobStatus :
+1108 return _client . get_job_status ( job_id )
+1109
+1110
+1111 @init_global_client
+1112 def get_result ( job_id : str , assert_result_is_not_none = True ) -> Dict [ str , Any ]:
+1113 return _client . get_result (
+1114 job_id = job_id , assert_result_is_not_none = assert_result_is_not_none
+1115 )
+1116
+1117
+1118 @init_global_client
+1119 def duckdb_connection_settings ( s3_resource_path : str = "" ) -> DuckDbConnectionSettings :
+1120 """
+1121 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+1122 initiate an S3 connection from DuckDB
+1123 """
+1124 return _client . get_duckdb_connection_settings ( s3_resource_path )
+1125
+1126
+1127 @init_global_client
+1128 def polars_connection_settings ( s3_resource_path : str = "" ) -> PolarsConnectionSettings :
+1129 """
+1130 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+1131 initiate an S3 connection from Polars
+1132 """
+1133 return _client . get_polars_connection_settings ( s3_resource_path )
+1134
+1135
+1136 @init_global_client
+1137 def boto3_connection_settings ( s3_resource_path : str = "" ) -> Boto3ConnectionSettings :
+1138 """
+1139 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+1140 initiate an S3 connection using boto3
+1141 """
+1142 return _client . get_boto3_connection_settings ( s3_resource_path )
+1143
+1144
+1145 @init_global_client
+1146 def load_s3_file ( s3object : S3Object | str , s3_resource_path : str | None = None ) -> bytes :
+1147 """
+1148 Load the entire content of a file stored in S3 as bytes
+1149 """
+1150 return _client . load_s3_file (
+1151 s3object , s3_resource_path if s3_resource_path != "" else None
+1152 )
+1153
+1154
+1155 @init_global_client
+1156 def load_s3_file_reader (
+1157 s3object : S3Object | str , s3_resource_path : str | None = None
+1158 ) -> BufferedReader :
+1159 """
+1160 Load the content of a file stored in S3
+1161 """
+1162 return _client . load_s3_file_reader (
+1163 s3object , s3_resource_path if s3_resource_path != "" else None
+1164 )
+1165
+1166
+1167 @init_global_client
+1168 def write_s3_file (
+1169 s3object : S3Object | str | None ,
+1170 file_content : BufferedReader | bytes ,
+1171 s3_resource_path : str | None = None ,
+1172 content_type : str | None = None ,
+1173 content_disposition : str | None = None ,
+1174 ) -> S3Object :
+1175 """
+1176 Upload a file to S3
+1177
+1178 Content type will be automatically guessed from path extension if left empty
+1179
+1180 See MDN for content_disposition: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
+1181 and content_type: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
+1182
+1183 """
+1184 return _client . write_s3_file (
+1185 s3object ,
+1186 file_content ,
+1187 s3_resource_path if s3_resource_path != "" else None ,
+1188 content_type ,
+1189 content_disposition ,
+1190 )
+1191
+1192
+1193 @init_global_client
+1194 def sign_s3_objects ( s3_objects : list [ S3Object | str ]) -> list [ S3Object ]:
+1195 """
+1196 Sign S3 objects to be used by anonymous users in public apps
+1197 Returns a list of signed s3 tokens
+1198 """
+1199 return _client . sign_s3_objects ( s3_objects )
+1200
+1201
+1202 @init_global_client
+1203 def sign_s3_object ( s3_object : S3Object | str ) -> S3Object :
+1204 """
+1205 Sign S3 object to be used by anonymous users in public apps
+1206 Returns a signed s3 object
+1207 """
+1208 return _client . sign_s3_object ( s3_object )
+1209
+1210
+1211 @init_global_client
+1212 def whoami () -> dict :
+1213 """
+1214 Returns the current user
+1215 """
+1216 return _client . user
+1217
+1218
+1219 @init_global_client
+1220 @deprecate ( "Windmill().state" )
+1221 def get_state () -> Any :
+1222 """
+1223 Get the state
+1224 """
+1225 return _client . state
+1226
+1227
+1228 @init_global_client
+1229 def get_resource (
+1230 path : str ,
+1231 none_if_undefined : bool = False ,
+1232 ) -> dict | None :
+1233 """Get resource from Windmill"""
+1234 return _client . get_resource ( path , none_if_undefined )
+1235
+1236
+1237 @init_global_client
+1238 def set_resource ( path : str , value : Any , resource_type : str = "any" ) -> None :
+1239 """
+1240 Set the resource at a given path as a string, creating it if it does not exist
+1241 """
+1242 return _client . set_resource ( value = value , path = path , resource_type = resource_type )
+1243
+1244
+1245 @init_global_client
+1246 def set_state ( value : Any ) -> None :
+1247 """
+1248 Set the state
+1249 """
+1250 return _client . set_state ( value )
+1251
+1252
+1253 @init_global_client
+1254 def set_progress ( value : int , job_id : Optional [ str ] = None ) -> None :
+1255 """
+1256 Set the progress
+1257 """
+1258 return _client . set_progress ( value , job_id )
+1259
+1260
+1261 @init_global_client
+1262 def get_progress ( job_id : Optional [ str ] = None ) -> Any :
+1263 """
+1264 Get the progress
+1265 """
+1266 return _client . get_progress ( job_id )
+1267
+1268
+1269 def set_shared_state_pickle ( value : Any , path = "state.pickle" ) -> None :
+1270 """
+1271 Set the state in the shared folder using pickle
+1272 """
+1273 return Windmill . set_shared_state_pickle ( value = value , path = path )
+1274
+1275
+1276 @deprecate ( "Windmill.get_shared_state_pickle(...)" )
+1277 def get_shared_state_pickle ( path = "state.pickle" ) -> Any :
+1278 """
+1279 Get the state in the shared folder using pickle
+1280 """
+1281 return Windmill . get_shared_state_pickle ( path = path )
+1282
+1283
+1284 def set_shared_state ( value : Any , path = "state.json" ) -> None :
+1285 """
+1286 Set the state in the shared folder using pickle
+1287 """
+1288 return Windmill . set_shared_state ( value = value , path = path )
+1289
+1290
+1291 def get_shared_state ( path = "state.json" ) -> None :
+1292 """
+1293 Get the state in the shared folder using pickle
+1294 """
+1295 return Windmill . get_shared_state ( path = path )
+1296
+1297
+1298 @init_global_client
+1299 def get_variable ( path : str ) -> str :
+1300 """
+1301 Returns the variable at a given path as a string
+1302 """
+1303 return _client . get_variable ( path )
+1304
+1305
+1306 @init_global_client
+1307 def set_variable ( path : str , value : str , is_secret : bool = False ) -> None :
+1308 """
+1309 Set the variable at a given path as a string, creating it if it does not exist
+1310 """
+1311 return _client . set_variable ( path , value , is_secret )
+1312
+1313
+1314 @init_global_client
+1315 def get_flow_user_state ( key : str ) -> Any :
+1316 """
+1317 Get the user state of a flow at a given key
+1318 """
+1319 return _client . get_flow_user_state ( key )
+1320
+1321
+1322 @init_global_client
+1323 def set_flow_user_state ( key : str , value : Any ) -> None :
+1324 """
+1325 Set the user state of a flow at a given key
+1326 """
+1327 return _client . set_flow_user_state ( key , value )
+1328
+1329
+1330 @init_global_client
+1331 def get_state_path () -> str :
+1332 return _client . state_path
+1333
+1334
+1335 @init_global_client
+1336 def get_resume_urls ( approver : str = None ) -> dict :
+1337 return _client . get_resume_urls ( approver )
+1338
+1339
+1340 @init_global_client
+1341 def request_interactive_slack_approval (
+1342 slack_resource_path : str ,
+1343 channel_id : str ,
+1344 message : str = None ,
+1345 approver : str = None ,
+1346 default_args_json : dict = None ,
+1347 dynamic_enums_json : dict = None ,
+1348 ) -> None :
+1349 return _client . request_interactive_slack_approval (
+1350 slack_resource_path = slack_resource_path ,
+1351 channel_id = channel_id ,
+1352 message = message ,
+1353 approver = approver ,
+1354 default_args_json = default_args_json ,
+1355 dynamic_enums_json = dynamic_enums_json ,
+1356 )
+1357
+1358
+1359 @init_global_client
+1360 def send_teams_message (
+1361 conversation_id : str , text : str , success : bool , card_block : dict = None
+1362 ):
+1363 return _client . send_teams_message ( conversation_id , text , success , card_block )
+1364
+1365
+1366 @init_global_client
+1367 def cancel_running () -> dict :
+1368 """Cancel currently running executions of the same script."""
+1369 return _client . cancel_running ()
+1370
+1371
+1372 @init_global_client
+1373 def run_script (
+1374 path : str = None ,
+1375 hash_ : str = None ,
+1376 args : dict = None ,
+1377 timeout : dt . timedelta | int | float = None ,
+1378 verbose : bool = False ,
+1379 cleanup : bool = True ,
+1380 assert_result_is_not_none : bool = True ,
+1381 ) -> Any :
+1382 """Run script synchronously and return its result.
+1383
+1384 .. deprecated:: Use run_script_by_path or run_script_by_hash instead.
+1385 """
+1386 return _client . run_script (
+1387 path = path ,
+1388 hash_ = hash_ ,
+1389 args = args ,
+1390 verbose = verbose ,
+1391 assert_result_is_not_none = assert_result_is_not_none ,
+1392 cleanup = cleanup ,
+1393 timeout = timeout ,
+1394 )
+1395
+1396
+1397 @init_global_client
+1398 def run_script_by_path (
+1399 path : str ,
+1400 args : dict = None ,
+1401 timeout : dt . timedelta | int | float = None ,
+1402 verbose : bool = False ,
+1403 cleanup : bool = True ,
+1404 assert_result_is_not_none : bool = True ,
+1405 ) -> Any :
+1406 """Run script by path synchronously and return its result."""
+1407 return _client . run_script_by_path (
+1408 path = path ,
+1409 args = args ,
+1410 verbose = verbose ,
+1411 assert_result_is_not_none = assert_result_is_not_none ,
+1412 cleanup = cleanup ,
+1413 timeout = timeout ,
+1414 )
+1415
+1416
+1417 @init_global_client
+1418 def run_script_by_hash (
+1419 hash_ : str ,
+1420 args : dict = None ,
+1421 timeout : dt . timedelta | int | float = None ,
+1422 verbose : bool = False ,
+1423 cleanup : bool = True ,
+1424 assert_result_is_not_none : bool = True ,
+1425 ) -> Any :
+1426 """Run script by hash synchronously and return its result."""
+1427 return _client . run_script_by_hash (
+1428 hash_ = hash_ ,
+1429 args = args ,
+1430 verbose = verbose ,
+1431 assert_result_is_not_none = assert_result_is_not_none ,
+1432 cleanup = cleanup ,
+1433 timeout = timeout ,
+1434 )
+1435
+1436
+1437 @init_global_client
+1438 def username_to_email ( username : str ) -> str :
+1439 """
+1440 Get email from workspace username
+1441 This method is particularly useful for apps that require the email address of the viewer.
+1442 Indeed, in the viewer context WM_USERNAME is set to the username of the viewer but WM_EMAIL is set to the email of the creator of the app.
+1443 """
+1444 return _client . username_to_email ( username )
+1445
+1446
+1447 def task ( * args , ** kwargs ):
+1448 from inspect import signature
+1449
+1450 def f ( func , tag : str | None = None ):
+1451 if (
+1452 os . environ . get ( "WM_JOB_ID" ) is None
+1453 or os . environ . get ( "MAIN_OVERRIDE" ) == func . __name__
+1454 ):
+1455
+1456 def inner ( * args , ** kwargs ):
+1457 return func ( * args , ** kwargs )
+1458
+1459 return inner
+1460 else :
+1461
+1462 def inner ( * args , ** kwargs ):
+1463 global _client
+1464 if _client is None :
+1465 _client = Windmill ()
+1466 w_id = os . environ . get ( "WM_WORKSPACE" )
+1467 job_id = os . environ . get ( "WM_JOB_ID" )
+1468 f_name = func . __name__
+1469 json = kwargs
+1470 params = list ( signature ( func ) . parameters )
+1471 for i , arg in enumerate ( args ):
+1472 if i < len ( params ):
+1473 p = params [ i ]
+1474 key = p
+1475 if key not in kwargs :
+1476 json [ key ] = arg
+1477
+1478 params = {}
+1479 if tag is not None :
+1480 params [ "tag" ] = tag
+1481 w_as_code_response = _client . post (
+1482 f "/w/ { w_id } /jobs/run/workflow_as_code/ { job_id } / { f_name } " ,
+1483 json = { "args" : json },
+1484 params = params ,
+1485 )
+1486 job_id = w_as_code_response . text
+1487 print ( f "Executing task { func . __name__ } on job { job_id } " )
+1488 job_result = _client . wait_job ( job_id )
+1489 print ( f "Task { func . __name__ } ( { job_id } ) completed" )
+1490 return job_result
+1491
+1492 return inner
+1493
+1494 if len ( args ) == 1 and len ( kwargs ) == 0 and callable ( args [ 0 ]):
+1495 return f ( args [ 0 ], None )
+1496 else :
+1497 return lambda x : f ( x , kwargs . get ( "tag" ))
+1498
+1499 def parse_resource_syntax ( s : str ) -> Optional [ str ]:
+1500 """Parse resource syntax from string."""
+1501 if s is None :
+1502 return None
+1503 if s . startswith ( "$res:" ):
+1504 return s [ 5 :]
+1505 if s . startswith ( "res://" ):
+1506 return s [ 6 :]
+1507 return None
+1508
+1509 def parse_s3_object ( s3_object : S3Object | str ) -> S3Object :
+1510 """Parse S3 object from string or S3Object format."""
+1511 if isinstance ( s3_object , str ):
+1512 match = re . match ( r '^s3://([^/]*)/(.*)$' , s3_object )
+1513 if match :
+1514 return S3Object ( s3 = match . group ( 2 ) or "" , storage = match . group ( 1 ) or None )
+1515 return S3Object ( s3 = "" )
+1516 else :
+1517 return s3_object
+1518
+1519
+1520
+1521 def parse_variable_syntax ( s : str ) -> Optional [ str ]:
+1522 """Parse variable syntax from string."""
+1523 if s . startswith ( "var://" ):
+1524 return s [ 6 :]
+1525 return None
+1526
+1527
+1528 def append_to_result_stream ( text : str ) -> None :
+1529 """Append a text to the result stream.
+1530
+1531 Args:
+1532 text: text to append to the result stream
+1533 """
+1534 print ( "WM_STREAM: {} " . format ( text . replace ( chr ( 10 ), ' \\ n' )))
+1535
+1536 def stream_result ( stream ) -> None :
+1537 """Stream to the result stream.
+1538
+1539 Args:
+1540 stream: stream to stream to the result stream
+1541 """
+1542 for text in stream :
+1543 append_to_result_stream ( text )
+
+
+
+
+
+
+ logger =
+<Logger windmill_client (WARNING)>
+
+
+
+
+
+
+
+
+
+
+ JobStatus =
+typing.Literal['RUNNING', 'WAITING', 'COMPLETED']
+
+
+
+
+
+
+
+
+
+
+
+
+ class
+ Windmill :
+
+ View Source
+
+
+
+ 35 class Windmill :
+ 36 def __init__ ( self , base_url = None , token = None , workspace = None , verify = True ):
+ 37 base = (
+ 38 base_url
+ 39 or os . environ . get ( "BASE_INTERNAL_URL" )
+ 40 or os . environ . get ( "WM_BASE_URL" )
+ 41 )
+ 42
+ 43 self . base_url = f " { base } /api"
+ 44 self . token = token or os . environ . get ( "WM_TOKEN" )
+ 45 self . headers = {
+ 46 "Content-Type" : "application/json" ,
+ 47 "Authorization" : f "Bearer { self . token } " ,
+ 48 }
+ 49 self . verify = verify
+ 50 self . client = self . get_client ()
+ 51 self . workspace = workspace or os . environ . get ( "WM_WORKSPACE" )
+ 52 self . path = os . environ . get ( "WM_JOB_PATH" )
+ 53
+ 54 self . mocked_api = self . get_mocked_api ()
+ 55
+ 56 assert self . workspace , (
+ 57 f "workspace required as an argument or as WM_WORKSPACE environment variable"
+ 58 )
+ 59
+ 60 def get_mocked_api ( self ) -> Optional [ dict ]:
+ 61 mocked_path = os . environ . get ( "WM_MOCKED_API_FILE" )
+ 62 if not mocked_path :
+ 63 return None
+ 64 logger . info ( "Using mocked API from %s " , mocked_path )
+ 65 mocked_api = { "variables" : {}, "resources" : {}}
+ 66 try :
+ 67 with open ( mocked_path , "r" ) as f :
+ 68 incoming_mocked_api = json . load ( f )
+ 69 mocked_api = { ** mocked_api , ** incoming_mocked_api }
+ 70 except Exception as e :
+ 71 logger . warning (
+ 72 "Error parsing mocked API file at path %s Using empty mocked API." ,
+ 73 mocked_path ,
+ 74 )
+ 75 logger . debug ( e )
+ 76 return mocked_api
+ 77
+ 78 def get_client ( self ) -> httpx . Client :
+ 79 return httpx . Client (
+ 80 base_url = self . base_url ,
+ 81 headers = self . headers ,
+ 82 verify = self . verify ,
+ 83 )
+ 84
+ 85 def get ( self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response :
+ 86 endpoint = endpoint . lstrip ( "/" )
+ 87 resp = self . client . get ( f "/ { endpoint } " , ** kwargs )
+ 88 if raise_for_status :
+ 89 try :
+ 90 resp . raise_for_status ()
+ 91 except httpx . HTTPStatusError as err :
+ 92 error = f " { err . request . url } : { err . response . status_code } , { err . response . text } "
+ 93 logger . error ( error )
+ 94 raise Exception ( error )
+ 95 return resp
+ 96
+ 97 def post ( self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response :
+ 98 endpoint = endpoint . lstrip ( "/" )
+ 99 resp = self . client . post ( f "/ { endpoint } " , ** kwargs )
+100 if raise_for_status :
+101 try :
+102 resp . raise_for_status ()
+103 except httpx . HTTPStatusError as err :
+104 error = f " { err . request . url } : { err . response . status_code } , { err . response . text } "
+105 logger . error ( error )
+106 raise Exception ( error )
+107 return resp
+108
+109 def create_token ( self , duration = dt . timedelta ( days = 1 )) -> str :
+110 endpoint = "/users/tokens/create"
+111 payload = {
+112 "label" : f "refresh { time . time () } " ,
+113 "expiration" : ( dt . datetime . now () + duration ) . strftime ( "%Y-%m- %d T%H:%M:%SZ" ),
+114 }
+115 return self . post ( endpoint , json = payload ) . text
+116
+117 def run_script_async (
+118 self ,
+119 path : str = None ,
+120 hash_ : str = None ,
+121 args : dict = None ,
+122 scheduled_in_secs : int = None ,
+123 ) -> str :
+124 """Create a script job and return its job id.
+125
+126 .. deprecated:: Use run_script_by_path_async or run_script_by_hash_async instead.
+127 """
+128 logging . warning (
+129 "run_script_async is deprecated. Use run_script_by_path_async or run_script_by_hash_async instead." ,
+130 )
+131 assert not ( path and hash_ ), "path and hash_ are mutually exclusive"
+132 return self . _run_script_async_internal ( path = path , hash_ = hash_ , args = args , scheduled_in_secs = scheduled_in_secs )
+133
+134 def _run_script_async_internal (
+135 self ,
+136 path : str = None ,
+137 hash_ : str = None ,
+138 args : dict = None ,
+139 scheduled_in_secs : int = None ,
+140 ) -> str :
+141 """Internal helper for running scripts asynchronously."""
+142 args = args or {}
+143 params = { "scheduled_in_secs" : scheduled_in_secs } if scheduled_in_secs else {}
+144 if os . environ . get ( "WM_JOB_ID" ):
+145 params [ "parent_job" ] = os . environ . get ( "WM_JOB_ID" )
+146 if os . environ . get ( "WM_ROOT_FLOW_JOB_ID" ):
+147 params [ "root_job" ] = os . environ . get ( "WM_ROOT_FLOW_JOB_ID" )
+148
+149 if path :
+150 endpoint = f "/w/ { self . workspace } /jobs/run/p/ { path } "
+151 elif hash_ :
+152 endpoint = f "/w/ { self . workspace } /jobs/run/h/ { hash_ } "
+153 else :
+154 raise Exception ( "path or hash_ must be provided" )
+155
+156 return self . post ( endpoint , json = args , params = params ) . text
+157
+158 def run_script_by_path_async (
+159 self ,
+160 path : str ,
+161 args : dict = None ,
+162 scheduled_in_secs : int = None ,
+163 ) -> str :
+164 """Create a script job by path and return its job id."""
+165 return self . _run_script_async_internal ( path = path , args = args , scheduled_in_secs = scheduled_in_secs )
+166
+167 def run_script_by_hash_async (
+168 self ,
+169 hash_ : str ,
+170 args : dict = None ,
+171 scheduled_in_secs : int = None ,
+172 ) -> str :
+173 """Create a script job by hash and return its job id."""
+174 return self . _run_script_async_internal ( hash_ = hash_ , args = args , scheduled_in_secs = scheduled_in_secs )
+175
+176 def run_flow_async (
+177 self ,
+178 path : str ,
+179 args : dict = None ,
+180 scheduled_in_secs : int = None ,
+181 # can only be set to false if this the job will be fully await and not concurrent with any other job
+182 # as otherwise the child flow and its own child will store their state in the parent job which will
+183 # lead to incorrectness and failures
+184 do_not_track_in_parent : bool = True ,
+185 ) -> str :
+186 """Create a flow job and return its job id."""
+187 args = args or {}
+188 params = { "scheduled_in_secs" : scheduled_in_secs } if scheduled_in_secs else {}
+189 if not do_not_track_in_parent :
+190 if os . environ . get ( "WM_JOB_ID" ):
+191 params [ "parent_job" ] = os . environ . get ( "WM_JOB_ID" )
+192 if os . environ . get ( "WM_ROOT_FLOW_JOB_ID" ):
+193 params [ "root_job" ] = os . environ . get ( "WM_ROOT_FLOW_JOB_ID" )
+194 if path :
+195 endpoint = f "/w/ { self . workspace } /jobs/run/f/ { path } "
+196 else :
+197 raise Exception ( "path must be provided" )
+198 return self . post ( endpoint , json = args , params = params ) . text
+199
+200 def run_script (
+201 self ,
+202 path : str = None ,
+203 hash_ : str = None ,
+204 args : dict = None ,
+205 timeout : dt . timedelta | int | float | None = None ,
+206 verbose : bool = False ,
+207 cleanup : bool = True ,
+208 assert_result_is_not_none : bool = False ,
+209 ) -> Any :
+210 """Run script synchronously and return its result.
+211
+212 .. deprecated:: Use run_script_by_path or run_script_by_hash instead.
+213 """
+214 logging . warning (
+215 "run_script is deprecated. Use run_script_by_path or run_script_by_hash instead." ,
+216 )
+217 assert not ( path and hash_ ), "path and hash_ are mutually exclusive"
+218 return self . _run_script_internal (
+219 path = path , hash_ = hash_ , args = args , timeout = timeout , verbose = verbose ,
+220 cleanup = cleanup , assert_result_is_not_none = assert_result_is_not_none
+221 )
+222
+223 def _run_script_internal (
+224 self ,
+225 path : str = None ,
+226 hash_ : str = None ,
+227 args : dict = None ,
+228 timeout : dt . timedelta | int | float | None = None ,
+229 verbose : bool = False ,
+230 cleanup : bool = True ,
+231 assert_result_is_not_none : bool = False ,
+232 ) -> Any :
+233 """Internal helper for running scripts synchronously."""
+234 args = args or {}
+235
+236 if verbose :
+237 if path :
+238 logger . info ( f "running ` { path } ` synchronously with { args = } " )
+239 elif hash_ :
+240 logger . info ( f "running script with hash ` { hash_ } ` synchronously with { args = } " )
+241
+242 if isinstance ( timeout , dt . timedelta ):
+243 timeout = timeout . total_seconds ()
+244
+245 job_id = self . _run_script_async_internal ( path = path , hash_ = hash_ , args = args )
+246 return self . wait_job (
+247 job_id , timeout , verbose , cleanup , assert_result_is_not_none
+248 )
+249
+250 def run_script_by_path (
+251 self ,
+252 path : str ,
+253 args : dict = None ,
+254 timeout : dt . timedelta | int | float | None = None ,
+255 verbose : bool = False ,
+256 cleanup : bool = True ,
+257 assert_result_is_not_none : bool = False ,
+258 ) -> Any :
+259 """Run script by path synchronously and return its result."""
+260 return self . _run_script_internal (
+261 path = path , args = args , timeout = timeout , verbose = verbose ,
+262 cleanup = cleanup , assert_result_is_not_none = assert_result_is_not_none
+263 )
+264
+265 def run_script_by_hash (
+266 self ,
+267 hash_ : str ,
+268 args : dict = None ,
+269 timeout : dt . timedelta | int | float | None = None ,
+270 verbose : bool = False ,
+271 cleanup : bool = True ,
+272 assert_result_is_not_none : bool = False ,
+273 ) -> Any :
+274 """Run script by hash synchronously and return its result."""
+275 return self . _run_script_internal (
+276 hash_ = hash_ , args = args , timeout = timeout , verbose = verbose ,
+277 cleanup = cleanup , assert_result_is_not_none = assert_result_is_not_none
+278 )
+279
+280 def wait_job (
+281 self ,
+282 job_id ,
+283 timeout : dt . timedelta | int | float | None = None ,
+284 verbose : bool = False ,
+285 cleanup : bool = True ,
+286 assert_result_is_not_none : bool = False ,
+287 ):
+288 def cancel_job ():
+289 logger . warning ( f "cancelling job: { job_id } " )
+290 self . post (
+291 f "/w/ { self . workspace } /jobs_u/queue/cancel/ { job_id } " ,
+292 json = { "reason" : "parent script cancelled" },
+293 ) . raise_for_status ()
+294
+295 if cleanup :
+296 atexit . register ( cancel_job )
+297
+298 start_time = time . time ()
+299
+300 if isinstance ( timeout , dt . timedelta ):
+301 timeout = timeout . total_seconds ()
+302
+303 while True :
+304 result_res = self . get (
+305 f "/w/ { self . workspace } /jobs_u/completed/get_result_maybe/ { job_id } " , True
+306 ) . json ()
+307
+308 started = result_res [ "started" ]
+309 completed = result_res [ "completed" ]
+310 success = result_res [ "success" ]
+311
+312 if not started and verbose :
+313 logger . info ( f "job { job_id } has not started yet" )
+314
+315 if cleanup and completed :
+316 atexit . unregister ( cancel_job )
+317
+318 if completed :
+319 result = result_res [ "result" ]
+320 if success :
+321 if result is None and assert_result_is_not_none :
+322 raise Exception ( "Result was none" )
+323 return result
+324 else :
+325 error = result [ "error" ]
+326 raise Exception ( f "Job { job_id } was not successful: { str ( error ) } " )
+327
+328 if timeout and (( time . time () - start_time ) > timeout ):
+329 msg = "reached timeout"
+330 logger . warning ( msg )
+331 self . post (
+332 f "/w/ { self . workspace } /jobs_u/queue/cancel/ { job_id } " ,
+333 json = { "reason" : msg },
+334 )
+335 raise TimeoutError ( msg )
+336 if verbose :
+337 logger . info ( f "sleeping 0.5 seconds for { job_id = } " )
+338
+339 time . sleep ( 0.5 )
+340
+341 def cancel_running ( self ) -> dict :
+342 """Cancel currently running executions of the same script."""
+343 logger . info ( "canceling running executions of this script" )
+344
+345 jobs = self . get (
+346 f "/w/ { self . workspace } /jobs/list" ,
+347 params = {
+348 "running" : "true" ,
+349 "script_path_exact" : self . path ,
+350 },
+351 ) . json ()
+352
+353 current_job_id = os . environ . get ( "WM_JOB_ID" )
+354
+355 logger . debug ( f " { current_job_id = } " )
+356
+357 job_ids = [ j [ "id" ] for j in jobs if j [ "id" ] != current_job_id ]
+358
+359 if job_ids :
+360 logger . info ( f "cancelling the following job ids: { job_ids } " )
+361 else :
+362 logger . info ( "no previous executions to cancel" )
+363
+364 result = {}
+365
+366 for id_ in job_ids :
+367 result [ id_ ] = self . post (
+368 f "/w/ { self . workspace } /jobs_u/queue/cancel/ { id_ } " ,
+369 json = { "reason" : "killed by `cancel_running` method" },
+370 )
+371
+372 return result
+373
+374 def get_job ( self , job_id : str ) -> dict :
+375 return self . get ( f "/w/ { self . workspace } /jobs_u/get/ { job_id } " ) . json ()
+376
+377 def get_root_job_id ( self , job_id : str | None = None ) -> dict :
+378 job_id = job_id or os . environ . get ( "WM_JOB_ID" )
+379 return self . get ( f "/w/ { self . workspace } /jobs_u/get_root_job_id/ { job_id } " ) . json ()
+380
+381 def get_id_token ( self , audience : str ) -> str :
+382 return self . post ( f "/w/ { self . workspace } /oidc/token/ { audience } " ) . text
+383
+384 def get_job_status ( self , job_id : str ) -> JobStatus :
+385 job = self . get_job ( job_id )
+386 job_type = job . get ( "type" , "" )
+387 assert job_type , f " { job } is not a valid job"
+388 if job_type . lower () == "completedjob" :
+389 return "COMPLETED"
+390 if job . get ( "running" ):
+391 return "RUNNING"
+392 return "WAITING"
+393
+394 def get_result (
+395 self ,
+396 job_id : str ,
+397 assert_result_is_not_none : bool = True ,
+398 ) -> Any :
+399 result = self . get ( f "/w/ { self . workspace } /jobs_u/completed/get_result/ { job_id } " )
+400 result_text = result . text
+401 if assert_result_is_not_none and result_text is None :
+402 raise Exception ( f "result is None for { job_id = } " )
+403 try :
+404 return result . json ()
+405 except JSONDecodeError :
+406 return result_text
+407
+408 def get_variable ( self , path : str ) -> str :
+409 path = parse_variable_syntax ( path ) or path
+410 if self . mocked_api is not None :
+411 variables = self . mocked_api [ "variables" ]
+412 try :
+413 result = variables [ path ]
+414 return result
+415 except KeyError :
+416 logger . info (
+417 f "MockedAPI present, but variable not found at { path } , falling back to real API"
+418 )
+419
+420 """Get variable from Windmill"""
+421 return self . get ( f "/w/ { self . workspace } /variables/get_value/ { path } " ) . json ()
+422
+423 def set_variable ( self , path : str , value : str , is_secret : bool = False ) -> None :
+424 path = parse_variable_syntax ( path ) or path
+425 if self . mocked_api is not None :
+426 self . mocked_api [ "variables" ][ path ] = value
+427 return
+428
+429 """Set variable from Windmill"""
+430 # check if variable exists
+431 r = self . get (
+432 f "/w/ { self . workspace } /variables/get/ { path } " , raise_for_status = False
+433 )
+434 if r . status_code == 404 :
+435 # create variable
+436 self . post (
+437 f "/w/ { self . workspace } /variables/create" ,
+438 json = {
+439 "path" : path ,
+440 "value" : value ,
+441 "is_secret" : is_secret ,
+442 "description" : "" ,
+443 },
+444 )
+445 else :
+446 # update variable
+447 self . post (
+448 f "/w/ { self . workspace } /variables/update/ { path } " ,
+449 json = { "value" : value },
+450 )
+451
+452 def get_resource (
+453 self ,
+454 path : str ,
+455 none_if_undefined : bool = False ,
+456 ) -> dict | None :
+457 path = parse_resource_syntax ( path ) or path
+458 if self . mocked_api is not None :
+459 resources = self . mocked_api [ "resources" ]
+460 try :
+461 result = resources [ path ]
+462 return result
+463 except KeyError :
+464 # NOTE: should mocked_api respect `none_if_undefined`?
+465 if none_if_undefined :
+466 logger . info (
+467 f "resource not found at $ { path } , but none_if_undefined is True, so returning None"
+468 )
+469 return None
+470 logger . info (
+471 f "MockedAPI present, but resource not found at $ { path } , falling back to real API"
+472 )
+473
+474 """Get resource from Windmill"""
+475 try :
+476 return self . get (
+477 f "/w/ { self . workspace } /resources/get_value_interpolated/ { path } "
+478 ) . json ()
+479 except Exception as e :
+480 if none_if_undefined :
+481 return None
+482 logger . error ( e )
+483 raise e
+484
+485 def set_resource (
+486 self ,
+487 value : Any ,
+488 path : str ,
+489 resource_type : str ,
+490 ):
+491 path = parse_resource_syntax ( path ) or path
+492 if self . mocked_api is not None :
+493 self . mocked_api [ "resources" ][ path ] = value
+494 return
+495
+496 # check if resource exists
+497 r = self . get (
+498 f "/w/ { self . workspace } /resources/get/ { path } " , raise_for_status = False
+499 )
+500 if r . status_code == 404 :
+501 # create resource
+502 self . post (
+503 f "/w/ { self . workspace } /resources/create" ,
+504 json = {
+505 "path" : path ,
+506 "value" : value ,
+507 "resource_type" : resource_type ,
+508 },
+509 )
+510 else :
+511 # update resource
+512 self . post (
+513 f "/w/ { self . workspace } /resources/update_value/ { path } " ,
+514 json = { "value" : value },
+515 )
+516
+517 def set_state ( self , value : Any ):
+518 self . set_resource ( value , path = self . state_path , resource_type = "state" )
+519
+520 def set_progress ( self , value : int , job_id : Optional [ str ] = None ):
+521 workspace = get_workspace ()
+522 flow_id = os . environ . get ( "WM_FLOW_JOB_ID" )
+523 job_id = job_id or os . environ . get ( "WM_JOB_ID" )
+524
+525 if job_id != None :
+526 job = self . get_job ( job_id )
+527 flow_id = job . get ( "parent_job" )
+528
+529 self . post (
+530 f "/w/ { workspace } /job_metrics/set_progress/ { job_id } " ,
+531 json = {
+532 "percent" : value ,
+533 "flow_job_id" : flow_id or None ,
+534 },
+535 )
+536
+537 def get_progress ( self , job_id : Optional [ str ] = None ) -> Any :
+538 workspace = get_workspace ()
+539 job_id = job_id or os . environ . get ( "WM_JOB_ID" )
+540
+541 r = self . get (
+542 f "/w/ { workspace } /job_metrics/get_progress/ { job_id } " ,
+543 )
+544 if r . status_code == 404 :
+545 print ( f "Job { job_id } does not exist" )
+546 return None
+547 else :
+548 return r . json ()
+549
+550 def set_flow_user_state ( self , key : str , value : Any ) -> None :
+551 """Set the user state of a flow at a given key"""
+552 flow_id = self . get_root_job_id ()
+553 r = self . post (
+554 f "/w/ { self . workspace } /jobs/flow/user_states/ { flow_id } / { key } " ,
+555 json = value ,
+556 raise_for_status = False ,
+557 )
+558 if r . status_code == 404 :
+559 print ( f "Job { flow_id } does not exist or is not a flow" )
+560
+561 def get_flow_user_state ( self , key : str ) -> Any :
+562 """Get the user state of a flow at a given key"""
+563 flow_id = self . get_root_job_id ()
+564 r = self . get (
+565 f "/w/ { self . workspace } /jobs/flow/user_states/ { flow_id } / { key } " ,
+566 raise_for_status = False ,
+567 )
+568 if r . status_code == 404 :
+569 print ( f "Job { flow_id } does not exist or is not a flow" )
+570 return None
+571 else :
+572 return r . json ()
+573
+574 @property
+575 def version ( self ):
+576 return self . get ( "version" ) . text
+577
+578 def get_duckdb_connection_settings (
+579 self ,
+580 s3_resource_path : str = "" ,
+581 ) -> DuckDbConnectionSettings | None :
+582 """
+583 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+584 initiate an S3 connection from DuckDB
+585 """
+586 s3_resource_path = parse_resource_syntax ( s3_resource_path ) or s3_resource_path
+587 try :
+588 raw_obj = self . post (
+589 f "/w/ { self . workspace } /job_helpers/v2/duckdb_connection_settings" ,
+590 json = {}
+591 if s3_resource_path == ""
+592 else { "s3_resource_path" : s3_resource_path },
+593 ) . json ()
+594 return DuckDbConnectionSettings ( raw_obj )
+595 except JSONDecodeError as e :
+596 raise Exception (
+597 "Could not generate DuckDB S3 connection settings from the provided resource"
+598 ) from e
+599
+600 def get_polars_connection_settings (
+601 self ,
+602 s3_resource_path : str = "" ,
+603 ) -> PolarsConnectionSettings :
+604 """
+605 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+606 initiate an S3 connection from Polars
+607 """
+608 s3_resource_path = parse_resource_syntax ( s3_resource_path ) or s3_resource_path
+609 try :
+610 raw_obj = self . post (
+611 f "/w/ { self . workspace } /job_helpers/v2/polars_connection_settings" ,
+612 json = {}
+613 if s3_resource_path == ""
+614 else { "s3_resource_path" : s3_resource_path },
+615 ) . json ()
+616 return PolarsConnectionSettings ( raw_obj )
+617 except JSONDecodeError as e :
+618 raise Exception (
+619 "Could not generate Polars S3 connection settings from the provided resource"
+620 ) from e
+621
+622 def get_boto3_connection_settings (
+623 self ,
+624 s3_resource_path : str = "" ,
+625 ) -> Boto3ConnectionSettings :
+626 """
+627 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+628 initiate an S3 connection using boto3
+629 """
+630 s3_resource_path = parse_resource_syntax ( s3_resource_path ) or s3_resource_path
+631 try :
+632 s3_resource = self . post (
+633 f "/w/ { self . workspace } /job_helpers/v2/s3_resource_info" ,
+634 json = {}
+635 if s3_resource_path == ""
+636 else { "s3_resource_path" : s3_resource_path },
+637 ) . json ()
+638 return self . __boto3_connection_settings ( s3_resource )
+639 except JSONDecodeError as e :
+640 raise Exception (
+641 "Could not generate Boto3 S3 connection settings from the provided resource"
+642 ) from e
+643
+644 def load_s3_file ( self , s3object : S3Object | str , s3_resource_path : str | None ) -> bytes :
+645 """
+646 Load a file from the workspace s3 bucket and returns its content as bytes.
+647
+648 '''python
+649 from wmill import S3Object
+650
+651 s3_obj = S3Object(s3="/path/to/my_file.txt")
+652 my_obj_content = client.load_s3_file(s3_obj)
+653 file_content = my_obj_content.decode("utf-8")
+654 '''
+655 """
+656 s3object = parse_s3_object ( s3object )
+657 with self . load_s3_file_reader ( s3object , s3_resource_path ) as file_reader :
+658 return file_reader . read ()
+659
+660 def load_s3_file_reader (
+661 self , s3object : S3Object | str , s3_resource_path : str | None
+662 ) -> BufferedReader :
+663 """
+664 Load a file from the workspace s3 bucket and returns the bytes stream.
+665
+666 '''python
+667 from wmill import S3Object
+668
+669 s3_obj = S3Object(s3="/path/to/my_file.txt")
+670 with wmill.load_s3_file_reader(s3object, s3_resource_path) as file_reader:
+671 print(file_reader.read())
+672 '''
+673 """
+674 s3object = parse_s3_object ( s3object )
+675 reader = S3BufferedReader (
+676 f " { self . workspace } " ,
+677 self . client ,
+678 s3object [ "s3" ],
+679 s3_resource_path ,
+680 s3object [ "storage" ] if "storage" in s3object else None ,
+681 )
+682 return reader
+683
+684 def write_s3_file (
+685 self ,
+686 s3object : S3Object | str | None ,
+687 file_content : BufferedReader | bytes ,
+688 s3_resource_path : str | None ,
+689 content_type : str | None = None ,
+690 content_disposition : str | None = None ,
+691 ) -> S3Object :
+692 """
+693 Write a file to the workspace S3 bucket
+694
+695 '''python
+696 from wmill import S3Object
+697
+698 s3_obj = S3Object(s3="/path/to/my_file.txt")
+699
+700 # for an in memory bytes array:
+701 file_content = b'Hello Windmill!'
+702 client.write_s3_file(s3_obj, file_content)
+703
+704 # for a file:
+705 with open("my_file.txt", "rb") as my_file:
+706 client.write_s3_file(s3_obj, my_file)
+707 '''
+708 """
+709 s3object = parse_s3_object ( s3object )
+710 # httpx accepts either bytes or "a bytes generator" as content. If it's a BufferedReader, we need to convert it to a generator
+711 if isinstance ( file_content , BufferedReader ):
+712 content_payload = bytes_generator ( file_content )
+713 elif isinstance ( file_content , bytes ):
+714 content_payload = file_content
+715 else :
+716 raise Exception ( "Type of file_content not supported" )
+717
+718 query_params = {}
+719 if s3object is not None and s3object [ "s3" ] != "" :
+720 query_params [ "file_key" ] = s3object [ "s3" ]
+721 if s3_resource_path is not None and s3_resource_path != "" :
+722 query_params [ "s3_resource_path" ] = s3_resource_path
+723 if (
+724 s3object is not None
+725 and "storage" in s3object
+726 and s3object [ "storage" ] is not None
+727 ):
+728 query_params [ "storage" ] = s3object [ "storage" ]
+729 if content_type is not None :
+730 query_params [ "content_type" ] = content_type
+731 if content_disposition is not None :
+732 query_params [ "content_disposition" ] = content_disposition
+733
+734 try :
+735 # need a vanilla client b/c content-type is not application/json here
+736 response = httpx . post (
+737 f " { self . base_url } /w/ { self . workspace } /job_helpers/upload_s3_file" ,
+738 headers = {
+739 "Authorization" : f "Bearer { self . token } " ,
+740 "Content-Type" : "application/octet-stream" ,
+741 },
+742 params = query_params ,
+743 content = content_payload ,
+744 verify = self . verify ,
+745 timeout = None ,
+746 ) . json ()
+747 except Exception as e :
+748 raise Exception ( "Could not write file to S3" ) from e
+749 return S3Object ( s3 = response [ "file_key" ])
+750
+751 def sign_s3_objects ( self , s3_objects : list [ S3Object | str ]) -> list [ S3Object ]:
+752 return self . post (
+753 f "/w/ { self . workspace } /apps/sign_s3_objects" , json = { "s3_objects" : list ( map ( parse_s3_object , s3_objects ))}
+754 ) . json ()
+755
+756 def sign_s3_object ( self , s3_object : S3Object | str ) -> S3Object :
+757 return self . post (
+758 f "/w/ { self . workspace } /apps/sign_s3_objects" ,
+759 json = { "s3_objects" : [ s3_object ]},
+760 ) . json ()[ 0 ]
+761
+762 def __boto3_connection_settings ( self , s3_resource ) -> Boto3ConnectionSettings :
+763 endpoint_url_prefix = "https://" if s3_resource [ "useSSL" ] else "http://"
+764 return Boto3ConnectionSettings (
+765 {
+766 "endpoint_url" : " {}{} " . format (
+767 endpoint_url_prefix , s3_resource [ "endPoint" ]
+768 ),
+769 "region_name" : s3_resource [ "region" ],
+770 "use_ssl" : s3_resource [ "useSSL" ],
+771 "aws_access_key_id" : s3_resource [ "accessKey" ],
+772 "aws_secret_access_key" : s3_resource [ "secretKey" ],
+773 # no need for path_style here as boto3 is clever enough to determine which one to use
+774 }
+775 )
+776
+777 def whoami ( self ) -> dict :
+778 return self . get ( "/users/whoami" ) . json ()
+779
+780 @property
+781 def user ( self ) -> dict :
+782 return self . whoami ()
+783
+784 @property
+785 def state_path ( self ) -> str :
+786 state_path = os . environ . get (
+787 "WM_STATE_PATH_NEW" , os . environ . get ( "WM_STATE_PATH" )
+788 )
+789 if state_path is None :
+790 raise Exception ( "State path not found" )
+791 return state_path
+792
+793 @property
+794 def state ( self ) -> Any :
+795 return self . get_resource ( path = self . state_path , none_if_undefined = True )
+796
+797 @state . setter
+798 def state ( self , value : Any ) -> None :
+799 self . set_state ( value )
+800
+801 @staticmethod
+802 def set_shared_state_pickle ( value : Any , path : str = "state.pickle" ) -> None :
+803 """
+804 Set the state in the shared folder using pickle
+805 """
+806 import pickle
+807
+808 with open ( f "/shared/ { path } " , "wb" ) as handle :
+809 pickle . dump ( value , handle , protocol = pickle . HIGHEST_PROTOCOL )
+810
+811 @staticmethod
+812 def get_shared_state_pickle ( path : str = "state.pickle" ) -> Any :
+813 """
+814 Get the state in the shared folder using pickle
+815 """
+816 import pickle
+817
+818 with open ( f "/shared/ { path } " , "rb" ) as handle :
+819 return pickle . load ( handle )
+820
+821 @staticmethod
+822 def set_shared_state ( value : Any , path : str = "state.json" ) -> None :
+823 """
+824 Set the state in the shared folder using pickle
+825 """
+826 import json
+827
+828 with open ( f "/shared/ { path } " , "w" , encoding = "utf-8" ) as f :
+829 json . dump ( value , f , ensure_ascii = False , indent = 4 )
+830
+831 @staticmethod
+832 def get_shared_state ( path : str = "state.json" ) -> None :
+833 """
+834 Get the state in the shared folder using pickle
+835 """
+836 import json
+837
+838 with open ( f "/shared/ { path } " , "r" , encoding = "utf-8" ) as f :
+839 return json . load ( f )
+840
+841 def get_resume_urls ( self , approver : str = None ) -> dict :
+842 nonce = random . randint ( 0 , 1000000000 )
+843 job_id = os . environ . get ( "WM_JOB_ID" ) or "NO_ID"
+844 return self . get (
+845 f "/w/ { self . workspace } /jobs/resume_urls/ { job_id } / { nonce } " ,
+846 params = { "approver" : approver },
+847 ) . json ()
+848
+849 def request_interactive_slack_approval (
+850 self ,
+851 slack_resource_path : str ,
+852 channel_id : str ,
+853 message : str = None ,
+854 approver : str = None ,
+855 default_args_json : dict = None ,
+856 dynamic_enums_json : dict = None ,
+857 ) -> None :
+858 """
+859 Sends an interactive approval request via Slack, allowing optional customization of the message, approver, and form fields.
+860
+861 **[Enterprise Edition Only]** To include form fields in the Slack approval request, use the "Advanced -> Suspend -> Form" functionality.
+862 Learn more at: https://www.windmill.dev/docs/flows/flow_approval#form
+863
+864 :param slack_resource_path: The path to the Slack resource in Windmill.
+865 :type slack_resource_path: str
+866 :param channel_id: The Slack channel ID where the approval request will be sent.
+867 :type channel_id: str
+868 :param message: Optional custom message to include in the Slack approval request.
+869 :type message: str, optional
+870 :param approver: Optional user ID or name of the approver for the request.
+871 :type approver: str, optional
+872 :param default_args_json: Optional dictionary defining or overriding the default arguments for form fields.
+873 :type default_args_json: dict, optional
+874 :param dynamic_enums_json: Optional dictionary overriding the enum default values of enum form fields.
+875 :type dynamic_enums_json: dict, optional
+876
+877 :raises Exception: If the function is not called within a flow or flow preview.
+878 :raises Exception: If the required flow job or flow step environment variables are not set.
+879
+880 :return: None
+881
+882 **Usage Example:**
+883 >>> client.request_interactive_slack_approval(
+884 ... slack_resource_path="/u/alex/my_slack_resource",
+885 ... channel_id="admins-slack-channel",
+886 ... message="Please approve this request",
+887 ... approver="approver123",
+888 ... default_args_json={"key1": "value1", "key2": 42},
+889 ... dynamic_enums_json={"foo": ["choice1", "choice2"], "bar": ["optionA", "optionB"]},
+890 ... )
+891
+892 **Notes:**
+893 - This function must be executed within a Windmill flow or flow preview.
+894 - The function checks for required environment variables (`WM_FLOW_JOB_ID`, `WM_FLOW_STEP_ID`) to ensure it is run in the appropriate context.
+895 """
+896 workspace = self . workspace
+897 flow_job_id = os . environ . get ( "WM_FLOW_JOB_ID" )
+898
+899 if not flow_job_id :
+900 raise Exception (
+901 "You can't use 'request_interactive_slack_approval' function in a standalone script or flow step preview. Please use it in a flow or a flow preview."
+902 )
+903
+904 # Only include non-empty parameters
+905 params = {}
+906 if message :
+907 params [ "message" ] = message
+908 if approver :
+909 params [ "approver" ] = approver
+910 if slack_resource_path :
+911 params [ "slack_resource_path" ] = slack_resource_path
+912 if channel_id :
+913 params [ "channel_id" ] = channel_id
+914 if os . environ . get ( "WM_FLOW_STEP_ID" ):
+915 params [ "flow_step_id" ] = os . environ . get ( "WM_FLOW_STEP_ID" )
+916 if default_args_json :
+917 params [ "default_args_json" ] = json . dumps ( default_args_json )
+918 if dynamic_enums_json :
+919 params [ "dynamic_enums_json" ] = json . dumps ( dynamic_enums_json )
+920
+921 self . get (
+922 f "/w/ { workspace } /jobs/slack_approval/ { os . environ . get ( 'WM_JOB_ID' , 'NO_JOB_ID' ) } " ,
+923 params = params ,
+924 )
+925
+926 def username_to_email ( self , username : str ) -> str :
+927 """
+928 Get email from workspace username
+929 This method is particularly useful for apps that require the email address of the viewer.
+930 Indeed, in the viewer context WM_USERNAME is set to the username of the viewer but WM_EMAIL is set to the email of the creator of the app.
+931 """
+932 return self . get ( f "/w/ { self . workspace } /users/username_to_email/ { username } " ) . text
+933
+934 def send_teams_message (
+935 self ,
+936 conversation_id : str ,
+937 text : str ,
+938 success : bool = True ,
+939 card_block : dict = None ,
+940 ):
+941 """
+942 Send a message to a Microsoft Teams conversation with conversation_id, where success is used to style the message
+943 """
+944 return self . post (
+945 f "/teams/activities" ,
+946 json = {
+947 "conversation_id" : conversation_id ,
+948 "text" : text ,
+949 "success" : success ,
+950 "card_block" : card_block ,
+951 },
+952 )
+
+
+
+
+
+
+
+
+
+ Windmill (base_url = None , token = None , workspace = None , verify = True )
+
+ View Source
+
+
+
+
36 def __init__ ( self , base_url = None , token = None , workspace = None , verify = True ):
+37 base = (
+38 base_url
+39 or os . environ . get ( "BASE_INTERNAL_URL" )
+40 or os . environ . get ( "WM_BASE_URL" )
+41 )
+42
+43 self . base_url = f " { base } /api"
+44 self . token = token or os . environ . get ( "WM_TOKEN" )
+45 self . headers = {
+46 "Content-Type" : "application/json" ,
+47 "Authorization" : f "Bearer { self . token } " ,
+48 }
+49 self . verify = verify
+50 self . client = self . get_client ()
+51 self . workspace = workspace or os . environ . get ( "WM_WORKSPACE" )
+52 self . path = os . environ . get ( "WM_JOB_PATH" )
+53
+54 self . mocked_api = self . get_mocked_api ()
+55
+56 assert self . workspace , (
+57 f "workspace required as an argument or as WM_WORKSPACE environment variable"
+58 )
+
+
+
+
+
+
+
+
+ base_url
+
+
+
+
+
+
+
+
+
+
+ token
+
+
+
+
+
+
+
+
+
+
+
+ verify
+
+
+
+
+
+
+
+
+
+
+ client
+
+
+
+
+
+
+
+
+
+
+ workspace
+
+
+
+
+
+
+
+
+
+
+
+ mocked_api
+
+
+
+
+
+
+
+
+
+
+
+
+ def
+ get_mocked_api (self ) -> Optional [ dict ] :
+
+ View Source
+
+
+
+
60 def get_mocked_api ( self ) -> Optional [ dict ]:
+61 mocked_path = os . environ . get ( "WM_MOCKED_API_FILE" )
+62 if not mocked_path :
+63 return None
+64 logger . info ( "Using mocked API from %s " , mocked_path )
+65 mocked_api = { "variables" : {}, "resources" : {}}
+66 try :
+67 with open ( mocked_path , "r" ) as f :
+68 incoming_mocked_api = json . load ( f )
+69 mocked_api = { ** mocked_api , ** incoming_mocked_api }
+70 except Exception as e :
+71 logger . warning (
+72 "Error parsing mocked API file at path %s Using empty mocked API." ,
+73 mocked_path ,
+74 )
+75 logger . debug ( e )
+76 return mocked_api
+
+
+
+
+
+
+
+
+
+
+ def
+ get_client (self ) -> httpx . Client :
+
+ View Source
+
+
+
+
78 def get_client ( self ) -> httpx . Client :
+79 return httpx . Client (
+80 base_url = self . base_url ,
+81 headers = self . headers ,
+82 verify = self . verify ,
+83 )
+
+
+
+
+
+
+
+
+
+
+ def
+ get (self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response :
+
+ View Source
+
+
+
+
85 def get ( self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response :
+86 endpoint = endpoint . lstrip ( "/" )
+87 resp = self . client . get ( f "/ { endpoint } " , ** kwargs )
+88 if raise_for_status :
+89 try :
+90 resp . raise_for_status ()
+91 except httpx . HTTPStatusError as err :
+92 error = f " { err . request . url } : { err . response . status_code } , { err . response . text } "
+93 logger . error ( error )
+94 raise Exception ( error )
+95 return resp
+
+
+
+
+
+
+
+
+
+
+ def
+ post (self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response :
+
+ View Source
+
+
+
+
97 def post ( self , endpoint , raise_for_status = True , ** kwargs ) -> httpx . Response :
+ 98 endpoint = endpoint . lstrip ( "/" )
+ 99 resp = self . client . post ( f "/ { endpoint } " , ** kwargs )
+100 if raise_for_status :
+101 try :
+102 resp . raise_for_status ()
+103 except httpx . HTTPStatusError as err :
+104 error = f " { err . request . url } : { err . response . status_code } , { err . response . text } "
+105 logger . error ( error )
+106 raise Exception ( error )
+107 return resp
+
+
+
+
+
+
+
+
+
+
+ def
+ create_token (self , duration = datetime . timedelta ( days = 1 ) ) -> str :
+
+ View Source
+
+
+
+
109 def create_token ( self , duration = dt . timedelta ( days = 1 )) -> str :
+110 endpoint = "/users/tokens/create"
+111 payload = {
+112 "label" : f "refresh { time . time () } " ,
+113 "expiration" : ( dt . datetime . now () + duration ) . strftime ( "%Y-%m- %d T%H:%M:%SZ" ),
+114 }
+115 return self . post ( endpoint , json = payload ) . text
+
+
+
+
+
+
+
+
+
+
+ def
+ run_script_async ( self , path : str = None , hash_ : str = None , args : dict = None , scheduled_in_secs : int = None ) -> str :
+
+ View Source
+
+
+
+
117 def run_script_async (
+118 self ,
+119 path : str = None ,
+120 hash_ : str = None ,
+121 args : dict = None ,
+122 scheduled_in_secs : int = None ,
+123 ) -> str :
+124 """Create a script job and return its job id.
+125
+126 .. deprecated:: Use run_script_by_path_async or run_script_by_hash_async instead.
+127 """
+128 logging . warning (
+129 "run_script_async is deprecated. Use run_script_by_path_async or run_script_by_hash_async instead." ,
+130 )
+131 assert not ( path and hash_ ), "path and hash_ are mutually exclusive"
+132 return self . _run_script_async_internal ( path = path , hash_ = hash_ , args = args , scheduled_in_secs = scheduled_in_secs )
+
+
+
+
Create a script job and return its job id.
+
+
Deprecated since version Use run_script_by_path_async or run_script_by_hash_async instead..
+
+
+
+
+
+
+
+
+ def
+ run_script_by_path_async (self , path : str , args : dict = None , scheduled_in_secs : int = None ) -> str :
+
+ View Source
+
+
+
+
158 def run_script_by_path_async (
+159 self ,
+160 path : str ,
+161 args : dict = None ,
+162 scheduled_in_secs : int = None ,
+163 ) -> str :
+164 """Create a script job by path and return its job id."""
+165 return self . _run_script_async_internal ( path = path , args = args , scheduled_in_secs = scheduled_in_secs )
+
+
+
+
Create a script job by path and return its job id.
+
+
+
+
+
+
+
+
+ def
+ run_script_by_hash_async ( self , hash_ : str , args : dict = None , scheduled_in_secs : int = None ) -> str :
+
+ View Source
+
+
+
+
167 def run_script_by_hash_async (
+168 self ,
+169 hash_ : str ,
+170 args : dict = None ,
+171 scheduled_in_secs : int = None ,
+172 ) -> str :
+173 """Create a script job by hash and return its job id."""
+174 return self . _run_script_async_internal ( hash_ = hash_ , args = args , scheduled_in_secs = scheduled_in_secs )
+
+
+
+
Create a script job by hash and return its job id.
+
+
+
+
+
+
+
+
+ def
+ run_flow_async ( self , path : str , args : dict = None , scheduled_in_secs : int = None , do_not_track_in_parent : bool = True ) -> str :
+
+ View Source
+
+
+
+
176 def run_flow_async (
+177 self ,
+178 path : str ,
+179 args : dict = None ,
+180 scheduled_in_secs : int = None ,
+181 # can only be set to false if this the job will be fully await and not concurrent with any other job
+182 # as otherwise the child flow and its own child will store their state in the parent job which will
+183 # lead to incorrectness and failures
+184 do_not_track_in_parent : bool = True ,
+185 ) -> str :
+186 """Create a flow job and return its job id."""
+187 args = args or {}
+188 params = { "scheduled_in_secs" : scheduled_in_secs } if scheduled_in_secs else {}
+189 if not do_not_track_in_parent :
+190 if os . environ . get ( "WM_JOB_ID" ):
+191 params [ "parent_job" ] = os . environ . get ( "WM_JOB_ID" )
+192 if os . environ . get ( "WM_ROOT_FLOW_JOB_ID" ):
+193 params [ "root_job" ] = os . environ . get ( "WM_ROOT_FLOW_JOB_ID" )
+194 if path :
+195 endpoint = f "/w/ { self . workspace } /jobs/run/f/ { path } "
+196 else :
+197 raise Exception ( "path must be provided" )
+198 return self . post ( endpoint , json = args , params = params ) . text
+
+
+
+
Create a flow job and return its job id.
+
+
+
+
+
+
+
+
+ def
+ run_script ( self , path : str = None , hash_ : str = None , args : dict = None , timeout : datetime . timedelta | int | float | None = None , verbose : bool = False , cleanup : bool = True , assert_result_is_not_none : bool = False ) -> Any :
+
+ View Source
+
+
+
+
200 def run_script (
+201 self ,
+202 path : str = None ,
+203 hash_ : str = None ,
+204 args : dict = None ,
+205 timeout : dt . timedelta | int | float | None = None ,
+206 verbose : bool = False ,
+207 cleanup : bool = True ,
+208 assert_result_is_not_none : bool = False ,
+209 ) -> Any :
+210 """Run script synchronously and return its result.
+211
+212 .. deprecated:: Use run_script_by_path or run_script_by_hash instead.
+213 """
+214 logging . warning (
+215 "run_script is deprecated. Use run_script_by_path or run_script_by_hash instead." ,
+216 )
+217 assert not ( path and hash_ ), "path and hash_ are mutually exclusive"
+218 return self . _run_script_internal (
+219 path = path , hash_ = hash_ , args = args , timeout = timeout , verbose = verbose ,
+220 cleanup = cleanup , assert_result_is_not_none = assert_result_is_not_none
+221 )
+
+
+
+
Run script synchronously and return its result.
+
+
Deprecated since version Use run_script_by_path or run_script_by_hash instead..
+
+
+
+
+
+
+
+
+ def
+ run_script_by_path ( self , path : str , args : dict = None , timeout : datetime . timedelta | int | float | None = None , verbose : bool = False , cleanup : bool = True , assert_result_is_not_none : bool = False ) -> Any :
+
+ View Source
+
+
+
+
250 def run_script_by_path (
+251 self ,
+252 path : str ,
+253 args : dict = None ,
+254 timeout : dt . timedelta | int | float | None = None ,
+255 verbose : bool = False ,
+256 cleanup : bool = True ,
+257 assert_result_is_not_none : bool = False ,
+258 ) -> Any :
+259 """Run script by path synchronously and return its result."""
+260 return self . _run_script_internal (
+261 path = path , args = args , timeout = timeout , verbose = verbose ,
+262 cleanup = cleanup , assert_result_is_not_none = assert_result_is_not_none
+263 )
+
+
+
+
Run script by path synchronously and return its result.
+
+
+
+
+
+
+
+
+ def
+ run_script_by_hash ( self , hash_ : str , args : dict = None , timeout : datetime . timedelta | int | float | None = None , verbose : bool = False , cleanup : bool = True , assert_result_is_not_none : bool = False ) -> Any :
+
+ View Source
+
+
+
+
265 def run_script_by_hash (
+266 self ,
+267 hash_ : str ,
+268 args : dict = None ,
+269 timeout : dt . timedelta | int | float | None = None ,
+270 verbose : bool = False ,
+271 cleanup : bool = True ,
+272 assert_result_is_not_none : bool = False ,
+273 ) -> Any :
+274 """Run script by hash synchronously and return its result."""
+275 return self . _run_script_internal (
+276 hash_ = hash_ , args = args , timeout = timeout , verbose = verbose ,
+277 cleanup = cleanup , assert_result_is_not_none = assert_result_is_not_none
+278 )
+
+
+
+
Run script by hash synchronously and return its result.
+
+
+
+
+
+
+
+
+ def
+ wait_job ( self , job_id , timeout : datetime . timedelta | int | float | None = None , verbose : bool = False , cleanup : bool = True , assert_result_is_not_none : bool = False ):
+
+ View Source
+
+
+
+
280 def wait_job (
+281 self ,
+282 job_id ,
+283 timeout : dt . timedelta | int | float | None = None ,
+284 verbose : bool = False ,
+285 cleanup : bool = True ,
+286 assert_result_is_not_none : bool = False ,
+287 ):
+288 def cancel_job ():
+289 logger . warning ( f "cancelling job: { job_id } " )
+290 self . post (
+291 f "/w/ { self . workspace } /jobs_u/queue/cancel/ { job_id } " ,
+292 json = { "reason" : "parent script cancelled" },
+293 ) . raise_for_status ()
+294
+295 if cleanup :
+296 atexit . register ( cancel_job )
+297
+298 start_time = time . time ()
+299
+300 if isinstance ( timeout , dt . timedelta ):
+301 timeout = timeout . total_seconds ()
+302
+303 while True :
+304 result_res = self . get (
+305 f "/w/ { self . workspace } /jobs_u/completed/get_result_maybe/ { job_id } " , True
+306 ) . json ()
+307
+308 started = result_res [ "started" ]
+309 completed = result_res [ "completed" ]
+310 success = result_res [ "success" ]
+311
+312 if not started and verbose :
+313 logger . info ( f "job { job_id } has not started yet" )
+314
+315 if cleanup and completed :
+316 atexit . unregister ( cancel_job )
+317
+318 if completed :
+319 result = result_res [ "result" ]
+320 if success :
+321 if result is None and assert_result_is_not_none :
+322 raise Exception ( "Result was none" )
+323 return result
+324 else :
+325 error = result [ "error" ]
+326 raise Exception ( f "Job { job_id } was not successful: { str ( error ) } " )
+327
+328 if timeout and (( time . time () - start_time ) > timeout ):
+329 msg = "reached timeout"
+330 logger . warning ( msg )
+331 self . post (
+332 f "/w/ { self . workspace } /jobs_u/queue/cancel/ { job_id } " ,
+333 json = { "reason" : msg },
+334 )
+335 raise TimeoutError ( msg )
+336 if verbose :
+337 logger . info ( f "sleeping 0.5 seconds for { job_id = } " )
+338
+339 time . sleep ( 0.5 )
+
+
+
+
+
+
+
+
+
+
+ def
+ cancel_running (self ) -> dict :
+
+ View Source
+
+
+
+
341 def cancel_running ( self ) -> dict :
+342 """Cancel currently running executions of the same script."""
+343 logger . info ( "canceling running executions of this script" )
+344
+345 jobs = self . get (
+346 f "/w/ { self . workspace } /jobs/list" ,
+347 params = {
+348 "running" : "true" ,
+349 "script_path_exact" : self . path ,
+350 },
+351 ) . json ()
+352
+353 current_job_id = os . environ . get ( "WM_JOB_ID" )
+354
+355 logger . debug ( f " { current_job_id = } " )
+356
+357 job_ids = [ j [ "id" ] for j in jobs if j [ "id" ] != current_job_id ]
+358
+359 if job_ids :
+360 logger . info ( f "cancelling the following job ids: { job_ids } " )
+361 else :
+362 logger . info ( "no previous executions to cancel" )
+363
+364 result = {}
+365
+366 for id_ in job_ids :
+367 result [ id_ ] = self . post (
+368 f "/w/ { self . workspace } /jobs_u/queue/cancel/ { id_ } " ,
+369 json = { "reason" : "killed by `cancel_running` method" },
+370 )
+371
+372 return result
+
+
+
+
Cancel currently running executions of the same script.
+
+
+
+
+
+
+
+
+ def
+ get_job (self , job_id : str ) -> dict :
+
+ View Source
+
+
+
+
374 def get_job ( self , job_id : str ) -> dict :
+375 return self . get ( f "/w/ { self . workspace } /jobs_u/get/ { job_id } " ) . json ()
+
+
+
+
+
+
+
+
+
+
+ def
+ get_root_job_id (self , job_id : str | None = None ) -> dict :
+
+ View Source
+
+
+
+
377 def get_root_job_id ( self , job_id : str | None = None ) -> dict :
+378 job_id = job_id or os . environ . get ( "WM_JOB_ID" )
+379 return self . get ( f "/w/ { self . workspace } /jobs_u/get_root_job_id/ { job_id } " ) . json ()
+
+
+
+
+
+
+
+
+
+
+ def
+ get_id_token (self , audience : str ) -> str :
+
+ View Source
+
+
+
+
381 def get_id_token ( self , audience : str ) -> str :
+382 return self . post ( f "/w/ { self . workspace } /oidc/token/ { audience } " ) . text
+
+
+
+
+
+
+
+
+
+
+ def
+ get_job_status (self , job_id : str ) -> Literal [ 'RUNNING' , 'WAITING' , 'COMPLETED' ] :
+
+ View Source
+
+
+
+
384 def get_job_status ( self , job_id : str ) -> JobStatus :
+385 job = self . get_job ( job_id )
+386 job_type = job . get ( "type" , "" )
+387 assert job_type , f " { job } is not a valid job"
+388 if job_type . lower () == "completedjob" :
+389 return "COMPLETED"
+390 if job . get ( "running" ):
+391 return "RUNNING"
+392 return "WAITING"
+
+
+
+
+
+
+
+
+
+
+ def
+ get_result (self , job_id : str , assert_result_is_not_none : bool = True ) -> Any :
+
+ View Source
+
+
+
+
394 def get_result (
+395 self ,
+396 job_id : str ,
+397 assert_result_is_not_none : bool = True ,
+398 ) -> Any :
+399 result = self . get ( f "/w/ { self . workspace } /jobs_u/completed/get_result/ { job_id } " )
+400 result_text = result . text
+401 if assert_result_is_not_none and result_text is None :
+402 raise Exception ( f "result is None for { job_id = } " )
+403 try :
+404 return result . json ()
+405 except JSONDecodeError :
+406 return result_text
+
+
+
+
+
+
+
+
+
+
+ def
+ get_variable (self , path : str ) -> str :
+
+ View Source
+
+
+
+
408 def get_variable ( self , path : str ) -> str :
+409 path = parse_variable_syntax ( path ) or path
+410 if self . mocked_api is not None :
+411 variables = self . mocked_api [ "variables" ]
+412 try :
+413 result = variables [ path ]
+414 return result
+415 except KeyError :
+416 logger . info (
+417 f "MockedAPI present, but variable not found at { path } , falling back to real API"
+418 )
+419
+420 """Get variable from Windmill"""
+421 return self . get ( f "/w/ { self . workspace } /variables/get_value/ { path } " ) . json ()
+
+
+
+
+
+
+
+
+
+
+ def
+ set_variable (self , path : str , value : str , is_secret : bool = False ) -> None :
+
+ View Source
+
+
+
+
423 def set_variable ( self , path : str , value : str , is_secret : bool = False ) -> None :
+424 path = parse_variable_syntax ( path ) or path
+425 if self . mocked_api is not None :
+426 self . mocked_api [ "variables" ][ path ] = value
+427 return
+428
+429 """Set variable from Windmill"""
+430 # check if variable exists
+431 r = self . get (
+432 f "/w/ { self . workspace } /variables/get/ { path } " , raise_for_status = False
+433 )
+434 if r . status_code == 404 :
+435 # create variable
+436 self . post (
+437 f "/w/ { self . workspace } /variables/create" ,
+438 json = {
+439 "path" : path ,
+440 "value" : value ,
+441 "is_secret" : is_secret ,
+442 "description" : "" ,
+443 },
+444 )
+445 else :
+446 # update variable
+447 self . post (
+448 f "/w/ { self . workspace } /variables/update/ { path } " ,
+449 json = { "value" : value },
+450 )
+
+
+
+
+
+
+
+
+
+
+ def
+ get_resource (self , path : str , none_if_undefined : bool = False ) -> dict | None :
+
+ View Source
+
+
+
+
452 def get_resource (
+453 self ,
+454 path : str ,
+455 none_if_undefined : bool = False ,
+456 ) -> dict | None :
+457 path = parse_resource_syntax ( path ) or path
+458 if self . mocked_api is not None :
+459 resources = self . mocked_api [ "resources" ]
+460 try :
+461 result = resources [ path ]
+462 return result
+463 except KeyError :
+464 # NOTE: should mocked_api respect `none_if_undefined`?
+465 if none_if_undefined :
+466 logger . info (
+467 f "resource not found at $ { path } , but none_if_undefined is True, so returning None"
+468 )
+469 return None
+470 logger . info (
+471 f "MockedAPI present, but resource not found at $ { path } , falling back to real API"
+472 )
+473
+474 """Get resource from Windmill"""
+475 try :
+476 return self . get (
+477 f "/w/ { self . workspace } /resources/get_value_interpolated/ { path } "
+478 ) . json ()
+479 except Exception as e :
+480 if none_if_undefined :
+481 return None
+482 logger . error ( e )
+483 raise e
+
+
+
+
+
+
+
+
+
+
+ def
+ set_resource (self , value : Any , path : str , resource_type : str ):
+
+ View Source
+
+
+
+
485 def set_resource (
+486 self ,
+487 value : Any ,
+488 path : str ,
+489 resource_type : str ,
+490 ):
+491 path = parse_resource_syntax ( path ) or path
+492 if self . mocked_api is not None :
+493 self . mocked_api [ "resources" ][ path ] = value
+494 return
+495
+496 # check if resource exists
+497 r = self . get (
+498 f "/w/ { self . workspace } /resources/get/ { path } " , raise_for_status = False
+499 )
+500 if r . status_code == 404 :
+501 # create resource
+502 self . post (
+503 f "/w/ { self . workspace } /resources/create" ,
+504 json = {
+505 "path" : path ,
+506 "value" : value ,
+507 "resource_type" : resource_type ,
+508 },
+509 )
+510 else :
+511 # update resource
+512 self . post (
+513 f "/w/ { self . workspace } /resources/update_value/ { path } " ,
+514 json = { "value" : value },
+515 )
+
+
+
+
+
+
+
+
+
+
+ def
+ set_state (self , value : Any ):
+
+ View Source
+
+
+
+
517 def set_state ( self , value : Any ):
+518 self . set_resource ( value , path = self . state_path , resource_type = "state" )
+
+
+
+
+
+
+
+
+
+
+ def
+ set_progress (self , value : int , job_id : Optional [ str ] = None ):
+
+ View Source
+
+
+
+
520 def set_progress ( self , value : int , job_id : Optional [ str ] = None ):
+521 workspace = get_workspace ()
+522 flow_id = os . environ . get ( "WM_FLOW_JOB_ID" )
+523 job_id = job_id or os . environ . get ( "WM_JOB_ID" )
+524
+525 if job_id != None :
+526 job = self . get_job ( job_id )
+527 flow_id = job . get ( "parent_job" )
+528
+529 self . post (
+530 f "/w/ { workspace } /job_metrics/set_progress/ { job_id } " ,
+531 json = {
+532 "percent" : value ,
+533 "flow_job_id" : flow_id or None ,
+534 },
+535 )
+
+
+
+
+
+
+
+
+
+
+ def
+ get_progress (self , job_id : Optional [ str ] = None ) -> Any :
+
+ View Source
+
+
+
+
537 def get_progress ( self , job_id : Optional [ str ] = None ) -> Any :
+538 workspace = get_workspace ()
+539 job_id = job_id or os . environ . get ( "WM_JOB_ID" )
+540
+541 r = self . get (
+542 f "/w/ { workspace } /job_metrics/get_progress/ { job_id } " ,
+543 )
+544 if r . status_code == 404 :
+545 print ( f "Job { job_id } does not exist" )
+546 return None
+547 else :
+548 return r . json ()
+
+
+
+
+
+
+
+
+
+
+ def
+ set_flow_user_state (self , key : str , value : Any ) -> None :
+
+ View Source
+
+
+
+
550 def set_flow_user_state ( self , key : str , value : Any ) -> None :
+551 """Set the user state of a flow at a given key"""
+552 flow_id = self . get_root_job_id ()
+553 r = self . post (
+554 f "/w/ { self . workspace } /jobs/flow/user_states/ { flow_id } / { key } " ,
+555 json = value ,
+556 raise_for_status = False ,
+557 )
+558 if r . status_code == 404 :
+559 print ( f "Job { flow_id } does not exist or is not a flow" )
+
+
+
+
Set the user state of a flow at a given key
+
+
+
+
+
+
+
+
+ def
+ get_flow_user_state (self , key : str ) -> Any :
+
+ View Source
+
+
+
+
561 def get_flow_user_state ( self , key : str ) -> Any :
+562 """Get the user state of a flow at a given key"""
+563 flow_id = self . get_root_job_id ()
+564 r = self . get (
+565 f "/w/ { self . workspace } /jobs/flow/user_states/ { flow_id } / { key } " ,
+566 raise_for_status = False ,
+567 )
+568 if r . status_code == 404 :
+569 print ( f "Job { flow_id } does not exist or is not a flow" )
+570 return None
+571 else :
+572 return r . json ()
+
+
+
+
Get the user state of a flow at a given key
+
+
+
+
+
+
+
+ version
+
+ View Source
+
+
+
+
574 @property
+575 def version ( self ):
+576 return self . get ( "version" ) . text
+
+
+
+
+
+
+
+
+
+
+
578 def get_duckdb_connection_settings (
+579 self ,
+580 s3_resource_path : str = "" ,
+581 ) -> DuckDbConnectionSettings | None :
+582 """
+583 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+584 initiate an S3 connection from DuckDB
+585 """
+586 s3_resource_path = parse_resource_syntax ( s3_resource_path ) or s3_resource_path
+587 try :
+588 raw_obj = self . post (
+589 f "/w/ { self . workspace } /job_helpers/v2/duckdb_connection_settings" ,
+590 json = {}
+591 if s3_resource_path == ""
+592 else { "s3_resource_path" : s3_resource_path },
+593 ) . json ()
+594 return DuckDbConnectionSettings ( raw_obj )
+595 except JSONDecodeError as e :
+596 raise Exception (
+597 "Could not generate DuckDB S3 connection settings from the provided resource"
+598 ) from e
+
+
+
+
Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+initiate an S3 connection from DuckDB
+
+
+
+
+
+
+
+
+
600 def get_polars_connection_settings (
+601 self ,
+602 s3_resource_path : str = "" ,
+603 ) -> PolarsConnectionSettings :
+604 """
+605 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+606 initiate an S3 connection from Polars
+607 """
+608 s3_resource_path = parse_resource_syntax ( s3_resource_path ) or s3_resource_path
+609 try :
+610 raw_obj = self . post (
+611 f "/w/ { self . workspace } /job_helpers/v2/polars_connection_settings" ,
+612 json = {}
+613 if s3_resource_path == ""
+614 else { "s3_resource_path" : s3_resource_path },
+615 ) . json ()
+616 return PolarsConnectionSettings ( raw_obj )
+617 except JSONDecodeError as e :
+618 raise Exception (
+619 "Could not generate Polars S3 connection settings from the provided resource"
+620 ) from e
+
+
+
+
Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+initiate an S3 connection from Polars
+
+
+
+
+
+
+
+
+
622 def get_boto3_connection_settings (
+623 self ,
+624 s3_resource_path : str = "" ,
+625 ) -> Boto3ConnectionSettings :
+626 """
+627 Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+628 initiate an S3 connection using boto3
+629 """
+630 s3_resource_path = parse_resource_syntax ( s3_resource_path ) or s3_resource_path
+631 try :
+632 s3_resource = self . post (
+633 f "/w/ { self . workspace } /job_helpers/v2/s3_resource_info" ,
+634 json = {}
+635 if s3_resource_path == ""
+636 else { "s3_resource_path" : s3_resource_path },
+637 ) . json ()
+638 return self . __boto3_connection_settings ( s3_resource )
+639 except JSONDecodeError as e :
+640 raise Exception (
+641 "Could not generate Boto3 S3 connection settings from the provided resource"
+642 ) from e
+
+
+
+
Convenient helpers that takes an S3 resource as input and returns the settings necessary to
+initiate an S3 connection using boto3
+
+
+
+
+
+
+
+
+
def
+
load_s3_file ( self , s3object : wmill.s3_types.S3Object | str , s3_resource_path : str | None ) -> bytes :
+
+
View Source
+
+
+
+
644 def load_s3_file ( self , s3object : S3Object | str , s3_resource_path : str | None ) -> bytes :
+645 """
+646 Load a file from the workspace s3 bucket and returns its content as bytes.
+647
+648 '''python
+649 from wmill import S3Object
+650
+651 s3_obj = S3Object(s3="/path/to/my_file.txt")
+652 my_obj_content = client.load_s3_file(s3_obj)
+653 file_content = my_obj_content.decode("utf-8")
+654 '''
+655 """
+656 s3object = parse_s3_object ( s3object )
+657 with self . load_s3_file_reader ( s3object , s3_resource_path ) as file_reader :
+658 return file_reader . read ()
+
+
+
+
Load a file from the workspace s3 bucket and returns its content as bytes.
+
+
'''python
+from wmill import S3Object
+
+
s3_obj = S3Object(s3="/path/to/my_file.txt")
+my_obj_content = client.load_s3_file(s3_obj)
+file_content = my_obj_content.decode("utf-8")
+'''
+
+
+
+
+
+
+
+
+
def
+
load_s3_file_reader ( self , s3object : wmill.s3_types.S3Object | str , s3_resource_path : str | None ) -> _io . BufferedReader :
+
+
View Source
+
+
+
+
660 def load_s3_file_reader (
+661 self , s3object : S3Object | str , s3_resource_path : str | None
+662 ) -> BufferedReader :
+663 """
+664 Load a file from the workspace s3 bucket and returns the bytes stream.
+665
+666 '''python
+667 from wmill import S3Object
+668
+669 s3_obj = S3Object(s3="/path/to/my_file.txt")
+670 with wmill.load_s3_file_reader(s3object, s3_resource_path) as file_reader:
+671 print(file_reader.read())
+672 '''
+673 """
+674 s3object = parse_s3_object ( s3object )
+675 reader = S3BufferedReader (
+676 f " { self . workspace } " ,
+677 self . client ,
+678 s3object [ "s3" ],
+679 s3_resource_path ,
+680 s3object [ "storage" ] if "storage" in s3object else None ,
+681 )
+682 return reader
+
+
+
+
Load a file from the workspace s3 bucket and returns the bytes stream.
+
+
'''python
+from wmill import S3Object
+
+
s3_obj = S3Object(s3="/path/to/my_file.txt")
+with wmill.load_s3_file_reader (s3object, s3_resource_path) as file_reader:
+ print(file_reader.read())
+'''
+
+
+
+
+
+
+
+
+
def
+
write_s3_file ( self , s3object : wmill.s3_types.S3Object | str | None , file_content : _io . BufferedReader | bytes , s3_resource_path : str | None , content_type : str | None = None , content_disposition : str | None = None ) -> wmill.s3_types.S3Object :
+
+
View Source
+
+
+
+
684 def write_s3_file (
+685 self ,
+686 s3object : S3Object | str | None ,
+687 file_content : BufferedReader | bytes ,
+688 s3_resource_path : str | None ,
+689 content_type : str | None = None ,
+690 content_disposition : str | None = None ,
+691 ) -> S3Object :
+692 """
+693 Write a file to the workspace S3 bucket
+694
+695 '''python
+696 from wmill import S3Object
+697
+698 s3_obj = S3Object(s3="/path/to/my_file.txt")
+699
+700 # for an in memory bytes array:
+701 file_content = b'Hello Windmill!'
+702 client.write_s3_file(s3_obj, file_content)
+703
+704 # for a file:
+705 with open("my_file.txt", "rb") as my_file:
+706 client.write_s3_file(s3_obj, my_file)
+707 '''
+708 """
+709 s3object = parse_s3_object ( s3object )
+710 # httpx accepts either bytes or "a bytes generator" as content. If it's a BufferedReader, we need to convert it to a generator
+711 if isinstance ( file_content , BufferedReader ):
+712 content_payload = bytes_generator ( file_content )
+713 elif isinstance ( file_content , bytes ):
+714 content_payload = file_content
+715 else :
+716 raise Exception ( "Type of file_content not supported" )
+717
+718 query_params = {}
+719 if s3object is not None and s3object [ "s3" ] != "" :
+720 query_params [ "file_key" ] = s3object [ "s3" ]
+721 if s3_resource_path is not None and s3_resource_path != "" :
+722 query_params [ "s3_resource_path" ] = s3_resource_path
+723 if (
+724 s3object is not None
+725 and "storage" in s3object
+726 and s3object [ "storage" ] is not None
+727 ):
+728 query_params [ "storage" ] = s3object [ "storage" ]
+729 if content_type is not None :
+730 query_params [ "content_type" ] = content_type
+731 if content_disposition is not None :
+732 query_params [ "content_disposition" ] = content_disposition
+733
+734 try :
+735 # need a vanilla client b/c content-type is not application/json here
+736 response = httpx . post (
+737 f " { self . base_url } /w/ { self . workspace } /job_helpers/upload_s3_file" ,
+738 headers = {
+739 "Authorization" : f "Bearer { self . token } " ,
+740 "Content-Type" : "application/octet-stream" ,
+741 },
+742 params = query_params ,
+743 content = content_payload ,
+744 verify = self . verify ,
+745 timeout = None ,
+746 ) . json ()
+747 except Exception as e :
+748 raise Exception ( "Could not write file to S3" ) from e
+749 return S3Object ( s3 = response [ "file_key" ])
+
+
+
+
Write a file to the workspace S3 bucket
+
+
'''python
+from wmill import S3Object
+
+
s3_obj = S3Object(s3="/path/to/my_file.txt")
+
+
for an in memory bytes array:
+
+
file_content = b'Hello Windmill!'
+client.write_s3_file(s3_obj, file_content)
+
+
for a file:
+
+
with open("my_file.txt", "rb") as my_file:
+ client.write_s3_file(s3_obj, my_file)
+'''
+
+
+
+
+
+
+
+
+
751 def sign_s3_objects ( self , s3_objects : list [ S3Object | str ]) -> list [ S3Object ]:
+752 return self . post (
+753 f "/w/ { self . workspace } /apps/sign_s3_objects" , json = { "s3_objects" : list ( map ( parse_s3_object , s3_objects ))}
+754 ) . json ()
+
+
+
+
+
+
+
+
+
+
+
756 def sign_s3_object ( self , s3_object : S3Object | str ) -> S3Object :
+757 return self . post (
+758 f "/w/ { self . workspace } /apps/sign_s3_objects" ,
+759 json = { "s3_objects" : [ s3_object ]},
+760 ) . json ()[ 0 ]
+
+
+
+
+
+
+
+
+
+
+ def
+ whoami (self ) -> dict :
+
+ View Source
+
+
+
+
777 def whoami ( self ) -> dict :
+778 return self . get ( "/users/whoami" ) . json ()
+
+
+
+
+
+
+
+
+
+ user : dict
+
+ View Source
+
+
+
+
780 @property
+781 def user ( self ) -> dict :
+782 return self . whoami ()
+
+
+
+
+
+
+
+
+
+ state_path : str
+
+ View Source
+
+
+
+
784 @property
+785 def state_path ( self ) -> str :
+786 state_path = os . environ . get (
+787 "WM_STATE_PATH_NEW" , os . environ . get ( "WM_STATE_PATH" )
+788 )
+789 if state_path is None :
+790 raise Exception ( "State path not found" )
+791 return state_path
+
+
+
+
+
+
+
+
+
+ state : Any
+
+ View Source
+
+
+
+
793 @property
+794 def state ( self ) -> Any :
+795 return self . get_resource ( path = self . state_path , none_if_undefined = True )
+
+
+
+
+
+
+
+
+
+
@staticmethod
+
+
def
+
set_shared_state_pickle (value : Any , path : str = 'state.pickle' ) -> None :
+
+
View Source
+
+
+
+
801 @staticmethod
+802 def set_shared_state_pickle ( value : Any , path : str = "state.pickle" ) -> None :
+803 """
+804 Set the state in the shared folder using pickle
+805 """
+806 import pickle
+807
+808 with open ( f "/shared/ { path } " , "wb" ) as handle :
+809 pickle . dump ( value , handle , protocol = pickle . HIGHEST_PROTOCOL )
+
+
+
+
Set the state in the shared folder using pickle
+
+
+
+
+
+
+
+
@staticmethod
+
+
def
+
get_shared_state_pickle (path : str = 'state.pickle' ) -> Any :
+
+
View Source
+
+
+
+
811 @staticmethod
+812 def get_shared_state_pickle ( path : str = "state.pickle" ) -> Any :
+813 """
+814 Get the state in the shared folder using pickle
+815 """
+816 import pickle
+817
+818 with open ( f "/shared/ { path } " , "rb" ) as handle :
+819 return pickle . load ( handle )
+
+
+
+
Get the state in the shared folder using pickle
+
+
+
+
+
+
+
+
@staticmethod
+
+
def
+
set_shared_state (value : Any , path : str = 'state.json' ) -> None :
+
+
View Source
+
+
+
+
821 @staticmethod
+822 def set_shared_state ( value : Any , path : str = "state.json" ) -> None :
+823 """
+824 Set the state in the shared folder using pickle
+825 """
+826 import json
+827
+828 with open ( f "/shared/ { path } " , "w" , encoding = "utf-8" ) as f :
+829 json . dump ( value , f , ensure_ascii = False , indent = 4 )
+
+
+
+
Set the state in the shared folder using pickle
+
+
+
+
+
+
+
+
@staticmethod
+
+
def
+
get_shared_state (path : str = 'state.json' ) -> None :
+
+
View Source
+
+
+
+
831 @staticmethod
+832 def get_shared_state ( path : str = "state.json" ) -> None :
+833 """
+834 Get the state in the shared folder using pickle
+835 """
+836 import json
+837
+838 with open ( f "/shared/ { path } " , "r" , encoding = "utf-8" ) as f :
+839 return json . load ( f )
+
+
+
+
Get the state in the shared folder using pickle
+
+
+
+
+
+
+
+
+ def
+ get_resume_urls (self , approver : str = None ) -> dict :
+
+ View Source
+
+
+
+
841 def get_resume_urls ( self , approver : str = None ) -> dict :
+842 nonce = random . randint ( 0 , 1000000000 )
+843 job_id = os . environ . get ( "WM_JOB_ID" ) or "NO_ID"
+844 return self . get (
+845 f "/w/ { self . workspace } /jobs/resume_urls/ { job_id } / { nonce } " ,
+846 params = { "approver" : approver },
+847 ) . json ()
+
+
+
+
+
+
+
+
+
+
+ def
+ request_interactive_slack_approval ( self , slack_resource_path : str , channel_id : str , message : str = None , approver : str = None , default_args_json : dict = None , dynamic_enums_json : dict = None ) -> None :
+
+ View Source
+
+
+
+
849 def request_interactive_slack_approval (
+850 self ,
+851 slack_resource_path : str ,
+852 channel_id : str ,
+853 message : str = None ,
+854 approver : str = None ,
+855 default_args_json : dict = None ,
+856 dynamic_enums_json : dict = None ,
+857 ) -> None :
+858 """
+859 Sends an interactive approval request via Slack, allowing optional customization of the message, approver, and form fields.
+860
+861 **[Enterprise Edition Only]** To include form fields in the Slack approval request, use the "Advanced -> Suspend -> Form" functionality.
+862 Learn more at: https://www.windmill.dev/docs/flows/flow_approval#form
+863
+864 :param slack_resource_path: The path to the Slack resource in Windmill.
+865 :type slack_resource_path: str
+866 :param channel_id: The Slack channel ID where the approval request will be sent.
+867 :type channel_id: str
+868 :param message: Optional custom message to include in the Slack approval request.
+869 :type message: str, optional
+870 :param approver: Optional user ID or name of the approver for the request.
+871 :type approver: str, optional
+872 :param default_args_json: Optional dictionary defining or overriding the default arguments for form fields.
+873 :type default_args_json: dict, optional
+874 :param dynamic_enums_json: Optional dictionary overriding the enum default values of enum form fields.
+875 :type dynamic_enums_json: dict, optional
+876
+877 :raises Exception: If the function is not called within a flow or flow preview.
+878 :raises Exception: If the required flow job or flow step environment variables are not set.
+879
+880 :return: None
+881
+882 **Usage Example:**
+883 >>> client.request_interactive_slack_approval(
+884 ... slack_resource_path="/u/alex/my_slack_resource",
+885 ... channel_id="admins-slack-channel",
+886 ... message="Please approve this request",
+887 ... approver="approver123",
+888 ... default_args_json={"key1": "value1", "key2": 42},
+889 ... dynamic_enums_json={"foo": ["choice1", "choice2"], "bar": ["optionA", "optionB"]},
+890 ... )
+891
+892 **Notes:**
+893 - This function must be executed within a Windmill flow or flow preview.
+894 - The function checks for required environment variables (`WM_FLOW_JOB_ID`, `WM_FLOW_STEP_ID`) to ensure it is run in the appropriate context.
+895 """
+896 workspace = self . workspace
+897 flow_job_id = os . environ . get ( "WM_FLOW_JOB_ID" )
+898
+899 if not flow_job_id :
+900 raise Exception (
+901 "You can't use 'request_interactive_slack_approval' function in a standalone script or flow step preview. Please use it in a flow or a flow preview."
+902 )
+903
+904 # Only include non-empty parameters
+905 params = {}
+906 if message :
+907 params [ "message" ] = message
+908 if approver :
+909 params [ "approver" ] = approver
+910 if slack_resource_path :
+911 params [ "slack_resource_path" ] = slack_resource_path
+912 if channel_id :
+913 params [ "channel_id" ] = channel_id
+914 if os . environ . get ( "WM_FLOW_STEP_ID" ):
+915 params [ "flow_step_id" ] = os . environ . get ( "WM_FLOW_STEP_ID" )
+916 if default_args_json :
+917 params [ "default_args_json" ] = json . dumps ( default_args_json )
+918 if dynamic_enums_json :
+919 params [ "dynamic_enums_json" ] = json . dumps ( dynamic_enums_json )
+920
+921 self . get (
+922 f "/w/ { workspace } /jobs/slack_approval/ { os . environ . get ( 'WM_JOB_ID' , 'NO_JOB_ID' ) } " ,
+923 params = params ,
+924 )
+
+
+
+
Sends an interactive approval request via Slack, allowing optional customization of the message, approver, and form fields.
+
+
[Enterprise Edition Only] To include form fields in the Slack approval request, use the "Advanced -> Suspend -> Form" functionality.
+Learn more at: https://www.windmill.dev/docs/flows/flow_approval#form
+
+
Parameters
+
+
+slack_resource_path : The path to the Slack resource in Windmill.
+channel_id : The Slack channel ID where the approval request will be sent.
+message : Optional custom message to include in the Slack approval request.
+approver : Optional user ID or name of the approver for the request.
+default_args_json : Optional dictionary defining or overriding the default arguments for form fields.
+dynamic_enums_json : Optional dictionary overriding the enum default values of enum form fields.
+
+
+
Raises
+
+
+Exception : If the function is not called within a flow or flow preview.
+Exception : If the required flow job or flow step environment variables are not set.
+
+
+
Returns
+
+
+ None
+
+
+
Usage Example:
+
+
+
+
+ client.request_interactive_slack_approval(
+ ... slack_resource_path="/u/alex/my_slack_resource",
+ ... channel_id="admins-slack-channel",
+ ... message="Please approve this request",
+ ... approver="approver123",
+ ... default_args_json={"key1": "value1", "key2": 42},
+ ... dynamic_enums_json={"foo": ["choice1", "choice2"], "bar": ["optionA", "optionB"]},
+ ... )
+
+
+
+
+
Notes:
+
+
+This function must be executed within a Windmill flow or flow preview.
+The function checks for required environment variables (WM_FLOW_JOB_ID, WM_FLOW_STEP_ID) to ensure it is run in the appropriate context.
+
+
+
+
+
+
+
+
+
+ def
+ username_to_email (self , username : str ) -> str :
+
+ View Source
+
+
+
+
926 def username_to_email ( self , username : str ) -> str :
+927 """
+928 Get email from workspace username
+929 This method is particularly useful for apps that require the email address of the viewer.
+930 Indeed, in the viewer context WM_USERNAME is set to the username of the viewer but WM_EMAIL is set to the email of the creator of the app.
+931 """
+932 return self . get ( f "/w/ { self . workspace } /users/username_to_email/ { username } " ) . text
+
+
+
+
Get email from workspace username
+This method is particularly useful for apps that require the email address of the viewer.
+Indeed, in the viewer context WM_USERNAME is set to the username of the viewer but WM_EMAIL is set to the email of the creator of the app.
+
+
+
+
+
+
+
+
+ def
+ send_teams_message ( self , conversation_id : str , text : str , success : bool = True , card_block : dict = None ):
+
+ View Source
+
+
+
+
934 def send_teams_message (
+935 self ,
+936 conversation_id : str ,
+937 text : str ,
+938 success : bool = True ,
+939 card_block : dict = None ,
+940 ):
+941 """
+942 Send a message to a Microsoft Teams conversation with conversation_id, where success is used to style the message
+943 """
+944 return self . post (
+945 f "/teams/activities" ,
+946 json = {
+947 "conversation_id" : conversation_id ,
+948 "text" : text ,
+949 "success" : success ,
+950 "card_block" : card_block ,
+951 },
+952 )
+
+
+
+
Send a message to a Microsoft Teams conversation with conversation_id, where success is used to style the message
+
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_workspace () -> str :
+
+
View Source
+
+
+
+ 984 @init_global_client
+985 def get_workspace () -> str :
+986 return _client . workspace
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_root_job_id (job_id : str | None = None ) -> str :
+
+
View Source
+
+
+
+ 989 @init_global_client
+990 def get_root_job_id ( job_id : str | None = None ) -> str :
+991 return _client . get_root_job_id ( job_id )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
@deprecate('Windmill().version')
+
+
def
+
get_version () -> str :
+
+
View Source
+
+
+
+ 994 @init_global_client
+995 @deprecate ( "Windmill().version" )
+996 def get_version () -> str :
+997 return _client . version
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
run_script_async ( hash_or_path : str , args : Dict [ str , Any ] = None , scheduled_in_secs : int = None ) -> str :
+
+
View Source
+
+
+
+ 1000 @init_global_client
+1001 def run_script_async (
+1002 hash_or_path : str ,
+1003 args : Dict [ str , Any ] = None ,
+1004 scheduled_in_secs : int = None ,
+1005 ) -> str :
+1006 is_path = "/" in hash_or_path
+1007 hash_ = None if is_path else hash_or_path
+1008 path = hash_or_path if is_path else None
+1009 return _client . run_script_async (
+1010 hash_ = hash_ ,
+1011 path = path ,
+1012 args = args ,
+1013 scheduled_in_secs = scheduled_in_secs ,
+1014 )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
run_flow_async ( path : str , args : Dict [ str , Any ] = None , scheduled_in_secs : int = None , do_not_track_in_parent : bool = True ) -> str :
+
+
View Source
+
+
+
+ 1017 @init_global_client
+1018 def run_flow_async (
+1019 path : str ,
+1020 args : Dict [ str , Any ] = None ,
+1021 scheduled_in_secs : int = None ,
+1022 # can only be set to false if this the job will be fully await and not concurrent with any other job
+1023 # as otherwise the child flow and its own child will store their state in the parent job which will
+1024 # lead to incorrectness and failures
+1025 do_not_track_in_parent : bool = True ,
+1026 ) -> str :
+1027 return _client . run_flow_async (
+1028 path = path ,
+1029 args = args ,
+1030 scheduled_in_secs = scheduled_in_secs ,
+1031 do_not_track_in_parent = do_not_track_in_parent ,
+1032 )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
run_script_sync ( hash : str , args : Dict [ str , Any ] = None , verbose : bool = False , assert_result_is_not_none : bool = True , cleanup : bool = True , timeout : datetime . timedelta = None ) -> Any :
+
+
View Source
+
+
+
+ 1035 @init_global_client
+1036 def run_script_sync (
+1037 hash : str ,
+1038 args : Dict [ str , Any ] = None ,
+1039 verbose : bool = False ,
+1040 assert_result_is_not_none : bool = True ,
+1041 cleanup : bool = True ,
+1042 timeout : dt . timedelta = None ,
+1043 ) -> Any :
+1044 return _client . run_script (
+1045 hash_ = hash ,
+1046 args = args ,
+1047 verbose = verbose ,
+1048 assert_result_is_not_none = assert_result_is_not_none ,
+1049 cleanup = cleanup ,
+1050 timeout = timeout ,
+1051 )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
run_script_by_path_async ( path : str , args : Dict [ str , Any ] = None , scheduled_in_secs : Optional [ int ] = None ) -> str :
+
+
View Source
+
+
+
+ 1054 @init_global_client
+1055 def run_script_by_path_async (
+1056 path : str ,
+1057 args : Dict [ str , Any ] = None ,
+1058 scheduled_in_secs : Union [ None , int ] = None ,
+1059 ) -> str :
+1060 return _client . run_script_by_path_async (
+1061 path = path ,
+1062 args = args ,
+1063 scheduled_in_secs = scheduled_in_secs ,
+1064 )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
run_script_by_hash_async ( hash_ : str , args : Dict [ str , Any ] = None , scheduled_in_secs : Optional [ int ] = None ) -> str :
+
+
View Source
+
+
+
+ 1067 @init_global_client
+1068 def run_script_by_hash_async (
+1069 hash_ : str ,
+1070 args : Dict [ str , Any ] = None ,
+1071 scheduled_in_secs : Union [ None , int ] = None ,
+1072 ) -> str :
+1073 return _client . run_script_by_hash_async (
+1074 hash_ = hash_ ,
+1075 args = args ,
+1076 scheduled_in_secs = scheduled_in_secs ,
+1077 )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
run_script_by_path_sync ( path : str , args : Dict [ str , Any ] = None , verbose : bool = False , assert_result_is_not_none : bool = True , cleanup : bool = True , timeout : datetime . timedelta = None ) -> Any :
+
+
View Source
+
+
+
+ 1080 @init_global_client
+1081 def run_script_by_path_sync (
+1082 path : str ,
+1083 args : Dict [ str , Any ] = None ,
+1084 verbose : bool = False ,
+1085 assert_result_is_not_none : bool = True ,
+1086 cleanup : bool = True ,
+1087 timeout : dt . timedelta = None ,
+1088 ) -> Any :
+1089 return _client . run_script (
+1090 path = path ,
+1091 args = args ,
+1092 verbose = verbose ,
+1093 assert_result_is_not_none = assert_result_is_not_none ,
+1094 cleanup = cleanup ,
+1095 timeout = timeout ,
+1096 )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_id_token (audience : str ) -> str :
+
+
View Source
+
+
+
+ 1099 @init_global_client
+1100 def get_id_token ( audience : str ) -> str :
+1101 """
+1102 Get a JWT token for the given audience for OIDC purposes to login into third parties like AWS, Vault, GCP, etc.
+1103 """
+1104 return _client . get_id_token ( audience )
+
+
+
+ Get a JWT token for the given audience for OIDC purposes to login into third parties like AWS, Vault, GCP, etc.
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_job_status (job_id : str ) -> Literal [ 'RUNNING' , 'WAITING' , 'COMPLETED' ] :
+
+
View Source
+
+
+
+ 1107 @init_global_client
+1108 def get_job_status ( job_id : str ) -> JobStatus :
+1109 return _client . get_job_status ( job_id )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_result (job_id : str , assert_result_is_not_none = True ) -> Dict [ str , Any ] :
+
+
View Source
+
+
+
+ 1112 @init_global_client
+1113 def get_result ( job_id : str , assert_result_is_not_none = True ) -> Dict [ str , Any ]:
+1114 return _client . get_result (
+1115 job_id = job_id , assert_result_is_not_none = assert_result_is_not_none
+1116 )
+
+
+
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
load_s3_file ( s3object : wmill.s3_types.S3Object | str , s3_resource_path : str | None = None ) -> bytes :
+
+
View Source
+
+
+
+ 1146 @init_global_client
+1147 def load_s3_file ( s3object : S3Object | str , s3_resource_path : str | None = None ) -> bytes :
+1148 """
+1149 Load the entire content of a file stored in S3 as bytes
+1150 """
+1151 return _client . load_s3_file (
+1152 s3object , s3_resource_path if s3_resource_path != "" else None
+1153 )
+
+
+
+ Load the entire content of a file stored in S3 as bytes
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
load_s3_file_reader ( s3object : wmill.s3_types.S3Object | str , s3_resource_path : str | None = None ) -> _io . BufferedReader :
+
+
View Source
+
+
+
+ 1156 @init_global_client
+1157 def load_s3_file_reader (
+1158 s3object : S3Object | str , s3_resource_path : str | None = None
+1159 ) -> BufferedReader :
+1160 """
+1161 Load the content of a file stored in S3
+1162 """
+1163 return _client . load_s3_file_reader (
+1164 s3object , s3_resource_path if s3_resource_path != "" else None
+1165 )
+
+
+
+ Load the content of a file stored in S3
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
write_s3_file ( s3object : wmill.s3_types.S3Object | str | None , file_content : _io . BufferedReader | bytes , s3_resource_path : str | None = None , content_type : str | None = None , content_disposition : str | None = None ) -> wmill.s3_types.S3Object :
+
+
View Source
+
+
+
+ 1168 @init_global_client
+1169 def write_s3_file (
+1170 s3object : S3Object | str | None ,
+1171 file_content : BufferedReader | bytes ,
+1172 s3_resource_path : str | None = None ,
+1173 content_type : str | None = None ,
+1174 content_disposition : str | None = None ,
+1175 ) -> S3Object :
+1176 """
+1177 Upload a file to S3
+1178
+1179 Content type will be automatically guessed from path extension if left empty
+1180
+1181 See MDN for content_disposition: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
+1182 and content_type: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
+1183
+1184 """
+1185 return _client . write_s3_file (
+1186 s3object ,
+1187 file_content ,
+1188 s3_resource_path if s3_resource_path != "" else None ,
+1189 content_type ,
+1190 content_disposition ,
+1191 )
+
+
+
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
whoami () -> dict :
+
+
View Source
+
+
+
+ 1212 @init_global_client
+1213 def whoami () -> dict :
+1214 """
+1215 Returns the current user
+1216 """
+1217 return _client . user
+
+
+
+ Returns the current user
+
+
+
+
+
+
+
+
@init_global_client
+
@deprecate('Windmill().state')
+
+
def
+
get_state () -> Any :
+
+
View Source
+
+
+
+ 1220 @init_global_client
+1221 @deprecate ( "Windmill().state" )
+1222 def get_state () -> Any :
+1223 """
+1224 Get the state
+1225 """
+1226 return _client . state
+
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_resource (path : str , none_if_undefined : bool = False ) -> dict | None :
+
+
View Source
+
+
+
+ 1229 @init_global_client
+1230 def get_resource (
+1231 path : str ,
+1232 none_if_undefined : bool = False ,
+1233 ) -> dict | None :
+1234 """Get resource from Windmill"""
+1235 return _client . get_resource ( path , none_if_undefined )
+
+
+
+ Get resource from Windmill
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
set_resource (path : str , value : Any , resource_type : str = 'any' ) -> None :
+
+
View Source
+
+
+
+ 1238 @init_global_client
+1239 def set_resource ( path : str , value : Any , resource_type : str = "any" ) -> None :
+1240 """
+1241 Set the resource at a given path as a string, creating it if it does not exist
+1242 """
+1243 return _client . set_resource ( value = value , path = path , resource_type = resource_type )
+
+
+
+ Set the resource at a given path as a string, creating it if it does not exist
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
set_state (value : Any ) -> None :
+
+
View Source
+
+
+
+ 1246 @init_global_client
+1247 def set_state ( value : Any ) -> None :
+1248 """
+1249 Set the state
+1250 """
+1251 return _client . set_state ( value )
+
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
set_progress (value : int , job_id : Optional [ str ] = None ) -> None :
+
+
View Source
+
+
+
+ 1254 @init_global_client
+1255 def set_progress ( value : int , job_id : Optional [ str ] = None ) -> None :
+1256 """
+1257 Set the progress
+1258 """
+1259 return _client . set_progress ( value , job_id )
+
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_progress (job_id : Optional [ str ] = None ) -> Any :
+
+
View Source
+
+
+
+ 1262 @init_global_client
+1263 def get_progress ( job_id : Optional [ str ] = None ) -> Any :
+1264 """
+1265 Get the progress
+1266 """
+1267 return _client . get_progress ( job_id )
+
+
+
+
+
+
+
+
+
+
+
+ def
+ set_shared_state_pickle (value : Any , path = 'state.pickle' ) -> None :
+
+ View Source
+
+
+
+ 1270 def set_shared_state_pickle ( value : Any , path = "state.pickle" ) -> None :
+1271 """
+1272 Set the state in the shared folder using pickle
+1273 """
+1274 return Windmill . set_shared_state_pickle ( value = value , path = path )
+
+
+
+ Set the state in the shared folder using pickle
+
+
+
+
+
+
+
+
@deprecate('Windmill.get_shared_state_pickle(...)')
+
+
def
+
get_shared_state_pickle (path = 'state.pickle' ) -> Any :
+
+
View Source
+
+
+
+ 1277 @deprecate ( "Windmill.get_shared_state_pickle(...)" )
+1278 def get_shared_state_pickle ( path = "state.pickle" ) -> Any :
+1279 """
+1280 Get the state in the shared folder using pickle
+1281 """
+1282 return Windmill . get_shared_state_pickle ( path = path )
+
+
+
+ Get the state in the shared folder using pickle
+
+
+
+
+
+
+
+
+ def
+ set_shared_state (value : Any , path = 'state.json' ) -> None :
+
+ View Source
+
+
+
+ 1285 def set_shared_state ( value : Any , path = "state.json" ) -> None :
+1286 """
+1287 Set the state in the shared folder using pickle
+1288 """
+1289 return Windmill . set_shared_state ( value = value , path = path )
+
+
+
+ Set the state in the shared folder using pickle
+
+
+
+
+
+
+
+
+ def
+ get_shared_state (path = 'state.json' ) -> None :
+
+ View Source
+
+
+
+ 1292 def get_shared_state ( path = "state.json" ) -> None :
+1293 """
+1294 Get the state in the shared folder using pickle
+1295 """
+1296 return Windmill . get_shared_state ( path = path )
+
+
+
+ Get the state in the shared folder using pickle
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_variable (path : str ) -> str :
+
+
View Source
+
+
+
+ 1299 @init_global_client
+1300 def get_variable ( path : str ) -> str :
+1301 """
+1302 Returns the variable at a given path as a string
+1303 """
+1304 return _client . get_variable ( path )
+
+
+
+ Returns the variable at a given path as a string
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
set_variable (path : str , value : str , is_secret : bool = False ) -> None :
+
+
View Source
+
+
+
+ 1307 @init_global_client
+1308 def set_variable ( path : str , value : str , is_secret : bool = False ) -> None :
+1309 """
+1310 Set the variable at a given path as a string, creating it if it does not exist
+1311 """
+1312 return _client . set_variable ( path , value , is_secret )
+
+
+
+ Set the variable at a given path as a string, creating it if it does not exist
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_flow_user_state (key : str ) -> Any :
+
+
View Source
+
+
+
+ 1315 @init_global_client
+1316 def get_flow_user_state ( key : str ) -> Any :
+1317 """
+1318 Get the user state of a flow at a given key
+1319 """
+1320 return _client . get_flow_user_state ( key )
+
+
+
+ Get the user state of a flow at a given key
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
set_flow_user_state (key : str , value : Any ) -> None :
+
+
View Source
+
+
+
+ 1323 @init_global_client
+1324 def set_flow_user_state ( key : str , value : Any ) -> None :
+1325 """
+1326 Set the user state of a flow at a given key
+1327 """
+1328 return _client . set_flow_user_state ( key , value )
+
+
+
+ Set the user state of a flow at a given key
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_state_path () -> str :
+
+
View Source
+
+
+
+ 1331 @init_global_client
+1332 def get_state_path () -> str :
+1333 return _client . state_path
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
get_resume_urls (approver : str = None ) -> dict :
+
+
View Source
+
+
+
+ 1336 @init_global_client
+1337 def get_resume_urls ( approver : str = None ) -> dict :
+1338 return _client . get_resume_urls ( approver )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
request_interactive_slack_approval ( slack_resource_path : str , channel_id : str , message : str = None , approver : str = None , default_args_json : dict = None , dynamic_enums_json : dict = None ) -> None :
+
+
View Source
+
+
+
+ 1341 @init_global_client
+1342 def request_interactive_slack_approval (
+1343 slack_resource_path : str ,
+1344 channel_id : str ,
+1345 message : str = None ,
+1346 approver : str = None ,
+1347 default_args_json : dict = None ,
+1348 dynamic_enums_json : dict = None ,
+1349 ) -> None :
+1350 return _client . request_interactive_slack_approval (
+1351 slack_resource_path = slack_resource_path ,
+1352 channel_id = channel_id ,
+1353 message = message ,
+1354 approver = approver ,
+1355 default_args_json = default_args_json ,
+1356 dynamic_enums_json = dynamic_enums_json ,
+1357 )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
send_teams_message ( conversation_id : str , text : str , success : bool , card_block : dict = None ):
+
+
View Source
+
+
+
+ 1360 @init_global_client
+1361 def send_teams_message (
+1362 conversation_id : str , text : str , success : bool , card_block : dict = None
+1363 ):
+1364 return _client . send_teams_message ( conversation_id , text , success , card_block )
+
+
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
cancel_running () -> dict :
+
+
View Source
+
+
+
+ 1367 @init_global_client
+1368 def cancel_running () -> dict :
+1369 """Cancel currently running executions of the same script."""
+1370 return _client . cancel_running ()
+
+
+
+ Cancel currently running executions of the same script.
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
run_script ( path : str = None , hash_ : str = None , args : dict = None , timeout : datetime . timedelta | int | float = None , verbose : bool = False , cleanup : bool = True , assert_result_is_not_none : bool = True ) -> Any :
+
+
View Source
+
+
+
+ 1373 @init_global_client
+1374 def run_script (
+1375 path : str = None ,
+1376 hash_ : str = None ,
+1377 args : dict = None ,
+1378 timeout : dt . timedelta | int | float = None ,
+1379 verbose : bool = False ,
+1380 cleanup : bool = True ,
+1381 assert_result_is_not_none : bool = True ,
+1382 ) -> Any :
+1383 """Run script synchronously and return its result.
+1384
+1385 .. deprecated:: Use run_script_by_path or run_script_by_hash instead.
+1386 """
+1387 return _client . run_script (
+1388 path = path ,
+1389 hash_ = hash_ ,
+1390 args = args ,
+1391 verbose = verbose ,
+1392 assert_result_is_not_none = assert_result_is_not_none ,
+1393 cleanup = cleanup ,
+1394 timeout = timeout ,
+1395 )
+
+
+
+ Run script synchronously and return its result.
+
+
Deprecated since version Use run_script_by_path or run_script_by_hash instead..
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
run_script_by_path ( path : str , args : dict = None , timeout : datetime . timedelta | int | float = None , verbose : bool = False , cleanup : bool = True , assert_result_is_not_none : bool = True ) -> Any :
+
+
View Source
+
+
+
+ 1398 @init_global_client
+1399 def run_script_by_path (
+1400 path : str ,
+1401 args : dict = None ,
+1402 timeout : dt . timedelta | int | float = None ,
+1403 verbose : bool = False ,
+1404 cleanup : bool = True ,
+1405 assert_result_is_not_none : bool = True ,
+1406 ) -> Any :
+1407 """Run script by path synchronously and return its result."""
+1408 return _client . run_script_by_path (
+1409 path = path ,
+1410 args = args ,
+1411 verbose = verbose ,
+1412 assert_result_is_not_none = assert_result_is_not_none ,
+1413 cleanup = cleanup ,
+1414 timeout = timeout ,
+1415 )
+
+
+
+ Run script by path synchronously and return its result.
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
run_script_by_hash ( hash_ : str , args : dict = None , timeout : datetime . timedelta | int | float = None , verbose : bool = False , cleanup : bool = True , assert_result_is_not_none : bool = True ) -> Any :
+
+
View Source
+
+
+
+ 1418 @init_global_client
+1419 def run_script_by_hash (
+1420 hash_ : str ,
+1421 args : dict = None ,
+1422 timeout : dt . timedelta | int | float = None ,
+1423 verbose : bool = False ,
+1424 cleanup : bool = True ,
+1425 assert_result_is_not_none : bool = True ,
+1426 ) -> Any :
+1427 """Run script by hash synchronously and return its result."""
+1428 return _client . run_script_by_hash (
+1429 hash_ = hash_ ,
+1430 args = args ,
+1431 verbose = verbose ,
+1432 assert_result_is_not_none = assert_result_is_not_none ,
+1433 cleanup = cleanup ,
+1434 timeout = timeout ,
+1435 )
+
+
+
+ Run script by hash synchronously and return its result.
+
+
+
+
+
+
+
+
@init_global_client
+
+
def
+
username_to_email (username : str ) -> str :
+
+
View Source
+
+
+
+ 1438 @init_global_client
+1439 def username_to_email ( username : str ) -> str :
+1440 """
+1441 Get email from workspace username
+1442 This method is particularly useful for apps that require the email address of the viewer.
+1443 Indeed, in the viewer context WM_USERNAME is set to the username of the viewer but WM_EMAIL is set to the email of the creator of the app.
+1444 """
+1445 return _client . username_to_email ( username )
+
+
+
+ Get email from workspace username
+This method is particularly useful for apps that require the email address of the viewer.
+Indeed, in the viewer context WM_USERNAME is set to the username of the viewer but WM_EMAIL is set to the email of the creator of the app.
+
+
+
+
+
+
+
+
+ def
+ task (* args , ** kwargs ):
+
+ View Source
+
+
+
+ 1448 def task ( * args , ** kwargs ):
+1449 from inspect import signature
+1450
+1451 def f ( func , tag : str | None = None ):
+1452 if (
+1453 os . environ . get ( "WM_JOB_ID" ) is None
+1454 or os . environ . get ( "MAIN_OVERRIDE" ) == func . __name__
+1455 ):
+1456
+1457 def inner ( * args , ** kwargs ):
+1458 return func ( * args , ** kwargs )
+1459
+1460 return inner
+1461 else :
+1462
+1463 def inner ( * args , ** kwargs ):
+1464 global _client
+1465 if _client is None :
+1466 _client = Windmill ()
+1467 w_id = os . environ . get ( "WM_WORKSPACE" )
+1468 job_id = os . environ . get ( "WM_JOB_ID" )
+1469 f_name = func . __name__
+1470 json = kwargs
+1471 params = list ( signature ( func ) . parameters )
+1472 for i , arg in enumerate ( args ):
+1473 if i < len ( params ):
+1474 p = params [ i ]
+1475 key = p
+1476 if key not in kwargs :
+1477 json [ key ] = arg
+1478
+1479 params = {}
+1480 if tag is not None :
+1481 params [ "tag" ] = tag
+1482 w_as_code_response = _client . post (
+1483 f "/w/ { w_id } /jobs/run/workflow_as_code/ { job_id } / { f_name } " ,
+1484 json = { "args" : json },
+1485 params = params ,
+1486 )
+1487 job_id = w_as_code_response . text
+1488 print ( f "Executing task { func . __name__ } on job { job_id } " )
+1489 job_result = _client . wait_job ( job_id )
+1490 print ( f "Task { func . __name__ } ( { job_id } ) completed" )
+1491 return job_result
+1492
+1493 return inner
+1494
+1495 if len ( args ) == 1 and len ( kwargs ) == 0 and callable ( args [ 0 ]):
+1496 return f ( args [ 0 ], None )
+1497 else :
+1498 return lambda x : f ( x , kwargs . get ( "tag" ))
+
+
+
+
+
+
+
+
+
+
+ def
+ parse_resource_syntax (s : str ) -> Optional [ str ] :
+
+ View Source
+
+
+
+ 1500 def parse_resource_syntax ( s : str ) -> Optional [ str ]:
+1501 """Parse resource syntax from string."""
+1502 if s is None :
+1503 return None
+1504 if s . startswith ( "$res:" ):
+1505 return s [ 5 :]
+1506 if s . startswith ( "res://" ):
+1507 return s [ 6 :]
+1508 return None
+
+
+
+ Parse resource syntax from string.
+
+
+
+
+
+
+
+
+
+ def
+ parse_variable_syntax (s : str ) -> Optional [ str ] :
+
+ View Source
+
+
+
+ 1522 def parse_variable_syntax ( s : str ) -> Optional [ str ]:
+1523 """Parse variable syntax from string."""
+1524 if s . startswith ( "var://" ):
+1525 return s [ 6 :]
+1526 return None
+
+
+
+ Parse variable syntax from string.
+
+
+
+
+
+
+
+
+ def
+ append_to_result_stream (text : str ) -> None :
+
+ View Source
+
+
+
+ 1529 def append_to_result_stream ( text : str ) -> None :
+1530 """Append a text to the result stream.
+1531
+1532 Args:
+1533 text: text to append to the result stream
+1534 """
+1535 print ( "WM_STREAM: {} " . format ( text . replace ( chr ( 10 ), ' \\ n' )))
+
+
+
+ Append a text to the result stream.
+
+
Args:
+ text: text to append to the result stream
+
+
+
+
+
+
+
+
+ def
+ stream_result (stream ) -> None :
+
+ View Source
+
+
+
+ 1537 def stream_result ( stream ) -> None :
+1538 """Stream to the result stream.
+1539
+1540 Args:
+1541 stream: stream to stream to the result stream
+1542 """
+1543 for text in stream :
+1544 append_to_result_stream ( text )
+
+
+
+ Stream to the result stream.
+
+
Args:
+ stream: stream to stream to the result stream
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python-client/docs/wmill/s3_reader.html b/python-client/docs/wmill/s3_reader.html
new file mode 100644
index 0000000000..8bacbc95d3
--- /dev/null
+++ b/python-client/docs/wmill/s3_reader.html
@@ -0,0 +1,550 @@
+
+
+
+
+
+
+ wmill.s3_reader API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+wmill .s3_reader
+
+
+
+
+ View Source
+
+ 1 from io import BufferedReader , BytesIO
+ 2 from typing import Optional , Union
+ 3
+ 4 import httpx
+ 5
+ 6
+ 7 class S3BufferedReader ( BufferedReader ):
+ 8 def __init__ ( self , workspace : str , windmill_client : httpx . Client , file_key : str , s3_resource_path : Optional [ str ], storage : Optional [ str ]):
+ 9 params = {
+10 "file_key" : file_key ,
+11 }
+12 if s3_resource_path is not None :
+13 params [ "s3_resource_path" ] = s3_resource_path
+14 if storage is not None :
+15 params [ "storage" ] = storage
+16 self . _context_manager = windmill_client . stream (
+17 "GET" ,
+18 f "/w/ { workspace } /job_helpers/download_s3_file" ,
+19 params = params ,
+20 timeout = None ,
+21 )
+22
+23 def __enter__ ( self ):
+24 reader = self . _context_manager . __enter__ ()
+25 if reader . status_code >= 400 :
+26 error_bytes = reader . read ()
+27 try :
+28 error_text = error_bytes . decode ( 'utf-8' )
+29 except UnicodeDecodeError :
+30 error_text = str ( error_bytes )
+31 raise httpx . HTTPStatusError (
+32 f "Failed to load S3 file: { reader . status_code } { reader . reason_phrase } - { error_text } " ,
+33 request = reader . request ,
+34 response = reader
+35 )
+36 self . _iterator = reader . iter_bytes ()
+37 return self
+38
+39 def peek ( self , size = 0 ):
+40 raise Exception ( "Not implemented, use read() instead" )
+41
+42 def read ( self , size =- 1 ):
+43 read_result = []
+44 if size < 0 :
+45 for b in self . _iterator :
+46 read_result . append ( b )
+47 else :
+48 for i in range ( size ):
+49 try :
+50 b = self . _iterator . __next__ ()
+51 except StopIteration :
+52 break
+53 read_result . append ( b )
+54
+55 return b "" . join ( read_result )
+56
+57 def read1 ( self , size =- 1 ):
+58 return self . read ( size )
+59
+60 def __exit__ ( self , * args ):
+61 self . _context_manager . __exit__ ( * args )
+62
+63
+64 def bytes_generator ( buffered_reader : Union [ BufferedReader , BytesIO ]):
+65 while True :
+66 byte = buffered_reader . read ( 50 * 1024 )
+67 if not byte :
+68 break
+69 yield byte
+
+
+
+
+
+
+
+
+ class
+ S3BufferedReader (_io.BufferedReader ):
+
+ View Source
+
+
+
+ 8 class S3BufferedReader ( BufferedReader ):
+ 9 def __init__ ( self , workspace : str , windmill_client : httpx . Client , file_key : str , s3_resource_path : Optional [ str ], storage : Optional [ str ]):
+10 params = {
+11 "file_key" : file_key ,
+12 }
+13 if s3_resource_path is not None :
+14 params [ "s3_resource_path" ] = s3_resource_path
+15 if storage is not None :
+16 params [ "storage" ] = storage
+17 self . _context_manager = windmill_client . stream (
+18 "GET" ,
+19 f "/w/ { workspace } /job_helpers/download_s3_file" ,
+20 params = params ,
+21 timeout = None ,
+22 )
+23
+24 def __enter__ ( self ):
+25 reader = self . _context_manager . __enter__ ()
+26 if reader . status_code >= 400 :
+27 error_bytes = reader . read ()
+28 try :
+29 error_text = error_bytes . decode ( 'utf-8' )
+30 except UnicodeDecodeError :
+31 error_text = str ( error_bytes )
+32 raise httpx . HTTPStatusError (
+33 f "Failed to load S3 file: { reader . status_code } { reader . reason_phrase } - { error_text } " ,
+34 request = reader . request ,
+35 response = reader
+36 )
+37 self . _iterator = reader . iter_bytes ()
+38 return self
+39
+40 def peek ( self , size = 0 ):
+41 raise Exception ( "Not implemented, use read() instead" )
+42
+43 def read ( self , size =- 1 ):
+44 read_result = []
+45 if size < 0 :
+46 for b in self . _iterator :
+47 read_result . append ( b )
+48 else :
+49 for i in range ( size ):
+50 try :
+51 b = self . _iterator . __next__ ()
+52 except StopIteration :
+53 break
+54 read_result . append ( b )
+55
+56 return b "" . join ( read_result )
+57
+58 def read1 ( self , size =- 1 ):
+59 return self . read ( size )
+60
+61 def __exit__ ( self , * args ):
+62 self . _context_manager . __exit__ ( * args )
+
+
+
+ Create a new buffered reader using the given readable raw IO object.
+
+
+
+
+
+
+
+ S3BufferedReader ( workspace : str , windmill_client : httpx . Client , file_key : str , s3_resource_path : Optional [ str ] , storage : Optional [ str ] )
+
+ View Source
+
+
+
+
9 def __init__ ( self , workspace : str , windmill_client : httpx . Client , file_key : str , s3_resource_path : Optional [ str ], storage : Optional [ str ]):
+10 params = {
+11 "file_key" : file_key ,
+12 }
+13 if s3_resource_path is not None :
+14 params [ "s3_resource_path" ] = s3_resource_path
+15 if storage is not None :
+16 params [ "storage" ] = storage
+17 self . _context_manager = windmill_client . stream (
+18 "GET" ,
+19 f "/w/ { workspace } /job_helpers/download_s3_file" ,
+20 params = params ,
+21 timeout = None ,
+22 )
+
+
+
+
+
+
+
+
+
+
+ def
+ peek (self , size = 0 ):
+
+ View Source
+
+
+
+
40 def peek ( self , size = 0 ):
+41 raise Exception ( "Not implemented, use read() instead" )
+
+
+
+
+
+
+
+
+
+
+ def
+ read (self , size =- 1 ):
+
+ View Source
+
+
+
+
43 def read ( self , size =- 1 ):
+44 read_result = []
+45 if size < 0 :
+46 for b in self . _iterator :
+47 read_result . append ( b )
+48 else :
+49 for i in range ( size ):
+50 try :
+51 b = self . _iterator . __next__ ()
+52 except StopIteration :
+53 break
+54 read_result . append ( b )
+55
+56 return b "" . join ( read_result )
+
+
+
+
Read and return up to n bytes.
+
+
If the argument is omitted, None, or negative, reads and
+returns all data until EOF.
+
+
If the argument is positive, and the underlying raw stream is
+not 'interactive', multiple raw reads may be issued to satisfy
+the byte count (unless EOF is reached first). But for
+interactive raw streams (as well as sockets and pipes), at most
+one raw read will be issued, and a short result does not imply
+that EOF is imminent.
+
+
Returns an empty bytes object on EOF.
+
+
Returns None if the underlying raw stream was open in non-blocking
+mode and no data is available at the moment.
+
+
+
+
+
+
+
+
+ def
+ read1 (self , size =- 1 ):
+
+ View Source
+
+
+
+
58 def read1 ( self , size =- 1 ):
+59 return self . read ( size )
+
+
+
+
Read and return up to n bytes, with at most one read() call
+to the underlying raw stream. A short result does not imply
+that EOF is imminent.
+
+
Returns an empty bytes object on EOF.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/python-client/docs/wmill/s3_types.html b/python-client/docs/wmill/s3_types.html
new file mode 100644
index 0000000000..be8528bc05
--- /dev/null
+++ b/python-client/docs/wmill/s3_types.html
@@ -0,0 +1,841 @@
+
+
+
+
+
+
+ wmill.s3_types API documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ class
+ S3Object (builtins.dict ):
+
+ View Source
+
+
+
+ 5 class S3Object ( dict ):
+ 6 s3 : str
+ 7 storage : Optional [ str ]
+ 8 presigned : Optional [ str ]
+ 9
+10 def __getattr__ ( self , attr ):
+11 return self [ attr ]
+
+
+
+
+
+
+
+ s3 : str
+
+
+
+
+
+
+
+
+
+
+ storage : Optional[str]
+
+
+
+
+
+
+
+
+
+
+ presigned : Optional[str]
+
+
+
+
+
+
+
+
+
+
+
+
+
+ class
+ S3FsClientKwargs (builtins.dict ):
+
+ View Source
+
+
+
+ 14 class S3FsClientKwargs ( dict ):
+15 region_name : str
+16
+17 def __getattr__ ( self , attr ):
+18 return self [ attr ]
+
+
+
+
+
+
+
+ region_name : str
+
+
+
+
+
+
+
+
+
+
+
+
+
+ class
+ S3FsArgs (builtins.dict ):
+
+ View Source
+
+
+
+ 21 class S3FsArgs ( dict ):
+22 endpoint_url : str
+23 key : str
+24 secret : str
+25 use_ssl : bool
+26 cache_regions : bool
+27 client_kwargs : S3FsClientKwargs
+28
+29 def __getattr__ ( self , attr ):
+30 return self [ attr ]
+
+
+
+
+
+
+
+ endpoint_url : str
+
+
+
+
+
+
+
+
+
+
+ key : str
+
+
+
+
+
+
+
+
+
+
+ secret : str
+
+
+
+
+
+
+
+
+
+
+ use_ssl : bool
+
+
+
+
+
+
+
+
+
+
+ cache_regions : bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ class
+ StorageOptions (builtins.dict ):
+
+ View Source
+
+
+
+ 33 class StorageOptions ( dict ):
+34 aws_endpoint_url : str
+35 aws_access_key_id : str
+36 aws_secret_access_key : str
+37 aws_region : str
+38 aws_allow_http : str
+39
+40 def __getattr__ ( self , attr ):
+41 return self [ attr ]
+
+
+
+
+
+
+
+ aws_endpoint_url : str
+
+
+
+
+
+
+
+
+
+
+ aws_access_key_id : str
+
+
+
+
+
+
+
+
+
+
+ aws_secret_access_key : str
+
+
+
+
+
+
+
+
+
+
+ aws_region : str
+
+
+
+
+
+
+
+
+
+
+ aws_allow_http : str
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ class
+ Boto3ConnectionSettings (builtins.dict ):
+
+ View Source
+
+
+
+ 52 class Boto3ConnectionSettings ( dict ):
+53 endpoint_url : str
+54 region_name : str
+55 use_ssl : bool
+56 aws_access_key_id : str
+57 aws_secret_access_key : str
+58
+59 def __getattr__ ( self , attr ):
+60 return self [ attr ]
+
+
+
+
+
+
+
+ endpoint_url : str
+
+
+
+
+
+
+
+
+
+
+ region_name : str
+
+
+
+
+
+
+
+
+
+
+ use_ssl : bool
+
+
+
+
+
+
+
+
+
+
+ aws_access_key_id : str
+
+
+
+
+
+
+
+
+
+
+ aws_secret_access_key : str
+
+
+
+
+
+
+
+
+
+
+
+
+
+ class
+ DuckDbConnectionSettings (builtins.dict ):
+
+ View Source
+
+
+
+ 63 class DuckDbConnectionSettings ( dict ):
+64 connection_settings_str : str
+65
+66 def __getattr__ ( self , attr ):
+67 return self [ attr ]
+
+
+
+
+
+
+
+ connection_settings_str : str
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From c4fff2165c2a2ff469309f6257533f14c07e9822 Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Wed, 15 Oct 2025 22:35:15 +0000
Subject: [PATCH 07/33] nit badges suspended
---
frontend/src/lib/components/runs/RunsQueue.svelte | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/src/lib/components/runs/RunsQueue.svelte b/frontend/src/lib/components/runs/RunsQueue.svelte
index 6967c92222..05e24f61c6 100644
--- a/frontend/src/lib/components/runs/RunsQueue.svelte
+++ b/frontend/src/lib/components/runs/RunsQueue.svelte
@@ -76,7 +76,7 @@
{/snippet}
0
- ? 'bg-yellow-500 text-white rounded-full w-6 h-6 flex center-center'
+ ? 'bg-yellow-500 text-white rounded-full min-w-6 h-6 flex center-center'
: ''}>{queue_count ? ($queue_count ?? 0).toFixed(0) : '...'}
@@ -102,7 +102,7 @@
{/snippet}
0
- ? 'bg-surface-secondary-inverse text-primary-inverse rounded-full w-6 h-6 flex center-center'
+ ? 'bg-surface-secondary-inverse text-primary-inverse rounded-full min-w-6 h-6 flex center-center'
: ''}>{suspended_count ? ($suspended_count ?? 0).toFixed(0) : '...'}
From 06c05200cd37e5d299768d7a1eda168e4d78b232 Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Wed, 15 Oct 2025 22:39:10 +0000
Subject: [PATCH 08/33] chore(main): release 1.560.0 (#6823)
* chore(main): release 1.560.0
* Apply automatic changes
---------
Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
---
CHANGELOG.md | 20 +++
backend/Cargo.lock | 133 ++++++++----------
backend/Cargo.toml | 4 +-
backend/windmill-api/openapi.yaml | 2 +-
benchmarks/lib.ts | 2 +-
cli/src/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, 99 insertions(+), 88 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 32ecd8fd76..bcac5dfdfa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog
+## [1.560.0](https://github.com/windmill-labs/windmill/compare/v1.559.0...v1.560.0) (2025-10-15)
+
+
+### Features
+
+* add support for zoho oauth ([#6809](https://github.com/windmill-labs/windmill/issues/6809)) ([9d9c29f](https://github.com/windmill-labs/windmill/commit/9d9c29fdfa15cc655854ec909dea944d10ce7374))
+* **backend:** use flow nodes opti for ai agent steps ([#6808](https://github.com/windmill-labs/windmill/issues/6808)) ([8d5acda](https://github.com/windmill-labs/windmill/commit/8d5acda340cd105c5b0dfc2bfe59b7e996bd2707))
+* build pydoc for wmill python client and mount in container image ([#6828](https://github.com/windmill-labs/windmill/issues/6828)) ([d75e9e3](https://github.com/windmill-labs/windmill/commit/d75e9e3d92d43f449a6296b367018f8fa3da6507))
+* **settings:** add unsaved changes warning for workspace settings ([#6813](https://github.com/windmill-labs/windmill/issues/6813)) ([cb88187](https://github.com/windmill-labs/windmill/commit/cb8818796ddd68d2b2ee1dea5f9b0a648f0c1ec9))
+
+
+### Bug Fixes
+
+* always create instance groups with uuid ([#6826](https://github.com/windmill-labs/windmill/issues/6826)) ([48acc57](https://github.com/windmill-labs/windmill/commit/48acc57823792c9e795f9735712e1b2ed6d2b4e2))
+* bug for loop flow inconsistent state ([#6815](https://github.com/windmill-labs/windmill/issues/6815)) ([2565222](https://github.com/windmill-labs/windmill/commit/256522273ee65b67075ac91408825b1c6e91ef06))
+* fix concurrency key filter ([892ce64](https://github.com/windmill-labs/windmill/commit/892ce64ea8550c22d65180c71f57c90a65583832))
+* gcp script picker ([#6837](https://github.com/windmill-labs/windmill/issues/6837)) ([d12c8f3](https://github.com/windmill-labs/windmill/commit/d12c8f34efe5ebbdbbf85ae41bb11307dc5d8ea3))
+* resource editor should not autoselect resources for optional fields ([#6821](https://github.com/windmill-labs/windmill/issues/6821)) ([85d1b8a](https://github.com/windmill-labs/windmill/commit/85d1b8a3e6af41bba93128ebcb88ada383ed2d65))
+* support dyn select for sub flow ([#6835](https://github.com/windmill-labs/windmill/issues/6835)) ([b211155](https://github.com/windmill-labs/windmill/commit/b211155784135b1377975a2759f2ddca1cffcea2))
+
## [1.559.0](https://github.com/windmill-labs/windmill/compare/v1.558.1...v1.559.0) (2025-10-14)
diff --git a/backend/Cargo.lock b/backend/Cargo.lock
index 9f8e2fc17b..b7f7d1d42e 100644
--- a/backend/Cargo.lock
+++ b/backend/Cargo.lock
@@ -122,7 +122,7 @@ checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"const-random",
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
"once_cell",
"version_check",
"zerocopy",
@@ -791,9 +791,9 @@ dependencies = [
[[package]]
name = "aws-lc-rs"
-version = "1.14.0"
+version = "1.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94b8ff6c09cd57b16da53641caa860168b88c172a5ee163b0288d3d6eea12786"
+checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d"
dependencies = [
"aws-lc-sys",
"zeroize",
@@ -801,9 +801,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
-version = "0.31.0"
+version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e44d16778acaf6a9ec9899b92cebd65580b83f685446bf2e1f5d3d732f99dcd"
+checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c"
dependencies = [
"bindgen 0.72.1",
"cc",
@@ -1023,7 +1023,7 @@ dependencies = [
"pin-project-lite",
"rustls 0.21.12",
"rustls 0.23.29",
- "rustls-native-certs 0.8.1",
+ "rustls-native-certs 0.8.2",
"rustls-pki-types",
"tokio",
"tower 0.5.2",
@@ -1962,9 +1962,9 @@ dependencies = [
[[package]]
name = "cfg-if"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
@@ -4831,9 +4831,9 @@ dependencies = [
[[package]]
name = "dyn-stack-macros"
-version = "0.1.0"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05dbec7076f432bb132db738df90d87a4f5789e99f59e7b1219a6b8ef61eaa68"
+checksum = "00140340c29b813fdf6ff2237c4407405baefc72cacbe8f1e2277a75b90e5d30"
[[package]]
name = "dynasm"
@@ -5870,21 +5870,21 @@ dependencies = [
"cfg-if",
"js-sys",
"libc",
- "wasi 0.11.1+wasi-snapshot-preview1",
+ "wasi",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
-version = "0.3.3"
+version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi",
- "wasi 0.14.7+wasi-0.2.4",
+ "wasip2",
"wasm-bindgen",
]
@@ -6666,7 +6666,7 @@ dependencies = [
"hyper-util",
"log",
"rustls 0.23.29",
- "rustls-native-certs 0.8.1",
+ "rustls-native-certs 0.8.2",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.26.4",
@@ -7182,7 +7182,7 @@ version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
"libc",
]
@@ -8180,7 +8180,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",
- "wasi 0.11.1+wasi-snapshot-preview1",
+ "wasi",
"windows-sys 0.48.0",
]
@@ -8191,7 +8191,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
- "wasi 0.11.1+wasi-snapshot-preview1",
+ "wasi",
"windows-sys 0.59.0",
]
@@ -8330,7 +8330,7 @@ dependencies = [
"bytes",
"crc32fast",
"flate2",
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
"mysql-common-derive",
"num-bigint",
"num-traits",
@@ -9058,9 +9058,9 @@ dependencies = [
[[package]]
name = "openssl"
-version = "0.10.73"
+version = "0.10.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8"
+checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654"
dependencies = [
"bitflags 2.9.4",
"cfg-if",
@@ -9099,9 +9099,9 @@ dependencies = [
[[package]]
name = "openssl-sys"
-version = "0.9.109"
+version = "0.9.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571"
+checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2"
dependencies = [
"cc",
"libc",
@@ -10179,9 +10179,9 @@ dependencies = [
[[package]]
name = "pure-rust-locales"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a"
+checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d"
[[package]]
name = "pwd"
@@ -10252,7 +10252,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
dependencies = [
"bytes",
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
"lru-slab",
"rand 0.9.0",
"ring 0.17.14",
@@ -10368,7 +10368,7 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
]
[[package]]
@@ -10700,7 +10700,7 @@ dependencies = [
"pin-project-lite",
"quinn",
"rustls 0.23.29",
- "rustls-native-certs 0.8.1",
+ "rustls-native-certs 0.8.2",
"rustls-pki-types",
"serde",
"serde_json",
@@ -11161,9 +11161,9 @@ dependencies = [
[[package]]
name = "rustls-native-certs"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3"
+checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923"
dependencies = [
"openssl-probe",
"rustls-pki-types",
@@ -13305,7 +13305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
dependencies = [
"fastrand",
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
"once_cell",
"rustix 1.1.2",
"windows-sys 0.61.2",
@@ -13832,7 +13832,7 @@ dependencies = [
"httparse",
"rand 0.8.5",
"ring 0.17.14",
- "rustls-native-certs 0.8.1",
+ "rustls-native-certs 0.8.2",
"rustls-pki-types",
"tokio",
"tokio-rustls 0.26.4",
@@ -13925,7 +13925,7 @@ dependencies = [
"percent-encoding",
"pin-project",
"prost",
- "rustls-native-certs 0.8.1",
+ "rustls-native-certs 0.8.2",
"rustls-pemfile 2.2.0",
"socket2 0.5.10",
"tokio",
@@ -14652,7 +14652,7 @@ version = "1.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
dependencies = [
- "getrandom 0.3.3",
+ "getrandom 0.3.4",
"js-sys",
"serde",
"wasm-bindgen",
@@ -14760,15 +14760,6 @@ version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
-[[package]]
-name = "wasi"
-version = "0.14.7+wasi-0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
-dependencies = [
- "wasip2",
-]
-
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
@@ -15131,7 +15122,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"aws-sdk-config",
@@ -15191,7 +15182,7 @@ dependencies = [
[[package]]
name = "windmill-api"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"argon2",
@@ -15311,7 +15302,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -15326,7 +15317,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"chrono",
"serde",
@@ -15339,7 +15330,7 @@ dependencies = [
[[package]]
name = "windmill-autoscaling"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"axum",
@@ -15358,7 +15349,7 @@ dependencies = [
[[package]]
name = "windmill-common"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -15442,7 +15433,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"regex",
"serde",
@@ -15457,7 +15448,7 @@ dependencies = [
[[package]]
name = "windmill-indexer"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"bytes",
@@ -15481,7 +15472,7 @@ dependencies = [
[[package]]
name = "windmill-macros"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"itertools 0.14.0",
"lazy_static",
@@ -15493,7 +15484,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"convert_case 0.6.0",
"serde",
@@ -15502,7 +15493,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15514,7 +15505,7 @@ dependencies = [
[[package]]
name = "windmill-parser-csharp"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"serde_json",
@@ -15526,7 +15517,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"gosyn",
@@ -15538,7 +15529,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15550,7 +15541,7 @@ dependencies = [
[[package]]
name = "windmill-parser-java"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"serde_json",
@@ -15562,7 +15553,7 @@ dependencies = [
[[package]]
name = "windmill-parser-nu"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"nu-parser",
@@ -15573,7 +15564,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -15584,7 +15575,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -15596,7 +15587,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -15619,7 +15610,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ruby"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15633,7 +15624,7 @@ dependencies = [
[[package]]
name = "windmill-parser-rust"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -15650,7 +15641,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15664,7 +15655,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15682,7 +15673,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"getrandom 0.2.16",
@@ -15707,7 +15698,7 @@ dependencies = [
[[package]]
name = "windmill-parser-yaml"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"serde_json",
@@ -15717,7 +15708,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -15750,7 +15741,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"wasm-bindgen",
"wasm-bindgen-test",
@@ -15760,7 +15751,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
-version = "1.559.0"
+version = "1.560.0"
dependencies = [
"anyhow",
"async-once-cell",
diff --git a/backend/Cargo.toml b/backend/Cargo.toml
index 3df2bb6f25..04a3464307 100644
--- a/backend/Cargo.toml
+++ b/backend/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "windmill"
-version = "1.559.0"
+version = "1.560.0"
authors.workspace = true
edition.workspace = true
@@ -34,7 +34,7 @@ members = [
exclude = ["./windmill-duckdb-ffi-internal"]
[workspace.package]
-version = "1.559.0"
+version = "1.560.0"
authors = ["Ruben Fiszel "]
edition = "2021"
diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml
index 417b433d1d..f7b949f2d9 100644
--- a/backend/windmill-api/openapi.yaml
+++ b/backend/windmill-api/openapi.yaml
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
- version: 1.559.0
+ version: 1.560.0
title: Windmill API
contact:
diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts
index f0c2dbd0a6..71f1371085 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.559.0";
+export const VERSION = "v1.560.0";
export async function login(email: string, password: string): Promise {
return await windmill.UserService.login({
diff --git a/cli/src/main.ts b/cli/src/main.ts
index 0e544c64bd..0aab1e0045 100644
--- a/cli/src/main.ts
+++ b/cli/src/main.ts
@@ -68,7 +68,7 @@ export {
// }
// });
-export const VERSION = "1.559.0";
+export const VERSION = "1.560.0";
export const WM_FORK_PREFIX = "wm-fork";
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 6bccce08e3..ee1ce2687b 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
- "version": "1.559.0",
+ "version": "1.560.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
- "version": "1.559.0",
+ "version": "1.560.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
diff --git a/frontend/package.json b/frontend/package.json
index 32acaaa4d7..4d04a38246 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
- "version": "1.559.0",
+ "version": "1.560.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
diff --git a/lsp/Pipfile b/lsp/Pipfile
index 6ff3d03015..c970c8e1c6 100644
--- a/lsp/Pipfile
+++ b/lsp/Pipfile
@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
-wmill = ">=1.559.0"
-wmill_pg = ">=1.559.0"
+wmill = ">=1.560.0"
+wmill_pg = ">=1.560.0"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"
diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml
index e0bf944062..54d0a7d450 100644
--- a/openflow.openapi.yaml
+++ b/openflow.openapi.yaml
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
- version: 1.559.0
+ version: 1.560.0
title: OpenFlow Spec
contact:
name: Ruben Fiszel
diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1
index 7ea75be2ec..ace7c69295 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.559.0'
+ ModuleVersion = '1.560.0'
# Supported PSEditions
# CompatiblePSEditions = @()
diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml
index 8e79fe355a..d867810f7d 100644
--- a/python-client/wmill/pyproject.toml
+++ b/python-client/wmill/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
-version = "1.559.0"
+version = "1.560.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 c342ec65f5..1a905f263b 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.559.0"
+version = "1.560.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 cc92ebd43a..70372d564c 100644
--- a/typescript-client/jsr.json
+++ b/typescript-client/jsr.json
@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
- "version": "1.559.0",
+ "version": "1.560.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 cd3c21925a..6e99cab791 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.559.0",
+ "version": "1.560.0",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {
diff --git a/version.txt b/version.txt
index b38560d95d..3d75838ea8 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-1.559.0
+1.560.0
From 32fae7a10c769473c708970e18c1f8268d62183f Mon Sep 17 00:00:00 2001
From: wendrul <53628737+wendrul@users.noreply.github.com>
Date: Thu, 16 Oct 2025 11:08:18 +0200
Subject: [PATCH 09/33] feat: ansible playbook execution git repo mode (repo
viewer + UI utils) (#6831)
* Improve minio on flake.nix
* Add first asset parsing logic for ansible
* Correct html gt sign
* Decouple s3 file picker from drawer
* Factor duplicate code into snippet
* Update S3FilePickerInner to be compatible
* Fix pane shrinking issue
* Git repo viewer
* Change GitRepoViewer
* Endpoints for git repo visualizer
* Move git repo viewer to its own component
* Add button to populate git repo viewer
* Update parser yaml for new ansisble features (repo viewer)
* Reflect parser changes for ansible
* Add button to add the git repo mode of declaration for ansible
* Factor function
* Playbook + inventories into the drawer
* Add button to add inventories from s3
* Move tests to lib.rs
* Inventory loading from s3
* Move get github app token logic to be reused by ansible
* Update parser and ansible executor
* Use the correct path for inventories
* Add nushell to flake for wasm builds
* Add published parser
* Update hubPaths with clone and upload to s3
* Update ee-repo to the branch ref
* Fix npm run check
* Update cargo.lock
* Change labels on buttons
* Remove debug log
* Update ee-repo-ref
* Fix ee issues
* Update ee-repo ref
* Fix typo
* Fix ee
* Update ee-repo-ref
* Fix missing imports
* Unused var
* Fix typo
* Layout improvents
* Fix typos
* Remove unused function and log
---------
Co-authored-by: Ruben Fiszel
---
backend/Cargo.lock | 1 +
backend/ee-repo-ref.txt | 2 +-
.../parsers/windmill-parser-wasm/src/lib.rs | 23 +
.../parsers/windmill-parser-yaml/Cargo.toml | 1 +
.../windmill-parser-yaml/src/asset_parser.rs | 41 +
.../parsers/windmill-parser-yaml/src/lib.rs | 259 ++++-
.../windmill-parser/src/asset_parser.rs | 7 +
backend/windmill-api/openapi.yaml | 238 +++++
backend/windmill-api/src/ai.rs | 6 +-
backend/windmill-api/src/resources.rs | 100 ++
backend/windmill-api/src/variables.rs | 33 -
backend/windmill-common/src/git_sync_oss.rs | 31 +
backend/windmill-common/src/lib.rs | 3 +
backend/windmill-common/src/variables.rs | 36 +-
.../windmill-worker/src/ansible_executor.rs | 139 ++-
flake.nix | 23 +-
frontend/package-lock.json | 8 +-
frontend/package.json | 4 +-
frontend/src/lib/ansibleUtils.ts | 384 ++++++++
frontend/src/lib/components/EditorBar.svelte | 35 +
.../components/FlowStatusViewerInner.svelte | 2 +-
.../components/GitRepoPopoverPicker.svelte | 99 ++
.../components/GitRepoResourcePicker.svelte | 275 ++++++
.../src/lib/components/GitRepoViewer.svelte | 183 ++++
.../src/lib/components/S3FilePicker.svelte | 906 +-----------------
.../lib/components/S3FilePickerInner.svelte | 886 +++++++++++++++++
.../src/lib/components/ScriptBuilder.svelte | 2 +-
.../src/lib/components/ScriptEditor.svelte | 350 ++++---
.../schema/EditableSchemaSdkWrapper.svelte | 4 +-
.../components/sidebar/SidebarContent.svelte | 2 +-
frontend/src/lib/hub.ts | 1 +
frontend/src/lib/hubPaths.json | 3 +-
frontend/src/lib/infer.ts | 16 +-
33 files changed, 3038 insertions(+), 1065 deletions(-)
create mode 100644 backend/parsers/windmill-parser-yaml/src/asset_parser.rs
create mode 100644 backend/windmill-common/src/git_sync_oss.rs
create mode 100644 frontend/src/lib/ansibleUtils.ts
create mode 100644 frontend/src/lib/components/GitRepoPopoverPicker.svelte
create mode 100644 frontend/src/lib/components/GitRepoResourcePicker.svelte
create mode 100644 frontend/src/lib/components/GitRepoViewer.svelte
create mode 100644 frontend/src/lib/components/S3FilePickerInner.svelte
diff --git a/backend/Cargo.lock b/backend/Cargo.lock
index b7f7d1d42e..50b1a4a4f1 100644
--- a/backend/Cargo.lock
+++ b/backend/Cargo.lock
@@ -15701,6 +15701,7 @@ name = "windmill-parser-yaml"
version = "1.560.0"
dependencies = [
"anyhow",
+ "serde",
"serde_json",
"windmill-parser",
"yaml-rust",
diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt
index 12cf35dc1b..48777ef499 100644
--- a/backend/ee-repo-ref.txt
+++ b/backend/ee-repo-ref.txt
@@ -1 +1 @@
-c7d34190819c83b4dfe62498a47ab9b439a321a9
+af5cfc6b0f0bd42f95a5842dff92cf598d3aa6d8
diff --git a/backend/parsers/windmill-parser-wasm/src/lib.rs b/backend/parsers/windmill-parser-wasm/src/lib.rs
index 238b207dd2..34969f8e5f 100644
--- a/backend/parsers/windmill-parser-wasm/src/lib.rs
+++ b/backend/parsers/windmill-parser-wasm/src/lib.rs
@@ -150,6 +150,16 @@ pub fn parse_ansible(code: &str) -> String {
wrap_sig(windmill_parser_yaml::parse_ansible_sig(code))
}
+#[cfg(feature = "ansible-parser")]
+#[wasm_bindgen]
+pub fn parse_ansible_delegate(code: &str) -> String {
+ if let Ok(r) = windmill_parser_yaml::parse_delegate_to_git_repo(code) {
+ return serde_json::to_string(&r).unwrap();
+ } else {
+ return "Invalid".to_string();
+ }
+}
+
#[cfg(feature = "csharp-parser")]
#[wasm_bindgen]
pub fn parse_csharp(code: &str) -> String {
@@ -173,6 +183,7 @@ pub fn parse_java(code: &str) -> String {
pub fn parse_ruby(code: &str) -> String {
wrap_sig(windmill_parser_ruby::parse_ruby_signature(code))
}
+
#[cfg(feature = "sql-parser")]
#[wasm_bindgen]
pub fn parse_assets_sql(code: &str) -> String {
@@ -203,4 +214,16 @@ pub fn parse_assets_py(code: &str) -> String {
}
}
+#[cfg(feature = "ansible-parser")]
+#[wasm_bindgen]
+pub fn parse_assets_ansible(code: &str) -> String {
+ let o = windmill_parser_yaml::parse_assets(code);
+ if let Ok(r) = o {
+ return serde_json::to_string(&r).unwrap();
+ } else {
+ return format!("err: {:?}", o.err().unwrap());
+ return "Invalid".to_string();
+ }
+}
+
// for related places search: ADD_NEW_LANG
diff --git a/backend/parsers/windmill-parser-yaml/Cargo.toml b/backend/parsers/windmill-parser-yaml/Cargo.toml
index 9bcfbea7a7..eb01ba1936 100644
--- a/backend/parsers/windmill-parser-yaml/Cargo.toml
+++ b/backend/parsers/windmill-parser-yaml/Cargo.toml
@@ -13,3 +13,4 @@ yaml-rust.workspace = true
windmill-parser.workspace = true
anyhow.workspace = true
serde_json.workspace = true
+serde.workspace = true
diff --git a/backend/parsers/windmill-parser-yaml/src/asset_parser.rs b/backend/parsers/windmill-parser-yaml/src/asset_parser.rs
new file mode 100644
index 0000000000..0b9ba0d38a
--- /dev/null
+++ b/backend/parsers/windmill-parser-yaml/src/asset_parser.rs
@@ -0,0 +1,41 @@
+use windmill_parser::asset_parser::{
+ merge_assets, AssetKind, AssetUsageAccessType, ParseAssetsResult,
+ };
+
+use crate::{parse_ansible_reqs, ResourceOrVariablePath};
+
+pub fn parse_assets(input: &str) -> anyhow::Result>> {
+ let mut assets = vec![];
+ if let (_, Some(ansible_reqs), _) = parse_ansible_reqs(input)? {
+ if let Some(delegate_to_git_repo_details) = ansible_reqs.delegate_to_git_repo {
+ assets.push(ParseAssetsResult {
+ kind: AssetKind::Resource,
+ path: delegate_to_git_repo_details.resource,
+ access_type: Some(AssetUsageAccessType::R),
+ })
+ }
+
+ for i in ansible_reqs.inventories {
+ if let Some(pinned_res) = i.pinned_resource {
+ assets.push(ParseAssetsResult {
+ kind: AssetKind::Resource,
+ path: pinned_res,
+ access_type: Some(AssetUsageAccessType::R),
+ })
+ }
+ }
+
+ for file in ansible_reqs.file_resources {
+ if let ResourceOrVariablePath::Resource(resource) = file.resource_path {
+ assets.push(ParseAssetsResult {
+ kind: AssetKind::Resource,
+ path: resource,
+ access_type: Some(AssetUsageAccessType::R),
+ })
+ }
+ }
+ }
+
+ Ok(merge_assets(assets))
+}
+
diff --git a/backend/parsers/windmill-parser-yaml/src/lib.rs b/backend/parsers/windmill-parser-yaml/src/lib.rs
index 710d17c966..dee9da5745 100644
--- a/backend/parsers/windmill-parser-yaml/src/lib.rs
+++ b/backend/parsers/windmill-parser-yaml/src/lib.rs
@@ -1,15 +1,26 @@
use std::collections::HashMap;
use anyhow::anyhow;
+use serde::Serialize;
use serde_json::json;
use windmill_parser::{Arg, MainArgSignature, ObjectProperty, ObjectType, Typ};
use yaml_rust::{Yaml, YamlEmitter, YamlLoader};
+pub mod asset_parser;
+pub use asset_parser::parse_assets;
+
pub fn parse_ansible_sig(inner_content: &str) -> anyhow::Result {
let docs = YamlLoader::load_from_str(inner_content)
.map_err(|e| anyhow!("Failed to parse yaml: {}", e))?;
- if docs.len() < 2 {
+ let mut delegating_to_git_repo = false;
+ if let Yaml::Hash(doc) = &docs[0] {
+ if let Some(v) = doc.get(&Yaml::String("delegate_to_git_repo".to_string())) {
+ delegating_to_git_repo = extract_delegate_to_git_repo_details(v).is_some();
+ }
+ }
+
+ if docs.len() < 2 && !delegating_to_git_repo {
return Ok(MainArgSignature {
star_args: false,
star_kwargs: false,
@@ -56,7 +67,21 @@ pub fn parse_ansible_sig(inner_content: &str) -> anyhow::Result {
+ for inv in parse_additional_inventories(value)? {
+ if let PreexistingAnsibleInventory::PassedInArgs(i) = inv {
+ args.push(Arg {
+ name: i.name,
+ otyp: None,
+ typ: Typ::List(Box::new(Typ::Str(i.options))),
+ has_default: false,
+ default: None,
+ oidx: None,
+ });
+ }
}
}
_ => (),
@@ -211,6 +236,18 @@ pub struct AnsibleInventory {
pub pinned_resource: Option,
}
+#[derive(Debug, Clone)]
+pub enum PreexistingAnsibleInventory {
+ Static(String),
+ PassedInArgs(InventoryFilenameListDefinition),
+}
+
+#[derive(Debug, Clone)]
+pub struct InventoryFilenameListDefinition {
+ pub options: Option>,
+ pub name: String,
+}
+
#[derive(Debug, Clone)]
pub struct GitRepo {
pub url: String,
@@ -219,12 +256,22 @@ pub struct GitRepo {
pub target_path: String,
}
+#[derive(Debug, Clone, Serialize)]
+pub struct DelegateToGitRepoDetails {
+ pub resource: String,
+ pub playbook: Option,
+ pub commit: Option,
+ pub inventories_location: Option,
+ pub vars_location: Option,
+}
+
#[derive(Debug, Clone)]
pub struct AnsibleRequirements {
pub python_reqs: Vec,
pub roles_and_collections: Option,
pub file_resources: Vec,
pub inventories: Vec,
+ pub additional_inventories: Vec,
pub vars: Vec<(String, String)>,
pub resources: Vec<(String, String)>,
pub options: AnsiblePlaybookOptions,
@@ -232,6 +279,7 @@ pub struct AnsibleRequirements {
pub vault_id: Vec,
pub git_repos: Vec,
pub git_ssh_identity: Vec,
+ pub delegate_to_git_repo: Option,
}
impl Default for AnsibleRequirements {
@@ -241,6 +289,7 @@ impl Default for AnsibleRequirements {
roles_and_collections: None,
file_resources: vec![],
inventories: vec![],
+ additional_inventories: vec![],
vars: vec![],
resources: vec![],
options: AnsiblePlaybookOptions {
@@ -254,10 +303,57 @@ impl Default for AnsibleRequirements {
vault_id: vec![],
git_repos: vec![],
git_ssh_identity: vec![],
+ delegate_to_git_repo: None,
}
}
}
+fn parse_additional_inventories(
+ inventory_yaml: &Yaml,
+) -> anyhow::Result> {
+ if let Yaml::Array(arr) = inventory_yaml {
+ let mut ret = vec![];
+ let mut count = -1;
+ for inv in arr {
+ if let Yaml::String(inv_name) = inv {
+ ret.push(PreexistingAnsibleInventory::Static(inv_name.clone()));
+ } else if let Yaml::Hash(inv) = inv {
+ if let Some(options) = inv.get(&Yaml::String("options".to_string())) {
+ let options = match options {
+ Yaml::Null => None,
+ Yaml::Array(elements) => Some(
+ elements
+ .iter()
+ .filter_map(|s| s.as_str().map(|s| s.to_string()))
+ .collect(),
+ ),
+ _ => continue,
+ };
+
+ let name = inv
+ .get(&Yaml::String("name".to_string()))
+ .and_then(|v| v.as_str())
+ .map(|s| s.to_string())
+ .unwrap_or_else(|| {
+ count += 1;
+ if count == 0 {
+ "Additional inventories".to_string()
+ } else {
+ format!("Additional inventories ({count})")
+ }
+ });
+
+ ret.push(PreexistingAnsibleInventory::PassedInArgs(
+ InventoryFilenameListDefinition { options, name },
+ ))
+ }
+ }
+ }
+ return Ok(ret);
+ }
+ return Err(anyhow!("Invalid inventory definition"));
+}
+
fn parse_inventories(inventory_yaml: &Yaml) -> anyhow::Result> {
if let Yaml::Array(arr) = inventory_yaml {
let mut ret = vec![];
@@ -303,6 +399,20 @@ fn parse_inventories(inventory_yaml: &Yaml) -> anyhow::Result anyhow::Result> {
+ let docs = YamlLoader::load_from_str(inner_content)
+ .map_err(|e| anyhow!("Failed to parse yaml: {}", e))?;
+
+ if let Yaml::Hash(doc) = &docs[0] {
+ if let Some(v) = doc.get(&Yaml::String("delegate_to_git_repo".to_string())) {
+ return Ok(extract_delegate_to_git_repo_details(v));
+ }
+ }
+ return Ok(None);
+}
+
pub fn parse_ansible_reqs(
inner_content: &str,
) -> anyhow::Result<(String, Option, String)> {
@@ -310,12 +420,19 @@ pub fn parse_ansible_reqs(
let docs = YamlLoader::load_from_str(inner_content)
.map_err(|e| anyhow!("Failed to parse yaml: {}", e))?;
- if docs.len() < 2 {
- return Ok((logs, None, inner_content.to_string()));
- }
let mut ret = AnsibleRequirements::default();
+ if let Yaml::Hash(doc) = &docs[0] {
+ if let Some(v) = doc.get(&Yaml::String("delegate_to_git_repo".to_string())) {
+ ret.delegate_to_git_repo = extract_delegate_to_git_repo_details(v);
+ }
+ }
+
+ if ret.delegate_to_git_repo.is_none() && docs.len() < 2 {
+ return Ok((logs, None, inner_content.to_string()));
+ }
+
if let Yaml::Hash(doc) = &docs[0] {
for (key, value) in doc {
match key {
@@ -367,7 +484,11 @@ pub fn parse_ansible_reqs(
}
}
Yaml::String(key) if key == "inventory" => {
- ret.inventories = parse_inventories(value)?;
+ ret.inventories.extend(parse_inventories(value)?);
+ }
+ Yaml::String(key) if key == "additional_inventories" => {
+ ret.additional_inventories
+ .extend(parse_additional_inventories(value)?);
}
Yaml::String(key) if key == "vault_password" => {
let Yaml::String(filename) = value else {
@@ -423,11 +544,13 @@ pub fn parse_ansible_reqs(
ret.git_ssh_identity.push(file_name.clone());
}
}
+ Yaml::String(key) if key == "delegate_to_git_repo" => {}
Yaml::String(key) => logs.push_str(&format!("\nUnknown field `{}`. Ignoring", key)),
_ => (),
}
}
}
+
let mut out_str = String::new();
let mut emitter = YamlEmitter::new(&mut out_str);
@@ -437,6 +560,42 @@ pub fn parse_ansible_reqs(
Ok((logs, Some(ret), out_str))
}
+fn extract_delegate_to_git_repo_details(value: &Yaml) -> Option {
+ if let Yaml::Hash(v) = value {
+ if let Some(resource) = v
+ .get(&Yaml::String("resource".to_string()))
+ .and_then(|s| s.as_str())
+ .map(|s| s.to_string())
+ {
+ let playbook = v
+ .get(&Yaml::String("playbook".to_string()))
+ .and_then(|s| s.as_str())
+ .map(|s| s.to_string());
+ let commit = v
+ .get(&Yaml::String("commit".to_string()))
+ .and_then(|s| s.as_str())
+ .map(|s| s.to_string());
+ let inventories_location = v
+ .get(&Yaml::String("inventories_location".to_string()))
+ .and_then(|s| s.as_str())
+ .map(|s| s.to_string());
+ let vars_location = v
+ .get(&Yaml::String("vars_location".to_string()))
+ .and_then(|s| s.as_str())
+ .map(|s| s.to_string());
+
+ return Some(DelegateToGitRepoDetails {
+ resource,
+ playbook,
+ commit,
+ inventories_location,
+ vars_location,
+ });
+ }
+ }
+ return None;
+}
+
fn parse_git_repo(r: &Yaml) -> anyhow::Result {
let Yaml::Hash(repo) = r else {
return Err(anyhow!("Should be a Map"));
@@ -679,7 +838,7 @@ pub fn add_versions_to_requirements_yaml(
input: &str,
role_versions: &HashMap,
collection_versions: &HashMap,
-) -> anyhow::Result<(String,String)> {
+) -> anyhow::Result<(String, String)> {
let mut docs =
YamlLoader::load_from_str(input).map_err(|e| anyhow!("YAML parse error: {}", e))?;
let doc = &mut docs[0];
@@ -709,3 +868,89 @@ pub fn add_versions_to_requirements_yaml(
Ok((out_str, logs))
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_parse_ansible_assets() {
+ let p = r#"
+---
+inventory:
+ - resource_type: ansible_inventory
+ # You can pin an inventory to this script by hardcoding the resource path:
+ # resource: u/user/your_resource
+# - name: hcloud.yml
+# resource_type: dynamic_inventory
+
+additional_inventories:
+ - options: ["a", "b", "c"]
+
+options:
+ - verbosity: vvv
+
+delegate_to_git_repo:
+ resource: u/admin/git_reportino
+ playbook: ./playbooks/playbook.yml
+ commit: 7sh7dh73h7dhd299d91hd1hdh3d3hygh4372
+
+
+# File resources will be written in the relative `target` location before
+# running the playbook
+files:
+ - resource: u/user/fabulous_jinja_template
+ target: ./config_template.j2
+ - variable: u/user/ssh_key
+ target: ./ssh_key
+ mode: '0600'
+
+# Define the arguments of the windmill script
+extra_vars:
+ world_qualifier:
+ type: string
+
+# If using Ansible Vault:
+# vault_password: u/user/ansible_vault_password
+
+dependencies:
+ galaxy:
+ collections:
+ - name: community.general
+ - name: community.vmware
+ roles:
+ python:
+ - jmespath
+---
+- name: Echo
+ hosts: 127.0.0.1
+ connection: local
+ vars:
+ my_result:
+ a: 2
+ b: true
+ c: "Hello"
+
+ tasks:
+ - name: Print debug message
+ debug:
+ msg: "Hello, {{world_qualifier}} world!"
+ - name: Write variable my_result to result.json
+ delegate_to: localhost
+ copy:
+ content: "{{ my_result | to_json }}"
+ dest: result.json
+"#;
+ let a = parse_assets(p).unwrap();
+ println!("The resulting assets are: {}", a.len());
+
+ let a = parse_ansible_reqs(p).unwrap();
+ println!("The resulting reqs are: {:#?}", a);
+
+ let a = parse_ansible_sig(p).unwrap();
+ println!("The resulting sig is: {:#?}", a);
+
+ let a = parse_delegate_to_git_repo(p).unwrap();
+ println!("The resulting delegate_to_kit_repo is: {:#?}", a);
+ }
+}
diff --git a/backend/parsers/windmill-parser/src/asset_parser.rs b/backend/parsers/windmill-parser/src/asset_parser.rs
index 82cf8b8499..a721590ae9 100644
--- a/backend/parsers/windmill-parser/src/asset_parser.rs
+++ b/backend/parsers/windmill-parser/src/asset_parser.rs
@@ -26,6 +26,13 @@ pub struct ParseAssetsResult> {
pub access_type: Option, // None in case of ambiguity
}
+#[derive(Debug, Clone, Serialize)]
+pub struct DelegateToGitRepoDetails {
+ pub resource: String,
+ pub playbook: Option,
+ pub commit: Option,
+}
+
pub fn merge_assets>(assets: Vec>) -> Vec> {
let mut arr: Vec> = vec![];
for asset in assets {
diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml
index f7b949f2d9..37c46fff9a 100644
--- a/backend/windmill-api/openapi.yaml
+++ b/backend/windmill-api/openapi.yaml
@@ -4192,6 +4192,29 @@ paths:
application/json:
schema: {}
+ /w/{workspace}/resources/git_commit_hash/{path}:
+ get:
+ summary: get git repository latest commit hash
+ operationId: getGitCommitHash
+ tags:
+ - resource
+ parameters:
+ - $ref: "#/components/parameters/WorkspaceId"
+ - $ref: "#/components/parameters/Path"
+ responses:
+ "200":
+ description: git commit hash
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ commit_hash:
+ type: string
+ description: Latest commit hash from git ls-remote
+ required:
+ - commit_hash
+
/w/{workspace}/resources/exists/{path}:
get:
summary: does resource exists
@@ -13441,6 +13464,160 @@ paths:
schema:
$ref: "#/components/schemas/WindmillFilePreview"
+ /w/{workspace}/job_helpers/list_git_repo_files:
+ get:
+ summary: List the file keys available in instance object storage with resource-based access control
+ operationId: listGitRepoFiles
+ tags:
+ - helpers
+ parameters:
+ - $ref: "#/components/parameters/WorkspaceId"
+ - name: max_keys
+ in: query
+ required: true
+ schema:
+ type: integer
+ - name: marker
+ in: query
+ schema:
+ type: string
+ - name: prefix
+ in: query
+ required: false
+ schema:
+ type: string
+ description: Must follow format gitrepos/{workspace_id}/{resource_path}/...
+ - name: storage
+ in: query
+ schema:
+ type: string
+ responses:
+ "200":
+ description: List of file keys
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ next_marker:
+ type: string
+ windmill_large_files:
+ type: array
+ items:
+ $ref: "#/components/schemas/WindmillLargeFile"
+ restricted_access:
+ type: boolean
+ required:
+ - windmill_large_files
+
+ /w/{workspace}/job_helpers/load_git_repo_file_preview:
+ get:
+ summary: Load a preview of a file from instance storage with resource-based access control
+ operationId: loadGitRepoFilePreview
+ tags:
+ - helpers
+ parameters:
+ - $ref: "#/components/parameters/WorkspaceId"
+ - name: file_key
+ in: query
+ required: true
+ schema:
+ type: string
+ description: Must follow format gitrepos/{workspace_id}/{resource_path}/...
+ - name: file_size_in_bytes
+ in: query
+ schema:
+ type: integer
+ - name: file_mime_type
+ in: query
+ schema:
+ type: string
+ - name: csv_separator
+ in: query
+ schema:
+ type: string
+ - name: csv_has_header
+ in: query
+ schema:
+ type: boolean
+ - name: read_bytes_from
+ in: query
+ schema:
+ type: integer
+ - name: read_bytes_length
+ in: query
+ schema:
+ type: integer
+ - name: storage
+ in: query
+ schema:
+ type: string
+ responses:
+ "200":
+ description: FilePreview
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/WindmillFilePreview"
+
+ /w/{workspace}/job_helpers/load_git_repo_file_metadata:
+ get:
+ summary: Load file metadata from instance storage with resource-based access control
+ operationId: loadGitRepoFileMetadata
+ tags:
+ - helpers
+ parameters:
+ - $ref: "#/components/parameters/WorkspaceId"
+ - name: file_key
+ in: query
+ required: true
+ schema:
+ type: string
+ description: Must follow format gitrepos/{workspace_id}/{resource_path}/...
+ - name: storage
+ in: query
+ schema:
+ type: string
+ responses:
+ "200":
+ description: FileMetadata
+ content:
+ application/json:
+ schema:
+ $ref: "#/components/schemas/WindmillFileMetadata"
+
+ /w/{workspace}/job_helpers/check_s3_folder_exists:
+ get:
+ summary: Check if S3 path exists and is a folder
+ operationId: checkS3FolderExists
+ tags:
+ - helpers
+ parameters:
+ - $ref: "#/components/parameters/WorkspaceId"
+ - name: file_key
+ description: S3 file key to check (e.g., gitrepos/{workspace_id}/u/user/resource/{commit_hash})
+ in: query
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: S3 folder existence check result
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ exists:
+ type: boolean
+ description: Whether the path exists
+ is_folder:
+ type: boolean
+ description: Whether the path is a folder (true) or file (false)
+ required:
+ - exists
+ - is_folder
+
/w/{workspace}/job_helpers/load_parquet_preview/{path}:
get:
summary: Load a preview of a parquet file
@@ -13677,6 +13854,67 @@ paths:
required:
- file_key
+ /w/{workspace}/job_helpers/upload_git_repo_file_to_instance_storage:
+ post:
+ summary: Upload a file to the instance storage gitrepos section for viewing
+ operationId: gitRepoViewerFileUpload
+ tags:
+ - helpers
+ parameters:
+ - $ref: "#/components/parameters/WorkspaceId"
+ - name: file_key
+ in: query
+ required: false
+ schema:
+ type: string
+ - name: file_extension
+ in: query
+ required: false
+ schema:
+ type: string
+ - name: s3_resource_path
+ in: query
+ required: false
+ schema:
+ type: string
+ - name: resource_type
+ in: query
+ required: false
+ schema:
+ type: string
+ - name: storage
+ in: query
+ schema:
+ type: string
+ - name: content_type
+ in: query
+ schema:
+ type: string
+ - name: content_disposition
+ in: query
+ schema:
+ type: string
+ requestBody:
+ description: File content
+ required: true
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ responses:
+ "200":
+ description: File upload status
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ file_key:
+ type: string
+ required:
+ - file_key
+
/w/{workspace}/job_helpers/download_s3_file:
get:
summary: Download file from S3 bucket
diff --git a/backend/windmill-api/src/ai.rs b/backend/windmill-api/src/ai.rs
index 3c3ba77bb5..090732e025 100644
--- a/backend/windmill-api/src/ai.rs
+++ b/backend/windmill-api/src/ai.rs
@@ -1,7 +1,4 @@
-use crate::{
- db::{ApiAuthed, DB},
- variables::get_variable_or_self,
-};
+use crate::db::{ApiAuthed, DB};
use axum::{body::Bytes, extract::Path, response::IntoResponse, routing::post, Extension, Router};
use http::{HeaderMap, Method};
@@ -9,6 +6,7 @@ use quick_cache::sync::Cache;
use reqwest::{Client, RequestBuilder};
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue;
+use windmill_common::variables::get_variable_or_self;
use std::collections::HashMap;
use windmill_audit::{audit_oss::audit_log, ActionKind};
use windmill_common::ai_providers::{AIProvider, ProviderConfig, ProviderModel, AZURE_API_VERSION};
diff --git a/backend/windmill-api/src/resources.rs b/backend/windmill-api/src/resources.rs
index c0162ae022..df31ed058e 100644
--- a/backend/windmill-api/src/resources.rs
+++ b/backend/windmill-api/src/resources.rs
@@ -28,6 +28,8 @@ use serde::{Deserialize, Serialize};
use serde_json::{value::RawValue, Value};
use sql_builder::{bind::Bind, quote, SqlBuilder};
use sqlx::{FromRow, Postgres, Transaction};
+use std::process::Stdio;
+use tokio::process::Command;
use uuid::Uuid;
use windmill_audit::audit_oss::{audit_log, AuditAuthor};
use windmill_audit::ActionKind;
@@ -58,6 +60,7 @@ pub fn workspaced_service() -> Router {
.route("/delete/*path", delete(delete_resource))
.route("/delete_bulk", delete(delete_resources_bulk))
.route("/create", post(create_resource))
+ .route("/git_commit_hash/*path", get(get_git_commit_hash))
.route("/type/list", get(list_resource_types))
.route("/type/listnames", get(list_resource_types_names))
.route("/type/get/:name", get(get_resource_type))
@@ -1387,3 +1390,100 @@ where
Ok(resource)
}
+
+#[derive(Deserialize, Serialize)]
+struct GitRepositoryResource {
+ url: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ branch: Option,
+}
+
+#[derive(Serialize)]
+struct GitCommitHashResponse {
+ commit_hash: String,
+}
+
+async fn get_git_commit_hash(
+ authed: ApiAuthed,
+ Extension(user_db): Extension,
+ Extension(db): Extension,
+ Tokened { token }: Tokened,
+ Path((w_id, path)): Path<(String, StripPath)>,
+) -> JsonResult {
+ let path = path.to_path();
+
+ check_scopes(&authed, || format!("resources:read:{}", path))?;
+
+ let git_repo_resource_value = get_resource_value_interpolated_internal(
+ &authed,
+ Some(user_db),
+ &db,
+ &w_id,
+ path,
+ None,
+ &token,
+ false,
+ )
+ .await
+ .map_err(|e| Error::NotAuthorized(format!("Access to resource {} denied: ({e})", path)))?;
+
+ let git_resource: GitRepositoryResource = match git_repo_resource_value {
+ Some(value) => serde_json::from_value(value).map_err(|e| {
+ Error::BadRequest(format!("Invalid git repository resource format: {}", e))
+ })?,
+ None => return Err(Error::NotFound(format!("Resource {} not found", path)).into()),
+ };
+
+ let commit_hash = get_repo_latest_commit_hash(&git_resource).await?;
+
+ Ok(Json(GitCommitHashResponse { commit_hash }))
+}
+
+async fn get_repo_latest_commit_hash(git_resource: &GitRepositoryResource) -> Result {
+ let mut git_cmd = Command::new("git");
+
+ let ref_spec = git_resource
+ .branch
+ .as_deref()
+ .filter(|s| !s.is_empty())
+ .unwrap_or("HEAD");
+
+ git_cmd.args(["ls-remote", &git_resource.url, ref_spec]);
+ git_cmd.stderr(Stdio::piped());
+
+ let output = git_cmd
+ .output()
+ .await
+ .map_err(|e| Error::internal_err(format!("Failed to execute git command: {}", e)))?;
+
+ if !output.status.success() {
+ let stderr = String::from_utf8(output.stderr)
+ .unwrap_or_else(|_| "Failed to decode stderr".to_string());
+ return Err(Error::BadRequest(format!(
+ "Error getting git repo commit hash: {}",
+ stderr
+ )));
+ }
+
+ let stdout = String::from_utf8(output.stdout)
+ .map_err(|e| Error::internal_err(format!("Failed to decode git output: {}", e)))?;
+
+ let lines: Vec<&str> = stdout.lines().collect();
+
+ if lines.is_empty() {
+ return Err(Error::BadRequest(format!(
+ "No commits found for reference '{}' in repository '{}'",
+ ref_spec, git_resource.url
+ )));
+ }
+
+ let commit_hash = lines
+ .first()
+ .and_then(|line| line.split_whitespace().next())
+ .map(|s| s.to_string())
+ .ok_or_else(|| {
+ Error::BadRequest("Unexpected output format for git ls-remote".to_string())
+ })?;
+
+ Ok(commit_hash)
+}
diff --git a/backend/windmill-api/src/variables.rs b/backend/windmill-api/src/variables.rs
index 80eac1c9be..07d0572e6f 100644
--- a/backend/windmill-api/src/variables.rs
+++ b/backend/windmill-api/src/variables.rs
@@ -864,36 +864,3 @@ pub async fn get_value_internal<'a, 'e, A: sqlx::Acquire<'e, Database = Postgres
Ok(r)
}
-pub async fn get_variable_or_self(path: String, db: &DB, w_id: &str) -> Result {
- if !path.starts_with("$var:") {
- return Ok(path);
- }
- let path = path.strip_prefix("$var:").unwrap().to_string();
-
- let record = sqlx::query!(
- "SELECT value, is_secret
- FROM variable
- WHERE path = $1 AND workspace_id = $2",
- &path,
- &w_id
- )
- .fetch_optional(db)
- .await?;
-
- if let Some(record) = record {
- let mut value = record.value;
- if record.is_secret {
- let mc = build_crypt(db, w_id).await?;
- value = decrypt(&mc, value).map_err(|e| {
- Error::internal_err(format!("Error decrypting variable {}: {}", path, e))
- })?;
- }
-
- Ok(value)
- } else {
- Err(Error::NotFound(format!(
- "Variable not found when resolving `$var:{}`",
- path
- )))
- }
-}
diff --git a/backend/windmill-common/src/git_sync_oss.rs b/backend/windmill-common/src/git_sync_oss.rs
new file mode 100644
index 0000000000..dcd2abf1f7
--- /dev/null
+++ b/backend/windmill-common/src/git_sync_oss.rs
@@ -0,0 +1,31 @@
+#[cfg(feature = "private")]
+#[allow(unused)]
+pub use crate::git_sync_ee::*;
+use url::Url;
+#[cfg(not(feature = "private"))]
+use sqlx::{Pool, Postgres};
+
+#[cfg(not(feature = "private"))]
+pub async fn get_github_app_token_internal(
+ _db: &Pool,
+ _job_token: &str,
+) -> crate::error::Result {
+ return Err(crate::error::Error::BadRequest("Github app authentication is not available on the open source build".to_string()))
+}
+
+pub fn prepend_token_to_github_url(
+ github_url: &str,
+ installation_token: &str,
+) -> crate::error::Result {
+ let url = Url::parse(github_url)?;
+
+ if url.host_str() != Some("github.com") {
+ return Err(crate::error::Error::BadRequest("Invalid: not a github URL".to_string()));
+ }
+
+ Ok(format!(
+ "https://x-access-token:{}@github.com{}",
+ installation_token,
+ url.path()
+ ))
+}
diff --git a/backend/windmill-common/src/lib.rs b/backend/windmill-common/src/lib.rs
index c17ebc3503..4ff4e18723 100644
--- a/backend/windmill-common/src/lib.rs
+++ b/backend/windmill-common/src/lib.rs
@@ -87,6 +87,9 @@ pub mod variables;
pub mod worker;
pub mod worker_group_job_stats;
pub mod workspaces;
+#[cfg(feature = "private")]
+pub mod git_sync_ee;
+pub mod git_sync_oss;
pub const DEFAULT_MAX_CONNECTIONS_SERVER: u32 = 50;
pub const DEFAULT_MAX_CONNECTIONS_WORKER: u32 = 5;
diff --git a/backend/windmill-common/src/variables.rs b/backend/windmill-common/src/variables.rs
index a00ca01856..9dd9edc64d 100644
--- a/backend/windmill-common/src/variables.rs
+++ b/backend/windmill-common/src/variables.rs
@@ -6,7 +6,7 @@
* LICENSE-AGPL for a copy of the license.
*/
-use crate::error;
+use crate::error::{self, Error};
use crate::scripts::ScriptHash;
use crate::utils::WarnAfterExt;
use crate::worker::Connection;
@@ -438,3 +438,37 @@ async fn get_cached_workspace_envs(conn: &Connection, w_id: &str) -> Vec<(String
};
custom_envs
}
+
+pub async fn get_variable_or_self(path: String, db: &DB, w_id: &str) -> crate::error::Result {
+ if !path.starts_with("$var:") {
+ return Ok(path);
+ }
+ let path = path.strip_prefix("$var:").unwrap().to_string();
+
+ let record = sqlx::query!(
+ "SELECT value, is_secret
+ FROM variable
+ WHERE path = $1 AND workspace_id = $2",
+ &path,
+ &w_id
+ )
+ .fetch_optional(db)
+ .await?;
+
+ if let Some(record) = record {
+ let mut value = record.value;
+ if record.is_secret {
+ let mc = build_crypt(db, w_id).await?;
+ value = decrypt(&mc, value).map_err(|e| {
+ Error::internal_err(format!("Error decrypting variable {}: {}", path, e))
+ })?;
+ }
+
+ Ok(value)
+ } else {
+ Err(Error::NotFound(format!(
+ "Variable not found when resolving `$var:{}`",
+ path
+ )))
+ }
+}
diff --git a/backend/windmill-worker/src/ansible_executor.rs b/backend/windmill-worker/src/ansible_executor.rs
index 04567b6217..bcd058836e 100644
--- a/backend/windmill-worker/src/ansible_executor.rs
+++ b/backend/windmill-worker/src/ansible_executor.rs
@@ -13,6 +13,7 @@ use tokio::process::Command;
use uuid::Uuid;
use windmill_common::{
error,
+ git_sync_oss::{get_github_app_token_internal, prepend_token_to_github_url},
worker::{
is_allowed_file_location, to_raw_value, write_file, write_file_at_user_defined_location,
Connection, WORKER_CONFIG,
@@ -20,7 +21,9 @@ use windmill_common::{
};
use windmill_queue::MiniPulledJob;
-use windmill_parser_yaml::{AnsibleRequirements, GitRepo, ResourceOrVariablePath};
+use windmill_parser_yaml::{
+ AnsibleRequirements, GitRepo, PreexistingAnsibleInventory, ResourceOrVariablePath,
+};
use windmill_queue::{append_logs, CanceledBy};
use crate::{
@@ -885,16 +888,41 @@ pub async fn handle_ansible_job(
let inventories: Vec = reqs
.as_ref()
- .map(|x| {
- x.inventories
+ .map(|x| -> Result, _> {
+ let mut ret: Vec = x
+ .inventories
.clone()
.iter()
.flat_map(|i| vec!["-i".to_string(), i.name.clone()].into_iter())
- .collect()
+ .collect();
+
+ let additional: Vec = x
+ .additional_inventories
+ .iter()
+ .map(|i| match i {
+ PreexistingAnsibleInventory::Static(name) => Ok(Some(vec![name.clone()])),
+ PreexistingAnsibleInventory::PassedInArgs(inv_def) => interpolated_args
+ .as_ref()
+ .and_then(|args| args.get(&inv_def.name))
+ .and_then(|v| serde_json::from_str(v.get()).transpose())
+ .transpose(),
+ })
+ .collect::, _>>()?
+ .into_iter()
+ .flatten()
+ .flatten()
+ .flat_map(|name| vec!["-i".to_string(), name])
+ .collect();
+
+ ret.extend(additional);
+ Ok::<_, windmill_common::error::Error>(ret)
})
+ .transpose()?
.unwrap_or_else(|| vec![]);
let mut nsjail_extra_mounts = vec![];
+ let mut playbook_override = None;
+
if let Some(r) = reqs.as_ref() {
nsjail_extra_mounts = create_file_resources(
&job.id,
@@ -907,6 +935,104 @@ pub async fn handle_ansible_job(
)
.await?;
+ if let Some(delegated_git_repo) = r.delegate_to_git_repo.as_ref() {
+ let serde_json::Value::Object(git_repo_resource) = client
+ .get_resource_value_interpolated::(
+ &delegated_git_repo.resource,
+ Some(job.id.to_string()),
+ )
+ .await?
+ else {
+ return Err(windmill_common::error::Error::BadRequest(
+ "Git repository resource is not an object".to_string(),
+ ));
+ };
+
+ let mut secret_url = git_repo_resource.get("url").and_then(|s| s.as_str()).map(|s| s.to_string())
+ .ok_or(anyhow!("Failed to get url from git repo resource, please check that the resource has the correct type (git_repository)"))?;
+
+ let is_github_app = git_repo_resource.get("is_github_app").and_then(|s| s.as_bool())
+ .ok_or(anyhow!("Failed to get `is_github_app` field from git repo resource, please check that the resource has the correct type (git_repository)"))?;
+
+ if is_github_app {
+ if let Connection::Sql(db) = conn {
+ let token = get_github_app_token_internal(db, &client.token).await?;
+ secret_url = prepend_token_to_github_url(&secret_url, &token)?;
+ } else {
+ return Err(windmill_common::error::Error::BadRequest("Github App authentication is currently unavailable for agent workers. Contact the windmill team to request this feature".to_string()));
+ }
+ }
+
+ let branch = Some(git_repo_resource.get("branch").and_then(|s| s.as_str()).map(|s| s.to_string())
+ .ok_or(anyhow!("Failed to get branch from git repo resource, please check that the resource has the correct type (git_repository)"))?).filter(|s| !s.is_empty());
+
+ let target_path = "delegate_git_repository".to_string();
+
+ let repo =
+ GitRepo { url: secret_url, commit: delegated_git_repo.commit.clone(), branch, target_path };
+ append_logs(
+ &job.id,
+ &job.workspace_id,
+ format!("\nCloning {}...\n", delegated_git_repo.resource),
+ conn,
+ )
+ .await;
+ if let Some(commit) = delegated_git_repo.commit.as_ref() {
+ clone_repo_without_history(
+ &repo,
+ commit,
+ job_dir,
+ &job.id,
+ worker_name,
+ conn,
+ mem_peak,
+ canceled_by,
+ &job.workspace_id,
+ occupancy_metrics,
+ git_ssh_cmd,
+ )
+ .await
+ .map_err(|e| anyhow!("Failed to clone git repo `{}`: {e}", repo.url))?;
+ } else {
+ clone_repo(
+ &repo,
+ job_dir,
+ &job.id,
+ worker_name,
+ conn,
+ mem_peak,
+ canceled_by,
+ &job.workspace_id,
+ occupancy_metrics,
+ git_ssh_cmd,
+ )
+ .await
+ .map_err(|e| anyhow!("Failed to clone git repo `{}`: {e}", repo.url))?;
+ }
+
+ append_logs(
+ &job.id,
+ &job.workspace_id,
+ format!(
+ "Cloned {} into {}\n",
+ delegated_git_repo.resource, &repo.target_path
+ ),
+ conn,
+ )
+ .await;
+
+ playbook_override = Some(
+ delegated_git_repo
+ .playbook
+ .as_ref()
+ .map(|p| format!("{}/{}", &repo.target_path, p)),
+ );
+ }
+
+ if playbook_override.clone().flatten().is_none() && playbook.is_empty() {
+ return Err(windmill_common::error::Error::BadRequest("No playbook was specified. Append a playbook to your script or specify one in the delegate_to_git_repo -> playbook section.".to_string()));
+ }
+
for repo in &r.git_repos {
append_logs(
&job.id,
@@ -1060,7 +1186,10 @@ mount {{
reserved_variables.insert("PYTHONPATH".to_string(), additional_python_paths_folders);
}
- let mut cmd_args = vec!["main.yml", "--extra-vars", "@args.json"];
+ let playbook = playbook_override
+ .flatten()
+ .unwrap_or("main.yml".to_string());
+ let mut cmd_args = vec![playbook.as_str(), "--extra-vars", "@args.json"];
cmd_args.extend(inventories.iter().map(|s| s.as_str()));
cmd_args.extend(cmd_options.iter().map(|s| s.as_str()));
diff --git a/flake.nix b/flake.nix
index c6f09a193d..102c79e8dd 100644
--- a/flake.nix
+++ b/flake.nix
@@ -103,6 +103,7 @@
wasm-pack
deno
emscripten
+ nushell
# Needed for extra dependencies
glibc_multi
]);
@@ -231,17 +232,31 @@
set -e
cd ./backend
mkdir -p .minio-data/wmill
- ${pkgs.minio}/bin/minio server ./.minio-data
+ ${pkgs.minio}/bin/minio server ./.minio-data --console-address ":9001"
'')
# Generate keys
# TODO: Do not set new keys if ran multiple times
(pkgs.writeScriptBin "wm-minio-keys" ''
set -e
cd ./backend
+
+ # Set up MinIO alias
${pkgs.minio-client}/bin/mc alias set 'wmill-minio-dev' 'http://localhost:9000' 'minioadmin' 'minioadmin'
- ${pkgs.minio-client}/bin/mc admin accesskey create 'wmill-minio-dev' | tee .minio-data/secrets.txt
- echo ""
- echo 'Saving to: ./backend/.minio-data/secrets.txt'
+
+ # Check if secrets file exists and contains valid keys
+ if [[ -f .minio-data/secrets.txt ]] && [[ -s .minio-data/secrets.txt ]]; then
+ echo "Access keys already exist:"
+ cat .minio-data/secrets.txt
+ echo ""
+ echo "Keys loaded from: ./backend/.minio-data/secrets.txt"
+ else
+ echo "Creating new access keys..."
+ mkdir -p .minio-data
+ ${pkgs.minio-client}/bin/mc admin accesskey create 'wmill-minio-dev' | tee .minio-data/secrets.txt
+ echo ""
+ echo 'New keys saved to: ./backend/.minio-data/secrets.txt'
+ fi
+
echo "bucket: wmill"
echo "endpoint: http://localhost:9000"
'')
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index ee1ce2687b..3910920c3f 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -83,7 +83,7 @@
"windmill-parser-wasm-ruby": "1.526.1",
"windmill-parser-wasm-rust": "1.558.1",
"windmill-parser-wasm-ts": "1.538.0",
- "windmill-parser-wasm-yaml": "1.510.1",
+ "windmill-parser-wasm-yaml": "1.558.1",
"windmill-sql-datatype-parser-wasm": "1.512.0",
"windmill-utils-internal": "^1.3.1",
"xterm": "^5.3.0",
@@ -13779,9 +13779,9 @@
"integrity": "sha512-hHhMIVIPhmsHx0lsNCGMoIa7cDBFlVWhhd9j/5yOIq2sxwqg5sl5juQIIJGvuwg5umdsD0ChlSm2/uES78DLYg=="
},
"node_modules/windmill-parser-wasm-yaml": {
- "version": "1.510.1",
- "resolved": "https://registry.npmjs.org/windmill-parser-wasm-yaml/-/windmill-parser-wasm-yaml-1.510.1.tgz",
- "integrity": "sha512-zQ1imcKrhP3iccJ01BK0+tptguo3Xc+J5ku2lgrZ+YQdDcC2wjGb6gH+kvcsMXDE3WT4aRwV3nL+ecHa7WHSrw=="
+ "version": "1.558.1",
+ "resolved": "https://registry.npmjs.org/windmill-parser-wasm-yaml/-/windmill-parser-wasm-yaml-1.558.1.tgz",
+ "integrity": "sha512-KBaSekkFiLJP5GpeArctupHStfp9/aWpNeT9my8Cp+QJB2TziJSf3FQ4k9mxJcKbjUQ65vO0jBtdNDvrnYufqQ=="
},
"node_modules/windmill-sql-datatype-parser-wasm": {
"version": "1.512.0",
diff --git a/frontend/package.json b/frontend/package.json
index 4d04a38246..e90f3672c2 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -148,7 +148,7 @@
"windmill-parser-wasm-ruby": "1.526.1",
"windmill-parser-wasm-rust": "1.558.1",
"windmill-parser-wasm-ts": "1.538.0",
- "windmill-parser-wasm-yaml": "1.510.1",
+ "windmill-parser-wasm-yaml": "1.558.1",
"windmill-sql-datatype-parser-wasm": "1.512.0",
"windmill-utils-internal": "^1.3.1",
"xterm": "^5.3.0",
@@ -541,4 +541,4 @@
"@rollup/rollup-linux-x64-gnu": "^4.35.0",
"fsevents": "^2.3.3"
}
-}
\ No newline at end of file
+}
diff --git a/frontend/src/lib/ansibleUtils.ts b/frontend/src/lib/ansibleUtils.ts
new file mode 100644
index 0000000000..762c1793a2
--- /dev/null
+++ b/frontend/src/lib/ansibleUtils.ts
@@ -0,0 +1,384 @@
+interface DelegateToGitRepoConfig {
+ resource?: string
+ playbook?: string
+ inventories_location?: string
+}
+
+/**
+ * Updates a specific field in the delegate_to_git_repo section
+ * @param code - The current YAML script content
+ * @param fieldName - The field name to update (resource, playbook, inventories_location)
+ * @param value - The value to set (or undefined to remove the field)
+ * @returns The modified YAML script content
+ */
+export function updateDelegateToGitRepoField(code: string, fieldName: string, value: string | undefined): string {
+ const lines = code.split('\n')
+
+ // Find delegate_to_git_repo section
+ const delegateLineIndex = lines.findIndex(line => line.trim().startsWith('delegate_to_git_repo:'))
+
+ if (delegateLineIndex === -1) {
+ // If no delegate section exists and we're setting a value, create the whole section
+ if (value !== undefined) {
+ return insertDelegateToGitRepoSection(code, { [fieldName]: value })
+ }
+ return code
+ }
+
+ // Find the specific field line
+ const fieldLineIndex = lines.findIndex((line, index) =>
+ index > delegateLineIndex && line.trim().startsWith(`${fieldName}:`)
+ )
+
+ if (fieldLineIndex !== -1) {
+ if (value !== undefined) {
+ // Update existing field
+ lines[fieldLineIndex] = ` ${fieldName}: ${value}`
+ } else {
+ // Remove field
+ lines.splice(fieldLineIndex, 1)
+ }
+ } else if (value !== undefined) {
+ // Add new field after delegate_to_git_repo line
+ lines.splice(delegateLineIndex + 1, 0, ` ${fieldName}: ${value}`)
+ }
+
+ return lines.join('\n')
+}
+
+/**
+ * Inserts or updates multiple fields in a delegate_to_git_repo section
+ * @param code - The current YAML script content
+ * @param config - Configuration object with fields to update
+ * @returns The modified YAML script content
+ */
+export function updateDelegateToGitRepoConfig(code: string, config: DelegateToGitRepoConfig): string {
+ let updatedCode = code
+
+ // Update each field that's provided
+ for (const [fieldName, value] of Object.entries(config)) {
+ if (value !== undefined) {
+ updatedCode = updateDelegateToGitRepoField(updatedCode, fieldName, value)
+ }
+ }
+
+ return updatedCode
+}
+
+/**
+ * Legacy function for backward compatibility
+ * Inserts or updates a delegate_to_git_repo section in an Ansible YAML script
+ * @param code - The current YAML script content
+ * @param resourcePath - The git repository resource path to delegate to
+ * @returns The modified YAML script content
+ */
+export function insertDelegateToGitRepoInCode(code: string, resourcePath: string): string {
+ return updateDelegateToGitRepoField(code, 'resource', resourcePath)
+}
+
+/**
+ * Inserts a new delegate_to_git_repo section with the given configuration
+ * @param code - The current YAML script content
+ * @param config - Configuration object with fields to set
+ * @returns The modified YAML script content
+ */
+function insertDelegateToGitRepoSection(code: string, config: DelegateToGitRepoConfig): string {
+ const lines = code.split('\n')
+
+ // Build the delegate section with all provided fields
+ const delegateSection = ['delegate_to_git_repo:']
+
+ // Add fields in a consistent order
+ if (config.resource) {
+ delegateSection.push(` resource: ${config.resource}`)
+ }
+ if (config.playbook) {
+ delegateSection.push(` playbook: ${config.playbook}`)
+ }
+ if (config.inventories_location) {
+ delegateSection.push(` inventories_location: ${config.inventories_location}`)
+ }
+
+ // Find a good insertion point (after ---, then after inventories if they exist, otherwise at the top)
+ let insertionIndex = 0
+
+ // First, skip whitespace and find document start marker ---
+ for (let i = 0; i < lines.length; i++) {
+ const trimmedLine = lines[i].trim()
+ if (trimmedLine === '---') {
+ insertionIndex = i + 1 // Start after the document marker
+ break
+ } else if (trimmedLine && !trimmedLine.startsWith('#')) {
+ // Hit non-comment, non-whitespace content without finding ---, stop looking
+ break
+ }
+ }
+
+ // Look for the end of inventories section
+ for (let i = insertionIndex; i < lines.length; i++) {
+ const line = lines[i].trim()
+ if (line.startsWith('inventories:')) {
+ // Find the end of inventories section
+ for (let j = i + 1; j < lines.length; j++) {
+ const nextLine = lines[j].trim()
+ if (nextLine && !nextLine.startsWith('-') && !nextLine.startsWith(' ') && !nextLine.startsWith('#')) {
+ insertionIndex = j
+ break
+ }
+ }
+ break
+ } else if (line && !line.startsWith('#') && insertionIndex <= 1) {
+ // First non-comment line after ---, insert before it
+ insertionIndex = i
+ break
+ }
+ }
+
+ // Insert the delegate section
+ lines.splice(insertionIndex, 0, ...delegateSection, '')
+
+ return lines.join('\n')
+}
+
+/**
+ * Generic function to extract a specific field from delegate_to_git_repo section
+ * @param code - The YAML script content
+ * @param fieldName - The field name to extract (resource, playbook, inventories_location)
+ * @returns The field value if found, undefined otherwise
+ */
+function extractDelegateToGitRepoField(code: string, fieldName: string): string | undefined {
+ const lines = code.split('\n')
+
+ // Find delegate_to_git_repo section
+ const delegateLineIndex = lines.findIndex(line => line.trim().startsWith('delegate_to_git_repo:'))
+
+ if (delegateLineIndex === -1) {
+ return undefined
+ }
+
+ // Look for the field line after delegate_to_git_repo
+ for (let i = delegateLineIndex + 1; i < lines.length; i++) {
+ const line = lines[i].trim()
+ if (line.startsWith(`${fieldName}:`)) {
+ // Extract the field value (everything after "fieldName:")
+ const fieldMatch = line.match(new RegExp(`^${fieldName}:\\s*(.+)$`))
+ return fieldMatch?.[1]?.trim()
+ } else if (line && !line.startsWith(' ') && !line.startsWith('\t')) {
+ // Hit a new top-level section, stop looking
+ break
+ }
+ }
+
+ return undefined
+}
+
+/**
+ * Extracts the current git repository resource from delegate_to_git_repo section
+ * @param code - The YAML script content
+ * @returns The resource path if found, undefined otherwise
+ */
+export function extractCurrentGitRepoResource(code: string): string | undefined {
+ return extractDelegateToGitRepoField(code, 'resource')
+}
+
+/**
+ * Extracts the current playbook path from delegate_to_git_repo section
+ * @param code - The YAML script content
+ * @returns The playbook path if found, undefined otherwise
+ */
+export function extractDelegateToGitRepoPlaybook(code: string): string | undefined {
+ return extractDelegateToGitRepoField(code, 'playbook')
+}
+
+/**
+ * Extracts the current inventories location from delegate_to_git_repo section
+ * @param code - The YAML script content
+ * @returns The inventories location if found, undefined otherwise
+ */
+export function extractDelegateToGitRepoInventoriesLocation(code: string): string | undefined {
+ return extractDelegateToGitRepoField(code, 'inventories_location')
+}
+
+/**
+ * Extracts all delegate_to_git_repo configuration from the code
+ * @param code - The YAML script content
+ * @returns Configuration object with all extracted fields
+ */
+export function extractDelegateToGitRepoConfig(code: string): DelegateToGitRepoConfig {
+ return {
+ resource: extractCurrentGitRepoResource(code),
+ playbook: extractDelegateToGitRepoPlaybook(code),
+ inventories_location: extractDelegateToGitRepoInventoriesLocation(code)
+ }
+}
+
+/**
+ * Inserts or updates additional_inventories section in an Ansible YAML script
+ * @param code - The current YAML script content
+ * @param inventoryPaths - Array of inventory file paths
+ * @returns The modified YAML script content
+ */
+export function insertAdditionalInventories(code: string, inventoryPaths: string[]): string {
+ const lines = code.split('\n')
+
+ // Find and update existing additional_inventories section if it exists
+ const additionalInventoriesIndex = lines.findIndex(line => line.trim().startsWith('additional_inventories:'))
+ if (additionalInventoriesIndex !== -1) {
+ // Determine the indentation level of the additional_inventories line
+ const sectionLine = lines[additionalInventoriesIndex]
+ const sectionIndentation = sectionLine.length - sectionLine.trimStart().length
+
+ // Find the options: field within the section
+ let optionsIndex = -1
+ let optionsEndIndex = -1
+
+ for (let i = additionalInventoriesIndex + 1; i < lines.length; i++) {
+ const line = lines[i]
+ const trimmedLine = line.trim()
+
+ // Skip empty lines
+ if (!trimmedLine) {
+ continue
+ }
+
+ // Calculate indentation of current line
+ const currentIndentation = line.length - line.trimStart().length
+
+ // If we find a line with same or lesser indentation than the section header,
+ // we've reached the end of the additional_inventories section
+ if (currentIndentation <= sectionIndentation) {
+ break
+ }
+
+ // Look for options: field (should be directly under additional_inventories)
+ if (trimmedLine.startsWith('- options:') && currentIndentation > sectionIndentation) {
+ optionsIndex = i
+
+ // Check if it's inline format: options: [...]
+ if (trimmedLine.includes('[') && trimmedLine.includes(']')) {
+ // Inline format - just this line
+ optionsEndIndex = i + 1
+ break
+ } else {
+ // Dash format - find all the dash items
+ optionsEndIndex = i + 1
+ for (let j = i + 1; j < lines.length; j++) {
+ const nextLine = lines[j]
+ const nextTrimmed = nextLine.trim()
+ const nextIndentation = nextLine.length - nextLine.trimStart().length
+
+ // Skip empty lines
+ if (!nextTrimmed) {
+ continue
+ }
+
+ // If we hit a line that's not more indented than options:, we're done
+ if (nextIndentation <= currentIndentation) {
+ break
+ }
+
+ // If it's a dash item, include it
+ if (nextTrimmed.startsWith('-')) {
+ optionsEndIndex = j + 1
+ } else {
+ // Hit a non-dash line that's indented - stop here
+ break
+ }
+ }
+ break
+ }
+ }
+ }
+
+ // Format the new options content
+ const optionsIndentation = ' ' // Standard 2-space indentation under additional_inventories
+ const formattedPaths = inventoryPaths.map(path => `"delegated_git_repository/${path}"`)
+ const inlineFormat = `${optionsIndentation}- options: [${formattedPaths.join(', ')}]`
+
+ let newOptionsContent: string[]
+ if (inlineFormat.length <= 100) {
+ // Use inline format
+ newOptionsContent = [inlineFormat]
+ } else {
+ // Use dash format
+ newOptionsContent = [`${optionsIndentation}- options:`]
+ inventoryPaths.forEach(path => {
+ newOptionsContent.push(`${optionsIndentation} - "delegated_git_repository/${path}"`)
+ })
+ }
+
+ if (optionsIndex !== -1) {
+ // Replace existing options: field
+ lines.splice(optionsIndex, optionsEndIndex - optionsIndex, ...newOptionsContent)
+ } else {
+ // Add options: field to existing section (right after the section header)
+ lines.splice(additionalInventoriesIndex + 1, 0, ...newOptionsContent)
+ }
+
+ return lines.join('\n')
+ }
+
+ // Format the inventory paths based on length
+ const formattedPaths = inventoryPaths.map(path => `"delegated_git_repository/${path}"`)
+ const inlineFormat = `options: [${formattedPaths.join(', ')}]`
+
+ let inventorySection: string[]
+ if (inlineFormat.length <= 100) {
+ // Use inline format
+ inventorySection = [
+ 'additional_inventories:',
+ ` ${inlineFormat}`
+ ]
+ } else {
+ // Use dash format with each item on new line
+ inventorySection = [
+ 'additional_inventories:',
+ ' - options:'
+ ]
+ inventoryPaths.forEach(path => {
+ inventorySection.push(` - "delegated_git_repository/${path}"`)
+ })
+ }
+
+ // Find insertion point (after the complete delegate_to_git_repo section)
+ const delegateLineIndex = lines.findIndex(line => line.trim().startsWith('delegate_to_git_repo:'))
+ if (delegateLineIndex === -1) {
+ // If no delegate_to_git_repo section, insert at the beginning (after document marker if exists)
+ let insertionIndex = 0
+ for (let i = 0; i < lines.length; i++) {
+ const trimmedLine = lines[i].trim()
+ if (trimmedLine === '---') {
+ insertionIndex = i + 1
+ break
+ } else if (trimmedLine && !trimmedLine.startsWith('#')) {
+ break
+ }
+ }
+ lines.splice(insertionIndex, 0, ...inventorySection, '')
+ } else {
+ // Find the last actual content line of the delegate_to_git_repo section
+ let lastContentIndex = delegateLineIndex
+ for (let i = delegateLineIndex + 1; i < lines.length; i++) {
+ const line = lines[i]
+ const trimmedLine = line.trim()
+
+ // If we hit a non-empty line that doesn't start with whitespace (not indented),
+ // we've reached the end of the delegate_to_git_repo section
+ if (trimmedLine && !line.startsWith(' ') && !line.startsWith('\t')) {
+ break
+ }
+
+ // If this is an indented non-empty line, it's part of delegate_to_git_repo
+ if (trimmedLine && (line.startsWith(' ') || line.startsWith('\t'))) {
+ lastContentIndex = i
+ }
+ }
+
+ // Insert right after the last content line of delegate_to_git_repo
+ const insertionIndex = lastContentIndex + 1
+ lines.splice(insertionIndex, 0, ...inventorySection, '')
+ }
+
+ return lines.join('\n')
+}
+
diff --git a/frontend/src/lib/components/EditorBar.svelte b/frontend/src/lib/components/EditorBar.svelte
index f81cb5ce78..b66834a49a 100644
--- a/frontend/src/lib/components/EditorBar.svelte
+++ b/frontend/src/lib/components/EditorBar.svelte
@@ -31,6 +31,7 @@
DiffIcon,
DollarSign,
File,
+ GitBranch,
History,
Library,
Link,
@@ -54,6 +55,8 @@
import DucklakeIcon from './icons/DucklakeIcon.svelte'
import FlowInlineScriptAiButton from './copilot/FlowInlineScriptAIButton.svelte'
import ScriptGen from './copilot/ScriptGen.svelte'
+ import GitRepoPopoverPicker from './GitRepoPopoverPicker.svelte'
+ import { insertDelegateToGitRepoInCode } from '$lib/ansibleUtils'
interface Props {
lang: SupportedLanguage | 'bunnative' | undefined
@@ -120,6 +123,7 @@
let s3FilePicker: S3FilePicker | undefined = $state()
let ducklakePicker: ItemPicker | undefined = $state()
let databasePicker: ItemPicker | undefined = $state()
+ let gitRepoPickerOpen = $state(false)
let showContextVarPicker = $derived(
[
@@ -194,6 +198,7 @@
)
let showDucklakePicker = $derived(['duckdb'].includes(lang ?? ''))
let showDatabasePicker = $derived(['duckdb'].includes(lang ?? ''))
+ let showGitRepoPicker = $derived(lang === 'ansible')
let showResourceTypePicker = $derived(
['typescript', 'javascript'].includes(scriptLangToEditorLang(lang)) ||
@@ -205,6 +210,14 @@
let codeViewer: Drawer | undefined = $state()
let codeObj: { language: SupportedLanguage; content: string } | undefined = $state(undefined)
+ function insertDelegateToGitRepo(resourcePath: string) {
+ if (!editor) return
+
+ const currentCode = editor.getCode()
+ const newCode = insertDelegateToGitRepoInCode(currentCode, resourcePath)
+ editor.setCode(newCode)
+ }
+
function addEditorActions() {
editor?.addAction('insert-variable', 'Windmill: Insert variable', () => {
variablePicker?.openDrawer()
@@ -845,6 +858,28 @@ JsonNode ${windmillPathToCamelCaseName(path)} = JsonNode.Parse(await client.GetS
{/if}
+ {#if showGitRepoPicker && customUi?.resource != false}
+ insertDelegateToGitRepo(e.detail.resourcePath)}
+ >
+ (gitRepoPickerOpen = true)}
+ {iconOnly}
+ startIcon={{ icon: GitBranch }}
+ >
+ +Git Repo
+
+
+ {/if}
+
{#if showResourceTypePicker && customUi?.type != false}
+ import { ResourceService } from '$lib/gen'
+ import { workspaceStore } from '$lib/stores'
+ import { Popover } from './meltComponents'
+ import { GitBranch, Loader2 } from 'lucide-svelte'
+ import { createEventDispatcher, untrack } from 'svelte'
+
+ interface Props {
+ isOpen?: boolean
+ children?: import('svelte').Snippet
+ }
+
+ let {
+ isOpen = $bindable(false),
+ children
+ }: Props = $props()
+
+ const dispatch = createEventDispatcher<{
+ selected: { resourcePath: string }
+ }>()
+
+ let loading = $state(false)
+ let gitRepoResources = $state<{ path: string; description?: string }[]>([])
+
+ async function loadGitRepoResources() {
+ if (!$workspaceStore || loading) return
+
+ loading = true
+ try {
+ const resources = await ResourceService.listResource({
+ workspace: $workspaceStore,
+ resourceType: 'git_repository'
+ })
+
+ gitRepoResources = resources
+ } catch (error) {
+ console.error('Failed to load git repository resources:', error)
+ gitRepoResources = []
+ } finally {
+ loading = false
+ }
+ }
+
+ $effect(() => {
+ if (isOpen && $workspaceStore) {
+ untrack(() => loadGitRepoResources())
+ }
+ })
+
+ function handleSelect(resourcePath: string) {
+ dispatch('selected', { resourcePath })
+ isOpen = false
+ }
+
+
+
+ {#snippet trigger()}
+ {@render children?.()}
+ {/snippet}
+ {#snippet content()}
+
+ {#if loading}
+
+
+ Loading...
+
+ {:else if gitRepoResources.length === 0}
+
+
+
No git repositories found
+
Create a git repository resource first
+
+ {:else}
+
+ {#each gitRepoResources as resource}
+
handleSelect(resource.path)}
+ >
+
+
+
{resource.path}
+ {#if resource.description}
+
{resource.description}
+ {/if}
+
+
+ {/each}
+
+ {/if}
+
+ {/snippet}
+
\ No newline at end of file
diff --git a/frontend/src/lib/components/GitRepoResourcePicker.svelte b/frontend/src/lib/components/GitRepoResourcePicker.svelte
new file mode 100644
index 0000000000..bc5676370f
--- /dev/null
+++ b/frontend/src/lib/components/GitRepoResourcePicker.svelte
@@ -0,0 +1,275 @@
+
+
+
+
+
+
+
Git Repository Resource
+
+ {#if currentResource}
+
+
+
+
+
+ Currently delegating to:
+
+
+ {currentResource}
+
+
+
+
+ {/if}
+
+ {#if loading}
+
+
+ Loading git repository resources...
+
+ {:else if gitRepoResources.length === 0}
+
+
+
No git repository resources found
+
+ Create a git repository resource first to use this feature
+
+
+ {:else}
+
+ {/if}
+
+
+
+
+
+ Playbook Path
+ (optional)
+
+
+
+ Specify the path to your main playbook file relative to the git repository root
+
+
+
+
+
+
+ Cancel
+
+ Apply Configuration
+
+
+ {#if selectedResource}
+
+
+ Inventories Location
+
+
+
+ Specify the directory containing your inventory files relative to the git repository
+ root
+
+
+
+
+ {#if loadingInventories}
+ Loading inventories...
+ {:else}
+ Add available inventories to script as options
+ {/if}
+
+
+
+ {/if}
+
+
+
diff --git a/frontend/src/lib/components/GitRepoViewer.svelte b/frontend/src/lib/components/GitRepoViewer.svelte
new file mode 100644
index 0000000000..35ada1ee61
--- /dev/null
+++ b/frontend/src/lib/components/GitRepoViewer.svelte
@@ -0,0 +1,183 @@
+
+
+{#if error}
+
+ {error}
+
+{:else if isLoadingCommitHash}
+
+
+ Fetching latest commit hash...
+
+{:else if isLoadingRepoClone}
+
+
+ Cloning repository...
+
+{:else if commitHash && isCheckingPathExists}
+
+
+ Checking repository availability...
+
+{:else if commitHash && pathExists === false}
+
+
+ The git repository content is not yet available in storage.
+
+
Load Repository Contents
+
+{:else if commitHash && pathExists === true}
+ {
+ const bucketConfig: any = await SettingService.getGlobal({ key: 'object_store_cache_config' })
+ return SettingService.testObjectStorageConfig({
+ requestBody: bucketConfig
+ })
+ }) as any}
+ loadFileMetadataRequest={HelpersService.loadGitRepoFileMetadata}
+ >
+ {#snippet replaceUnauthorizedWarning()}
+
+
+
+ The git repo resource you are trying to access either doesn't exist or you don't have
+ access to it. Make sure the resource path is correct and that you have visibility over
+ the resource.
+
+
+
+ {/snippet}
+
+{/if}
diff --git a/frontend/src/lib/components/S3FilePicker.svelte b/frontend/src/lib/components/S3FilePicker.svelte
index c98725c287..338a08ea02 100644
--- a/frontend/src/lib/components/S3FilePicker.svelte
+++ b/frontend/src/lib/components/S3FilePicker.svelte
@@ -1,55 +1,15 @@
@@ -508,413 +43,32 @@
{
- dispatch(
- 'close',
- selectedFileKey?.s3
- ? {
- s3: selectedFileKey.s3,
- storage: storage
- }
- : undefined
- )
+ dispatch('close')
+ s3FilePickerInner?.close?.()
}}
size="1200px"
>
{
+ s3FilePickerInner?.exit?.()
+ drawer?.closeDrawer?.()
+ }}
tooltip="Files present in the Workspace S3 bucket. You can set the workspace S3 bucket in the settings."
documentationLink="https://www.windmill.dev/docs/integrations/s3"
>
- {#if workspaceSettingsInitialized === false}
- {#if fromWorkspaceSettings}
-
-
-
- Double check the S3 resource fields and try again.
-
-
-
- {:else}
-
-
-
- The workspace needs to be connected to an S3 storage to use this feature. You can configure it here .
-
-
-
-
- {/if}
- {:else}
- {#if fileListUnavailable == true}
-
-
-
- You don't have access to the S3 bucket resource and your administrator has restricted
- the access to it. You are not authorized to browse the bucket content. If you think
- this is incorrect, please contact your workspace administrator.
-
-
- More info in Windmill's documentation
-
- {/if}
-
- {#if !fileListUnavailable}
-
-
-
-
- {#if fileListLoading === false && displayedFileKeys.length === 0}
-
- No files in the workspace S3 bucket at that prefix
-
- {:else}
-
-
- {#snippet header()}{/snippet}
- {#snippet footer()}{/snippet}
- {#snippet item({ index, style })}
- {@const file_info = allFilesByKey[displayedFileKeys[index]]}
-
-
- {#if file_info}
-
selectItem(index)}
- class={twMerge(
- 'flex flex-row h-full font-semibold text-xs items-center justify-start',
- selectedFileKey !== undefined &&
- selectedFileKey.s3 === file_info.full_key
- ? 'bg-surface-hover'
- : ''
- )}
- >
-
- {#if file_info.type === 'folder'}
- {#if file_info.collapsed}
{:else}
{/if}
-
- {file_info.display_name} ({file_info.count}{count % 1000 === 0 &&
- lastKeyFolders[file_info.nestingLevel / 2] ===
- file_info.display_name
- ? '+'
- : ''} item{file_info.count === 1 ? '' : 's'})
-
- {:else}
-
-
- {file_info.display_name}
-
- {/if}
-
-
- {/if}
-
- {/snippet}
-
-
-
- {#if fileListLoading === true}
-
- Loading content
-
- {:else}
-
- {displayedCount}{count % maxKeys === 0 ? '+' : ''}
- {displayedCount !== count ? 'filtered ' : ''}items (including inside folders)
-
-
- {#if count % maxKeys === 0}
-
{
- page += 1
- loadFiles()
- }}
- >
- Load more
-
- {/if}
- {/if}
-
- {/if}
-
- {/if}
-
- {#if fileMetadata === undefined}
-
- {#if fileInfoLoading}
-
- {:else if fileListUnavailable}
-
- {:else}
-
- {/if}
-
- {:else}
-
-
- {#snippet action()}
-
- {#if filePreview !== undefined}
-
- {
- moveDestKey = fileMetadata?.fileKey ?? ''
- moveModalOpen = true
- }}
- startIcon={{ icon: MoveRight }}
- iconOnly={true}
- />
- {
- deletionModalOpen = true
- }}
- startIcon={{ icon: Trash }}
- iconOnly={true}
- />
- {/if}
-
- {/snippet}
-
-
-
- {/if}
-
-
- {#if filePreviewLoading || fileMetadata}
- {#if fileMetadata?.fileKey.endsWith('.png') || fileMetadata?.fileKey.endsWith('.jpg') || fileMetadata?.fileKey.endsWith('.jpeg') || fileMetadata?.fileKey.endsWith('.webp')}
-
-
-
- {:else if fileMetadata?.fileKey.endsWith('.pdf')}
-
- {#await import('$lib/components/display/PdfViewer.svelte')}
-
- {:then Module}
-
- {/await}
-
- {:else if filePreviewLoading}
-
- File preview loading
-
- {:else if fileMetadata !== undefined && filePreview !== undefined}
-
- {#if filePreview.contentType === 'Unknown'}
- Type of file not supported for preview.
- {:else if filePreview.contentType === 'Csv'}
- Previewing a {filePreview.contentType?.toLowerCase()} file. Separator character:
-
-
- loadFilePreview(
- fileMetadata?.fileKey ?? '',
- fileMetadata?.size,
- fileMetadata?.mimeType
- )}
- >
- ,
- ;
- \t
- |
-
-
- Header row:
-
-
- loadFilePreview(
- fileMetadata?.fileKey ?? '',
- fileMetadata?.size,
- fileMetadata?.mimeType
- )
- )}
- />
-
- {:else}
- Previewing a {filePreview.contentType?.toLowerCase()} file.
- {/if}
-
-
{#if !emptyString(filePreview.contentPreview)}{filePreview.contentPreview}{:else if filePreview.contentType !== undefined}Preview impossible.{/if}
-
- {/if}
- {/if}
-
-
-
- {/if}
-
- {#snippet actions()}
-
- {#if secondaryStorageNames.value?.length}
- ({ value }))
- ]}
- placeholder="Default storage"
- bind:value={
- () => storage,
- (v) => {
- if (v === storage) return
- storage = v
- reloadContent()
- }
- }
- />
- {/if}
- {#if !readOnlyMode}
- {
- uploadModalOpen = true
- }}>Upload File
- {#if !fromWorkspaceSettings}
- Select
- {/if}
- {/if}
-
- {/snippet}
+ {
+ dispatch('selectAndClose', e.detail)
+ drawer?.closeDrawer?.()
+ }}
+ {fromWorkspaceSettings}
+ {readOnlyMode}
+ bind:initialFileKey
+ bind:selectedFileKey
+ {folderOnly}
+ {regexFilter}
+ />
-
- {
- deletionModalOpen = false
- }}
- on:confirmed={() => {
- deleteFileFromS3(fileMetadata?.fileKey)
- }}
- keyListen={false}
- loading={fileDeletionInProgress}
->
-
- Are you sure you want to permanently delete {fileMetadata?.fileKey} from the S3 bucket?
-
-
-
- {
- moveModalOpen = false
- }}
- on:confirmed={() => {
- moveS3File(fileMetadata?.fileKey, moveDestKey)
- }}
- keyListen={false}
- loading={fileMoveInProgress}
->
-
-
- New key:
-
-
-
Are you sure you want to permanently move {fileMetadata?.fileKey}?
-
-
-
- {
- uploadModalOpen = false
- if (evt.detail !== undefined && evt.detail !== null) {
- selectedFileKey = { s3: evt.detail, storage }
- await clearAndLoadFiles()
- loadFileMetadataPlusPreviewAsync(evt.detail)
- }
- }}
-/>
diff --git a/frontend/src/lib/components/S3FilePickerInner.svelte b/frontend/src/lib/components/S3FilePickerInner.svelte
new file mode 100644
index 0000000000..99c107308e
--- /dev/null
+++ b/frontend/src/lib/components/S3FilePickerInner.svelte
@@ -0,0 +1,886 @@
+
+
+{#if workspaceSettingsInitialized === false}
+ {#if fromWorkspaceSettings}
+
+
+
Double check the S3 resource fields and try again.
+
+
+ {:else}
+
+
+
+ The workspace needs to be connected to an S3 storage to use this feature. You can configure it here .
+
+
+
+
+ {/if}
+{:else}
+ {#if fileListUnavailable == true}
+ {#if replaceUnauthorizedWarning}
+ {@render replaceUnauthorizedWarning()}
+ {:else}
+
+
+
+ You don't have access to the S3 bucket resource and your administrator has restricted the
+ access to it. You are not authorized to browse the bucket content. If you think this is
+ incorrect, please contact your workspace administrator.
+
+
+ More info in Windmill's documentation
+
+ {/if}
+ {/if}
+
+ {#if !fileListUnavailable}
+
+ {#if !rootPath}
+
+
+
+ {/if}
+ {#if fileListLoading === false && displayedFileKeys.length === 0}
+
+ No files in the workspace S3 bucket at that prefix
+
+ {:else}
+
+
+ {#snippet header()}{/snippet}
+ {#snippet footer()}{/snippet}
+ {#snippet item({ index, style })}
+ {@const file_info = allFilesByKey[displayedFileKeys[index]]}
+
+
+ {#if file_info}
+ {@const nestingLevel = file_info.nestingLevel - 2 * rootPathNestingLevel}
+
+
+
selectItem(index)}
+ class={twMerge(
+ 'flex flex-row h-full font-semibold text-xs items-center justify-start',
+ selectedFileKey !== undefined && selectedFileKey.s3 === file_info.full_key
+ ? 'bg-surface-hover'
+ : ''
+ )}
+ >
+
+ {#if file_info.type === 'folder'}
+ {#if file_info.collapsed}
{:else}
{/if}
+
+ {file_info.display_name} ({file_info.count}{count % 1000 === 0 &&
+ lastKeyFolders[file_info.nestingLevel / 2] === file_info.display_name
+ ? '+'
+ : ''} item{file_info.count === 1 ? '' : 's'})
+
+ {:else}
+
+
+ {file_info.display_name}
+
+ {/if}
+
+
+ {/if}
+
+ {/snippet}
+
+
+
+ {#if fileListLoading === true}
+
+ Loading content
+
+ {:else}
+
+ {displayedCount}{count % maxKeys === 0 ? '+' : ''}
+ {displayedCount !== count ? 'filtered ' : ''}items (including inside folders)
+
+
+ {#if count % maxKeys === 0}
+
{
+ page += 1
+ loadFiles()
+ }}
+ >
+ Load more
+
+ {/if}
+ {/if}
+
+ {/if}
+
+ {/if}
+
+ {#if fileMetadata === undefined}
+
+ {#if fileInfoLoading}
+
+ {:else if fileListUnavailable}
+
+ {:else}
+
+ {/if}
+
+ {:else}
+
+
p.startsWith(rootPath) ? p.slice(rootPath.length) : p)(fileMetadata.fileKey)} breakAll>
+ {#snippet action()}
+
+ {#if filePreview !== undefined}
+ {#if !hideS3SpecificDetails}
+
+ {/if}
+ {#if !readOnlyMode}
+ {
+ moveDestKey = fileMetadata?.fileKey ?? ''
+ moveModalOpen = true
+ }}
+ startIcon={{ icon: MoveRight }}
+ iconOnly={true}
+ />
+ {
+ deletionModalOpen = true
+ }}
+ startIcon={{ icon: Trash }}
+ iconOnly={true}
+ />
+ {/if}
+ {/if}
+
+ {/snippet}
+
+ {#if !hideS3SpecificDetails}
+
+ {/if}
+
+ {/if}
+
+
+ {#if filePreviewLoading || fileMetadata}
+ {#if fileMetadata?.fileKey.endsWith('.png') || fileMetadata?.fileKey.endsWith('.jpg') || fileMetadata?.fileKey.endsWith('.jpeg') || fileMetadata?.fileKey.endsWith('.webp')}
+
+
+
+ {:else if fileMetadata?.fileKey.endsWith('.pdf')}
+
+ {#await import('$lib/components/display/PdfViewer.svelte')}
+
+ {:then Module}
+
+ {/await}
+
+ {:else if filePreviewLoading}
+
+ File preview loading
+
+ {:else if fileMetadata !== undefined && filePreview !== undefined}
+
+ {#if filePreview.contentType === 'Unknown'}
+ Type of file not supported for preview.
+ {:else if filePreview.contentType === 'Csv'}
+ Previewing a {filePreview.contentType?.toLowerCase()} file. Separator character:
+
+
+ loadFilePreview(
+ fileMetadata?.fileKey ?? '',
+ fileMetadata?.size,
+ fileMetadata?.mimeType
+ )}
+ >
+ ,
+ ;
+ \t
+ |
+
+
+ Header row:
+
+
+ loadFilePreview(
+ fileMetadata?.fileKey ?? '',
+ fileMetadata?.size,
+ fileMetadata?.mimeType
+ )
+ )}
+ />
+
+ {:else if !hideS3SpecificDetails}
+ Previewing a {filePreview.contentType?.toLowerCase()} file.
+ {/if}
+
+
{#if !emptyString(filePreview.contentPreview)}{filePreview.contentPreview}{:else if filePreview.contentType !== undefined}Preview impossible.{/if}
+
+ {/if}
+ {/if}
+
+
+
+{/if}
+
+
+ {
+ deletionModalOpen = false
+ }}
+ on:confirmed={() => {
+ deleteFileFromS3(fileMetadata?.fileKey)
+ }}
+ keyListen={false}
+ loading={fileDeletionInProgress}
+>
+
+ Are you sure you want to permanently delete {fileMetadata?.fileKey} from the S3 bucket?
+
+
+
+ {
+ moveModalOpen = false
+ }}
+ on:confirmed={() => {
+ moveS3File(fileMetadata?.fileKey, moveDestKey)
+ }}
+ keyListen={false}
+ loading={fileMoveInProgress}
+>
+
+
+ New key:
+
+
+
Are you sure you want to permanently move {fileMetadata?.fileKey}?
+
+
+
+ {
+ uploadModalOpen = false
+ if (evt.detail !== undefined && evt.detail !== null) {
+ selectedFileKey = { s3: evt.detail, storage }
+ await clearAndLoadFiles()
+ loadFileMetadataPlusPreviewAsync(evt.detail)
+ }
+ }}
+/>
diff --git a/frontend/src/lib/components/ScriptBuilder.svelte b/frontend/src/lib/components/ScriptBuilder.svelte
index 2394ffcbe2..c0957d606e 100644
--- a/frontend/src/lib/components/ScriptBuilder.svelte
+++ b/frontend/src/lib/components/ScriptBuilder.svelte
@@ -1377,7 +1377,7 @@
documentationLink="https://www.windmill.dev/docs/core_concepts/dedicated_workers"
>
In this mode, the script is meant to be run on dedicated workers that run
- the script at native speed. Can reach >1500rps per dedicated worker. Only
+ the script at native speed. Can reach >1500rps per dedicated worker. Only
available on enterprise edition and for Python3, Deno and Bun. For other
languages, the efficiency is already on par with deidcated workers since
they do not spawn a full runtime {
+ if (
+ v !== undefined &&
+ (v === null ||
+ v.resource !== ansibleAlternativeExecutionMode?.resource ||
+ v.playbook !== ansibleAlternativeExecutionMode?.playbook ||
+ v.inventories_location !== ansibleAlternativeExecutionMode?.inventories_location ||
+ v.commit !== ansibleAlternativeExecutionMode?.commit)
+ ) {
+ ansibleAlternativeExecutionMode = v
+ }
+ })
+ }
})
})
@@ -176,6 +195,12 @@
let peers: { name: string }[] = $state([])
let showCollabPopup = $state(false)
+ let ansibleAlternativeExecutionMode = $state<
+ | { resource?: string; commit?: string; inventories_location?: string; playbook?: string }
+ | null
+ | undefined
+ >()
+
const url = new URL(window.location.toString())
let initialCollab = /true|1/i.test(url.searchParams.get('collab') ?? '0')
@@ -271,6 +296,40 @@
}
}
+ let gitRepoResourcePickerOpen = $state(false)
+ let commitHashForGitRepo = $derived(ansibleAlternativeExecutionMode?.commit)
+
+ // Check if delegate_to_git_repo exists in the code
+ let hasDelegateToGitRepo = $derived(code && code.includes('delegate_to_git_repo:'))
+
+ function handleDelegateConfigUpdate(event: {
+ detail: { resourcePath: string; playbook?: string; inventoriesLocation?: string }
+ }) {
+ if (!editor) return
+
+ const currentCode = editor.getCode()
+ const newCode = updateDelegateToGitRepoConfig(currentCode, {
+ resource: event.detail.resourcePath,
+ playbook: event.detail.playbook,
+ inventories_location: event.detail.inventoriesLocation
+ })
+ editor.setCode(newCode)
+
+ // Trigger schema inference to update assets
+ inferSchema(newCode)
+ }
+
+ function handleAddInventories(event: { detail: { inventoryPaths: string[] } }) {
+ if (!editor) return
+
+ const currentCode = editor.getCode()
+ const newCode = insertAdditionalInventories(currentCode, event.detail.inventoryPaths)
+ editor.setCode(newCode)
+
+ // Trigger schema inference to update assets
+ inferSchema(newCode)
+ }
+
onMount(() => {
inferSchema(code)
loadPastTests()
@@ -538,126 +597,30 @@
-
-
- {#if assets?.length}
-
- {/if}
- {#if testPanelSize === 0}
-
{
- toggleTestPanel()
- }}
- btnClasses="bg-marine-400 hover:bg-marine-200 !text-primary-inverse hover:!text-primary-inverse hover:dark:!text-primary-inverse dark:bg-marine-50 dark:hover:bg-marine-50/70"
- color="marine"
- />
- {/if}
- {#if !aiChatManager.open && !disableAi}
- {#if customUi?.editorBar?.aiGen != false && SUPPORTED_CHAT_SCRIPT_LANGUAGES.includes(lang ?? '')}
- {
- if (!aiChatManager.open) {
- aiChatManager.changeMode(AIMode.SCRIPT)
- }
- aiChatManager.toggleOpen()
- }}
- >
- {#snippet popoverOverride()}
-
- Enable Windmill AI in the
- workspace settings
-
-
- {/snippet}
-
- {/if}
- {/if}
-
-
- {#key lang}
-
{
- inferSchema(e.detail)
- }}
- on:saveDraft
- on:toggleTestPanel={toggleTestPanel}
- cmdEnterAction={async () => {
- await inferSchema(code)
- runTest()
- }}
- formatAction={async () => {
- await inferSchema(code)
- try {
- localStorage.setItem(path ?? 'last_save', code)
- } catch (e) {
- console.error('Could not save last_save to local storage', e)
- }
- dispatch('format')
- }}
- class="flex flex-1 h-full !overflow-visible"
- scriptLang={lang}
- automaticLayout={true}
- {fixedOverflowWidgets}
- {args}
- {enablePreprocessorSnippet}
- />
- {
- showHistoryDrawer = true
- }
- },
- {
- text: 'Quit diff mode',
- onClick: () => {
- hideDiffMode()
- },
- color: 'red'
- }
- ]
- : []}
- />
- {/key}
-
+ {#if lang === 'ansible' && ansibleAlternativeExecutionMode != null}
+
+
+
+ {@render editorContent()}
+
+
+
+
+
+ {:else}
+
+ {@render editorContent()}
+ {/if}
@@ -812,3 +775,148 @@
+
+{#snippet editorContent()}
+
+
+ {#if assets?.length}
+
+ {/if}
+ {#if lang === 'ansible' && hasDelegateToGitRepo}
+
(gitRepoResourcePickerOpen = true)}
+ startIcon={{ icon: GitBranch }}
+ btnClasses="bg-surface hover:bg-surface-hover border border-tertiary/30"
+ >
+ Delegating to git repo
+
+ {/if}
+ {#if testPanelSize === 0}
+
{
+ toggleTestPanel()
+ }}
+ btnClasses="bg-marine-400 hover:bg-marine-200 !text-primary-inverse hover:!text-primary-inverse hover:dark:!text-primary-inverse dark:bg-marine-50 dark:hover:bg-marine-50/70"
+ color="marine"
+ />
+ {/if}
+ {#if !aiChatManager.open && !disableAi}
+ {#if customUi?.editorBar?.aiGen != false && SUPPORTED_CHAT_SCRIPT_LANGUAGES.includes(lang ?? '')}
+ {
+ if (!aiChatManager.open) {
+ aiChatManager.changeMode(AIMode.SCRIPT)
+ }
+ aiChatManager.toggleOpen()
+ }}
+ >
+ {#snippet popoverOverride()}
+
+ Enable Windmill AI in the
+ workspace settings
+
+
+ {/snippet}
+
+ {/if}
+ {/if}
+
+
+ {#key lang}
+
{
+ inferSchema(e.detail)
+ }}
+ on:saveDraft
+ on:toggleTestPanel={toggleTestPanel}
+ cmdEnterAction={async () => {
+ await inferSchema(code)
+ runTest()
+ }}
+ formatAction={async () => {
+ await inferSchema(code)
+ try {
+ localStorage.setItem(path ?? 'last_save', code)
+ } catch (e) {
+ console.error('Could not save last_save to local storage', e)
+ }
+ dispatch('format')
+ }}
+ class="flex flex-1 h-full !overflow-visible"
+ scriptLang={lang}
+ automaticLayout={true}
+ {fixedOverflowWidgets}
+ {args}
+ {enablePreprocessorSnippet}
+ />
+ {
+ showHistoryDrawer = true
+ }
+ },
+ {
+ text: 'Quit diff mode',
+ onClick: () => {
+ hideDiffMode()
+ },
+ color: 'red'
+ }
+ ]
+ : []}
+ />
+ {/key}
+
+{/snippet}
+
+
diff --git a/frontend/src/lib/components/schema/EditableSchemaSdkWrapper.svelte b/frontend/src/lib/components/schema/EditableSchemaSdkWrapper.svelte
index c7f6dcc614..124779635e 100644
--- a/frontend/src/lib/components/schema/EditableSchemaSdkWrapper.svelte
+++ b/frontend/src/lib/components/schema/EditableSchemaSdkWrapper.svelte
@@ -12,9 +12,9 @@
let schema = $state(oldSchema)
- let lastSchema = $state.snapshot(schema)
+ let lastSchema = $state
(undefined)
$effect(() => {
- if (onSchemaChange) {
+ if (onSchemaChange && schema) {
readFieldsRecursively(schema)
let newSchema = $state.snapshot(schema)
if (!deepEqual(lastSchema, newSchema)) {
diff --git a/frontend/src/lib/components/sidebar/SidebarContent.svelte b/frontend/src/lib/components/sidebar/SidebarContent.svelte
index f0bba47a7b..26b2bf8a74 100644
--- a/frontend/src/lib/components/sidebar/SidebarContent.svelte
+++ b/frontend/src/lib/components/sidebar/SidebarContent.svelte
@@ -73,7 +73,7 @@
}
async function deleteFork() {
- await WorkspaceService.deleteWorkspace({ workspace: $workspaceStore ?? '', onlyDeleteForks: true })
+ await WorkspaceService.deleteWorkspace({ workspace: $workspaceStore ?? '' })
sendUserToast('You deleted the workspace')
clearStores()
goto('/user/workspaces')
diff --git a/frontend/src/lib/hub.ts b/frontend/src/lib/hub.ts
index 1ecf45c14b..5f49ff59b9 100644
--- a/frontend/src/lib/hub.ts
+++ b/frontend/src/lib/hub.ts
@@ -94,6 +94,7 @@ type HubPaths = {
teamsRecoveryHandler: string
teamsSuccessHandler: string
emailErrorHandler: string
+ cloneRepoToS3forGitRepoViewer: string
}
export const hubPaths = JSON.parse(rawHubPaths) as HubPaths
diff --git a/frontend/src/lib/hubPaths.json b/frontend/src/lib/hubPaths.json
index 3207b3fbd7..3a82035564 100644
--- a/frontend/src/lib/hubPaths.json
+++ b/frontend/src/lib/hubPaths.json
@@ -36,5 +36,6 @@
"teamsSuccessHandler": "hub/11596/schedule-success-handler-teams",
"slackReport": "hub/9084/slack",
"discordReport": "hub/9085/discord",
- "smtpReport": "hub/9086/smtp"
+ "smtpReport": "hub/9086/smtp",
+ "cloneRepoToS3forGitRepoViewer": "hub/19825/clone_repo_and_upload_to_instance_storage"
}
diff --git a/frontend/src/lib/infer.ts b/frontend/src/lib/infer.ts
index 7685cd9a66..3510d84bcc 100644
--- a/frontend/src/lib/infer.ts
+++ b/frontend/src/lib/infer.ts
@@ -23,7 +23,7 @@ import initPythonParser, { parse_assets_py, parse_python } from 'windmill-parser
import initGoParser, { parse_go } from 'windmill-parser-wasm-go'
import initPhpParser, { parse_php } from 'windmill-parser-wasm-php'
import initRustParser, { parse_rust } from 'windmill-parser-wasm-rust'
-import initYamlParser, { parse_ansible } from 'windmill-parser-wasm-yaml'
+import initYamlParser, { parse_assets_ansible, parse_ansible, parse_ansible_delegate } from 'windmill-parser-wasm-yaml'
import initCSharpParser, { parse_csharp } from 'windmill-parser-wasm-csharp'
import initNuParser, { parse_nu } from 'windmill-parser-wasm-nu'
import initJavaParser, { parse_java } from 'windmill-parser-wasm-java'
@@ -103,6 +103,10 @@ export async function inferAssets(
await initWasmPython()
return JSON.parse(parse_assets_py(code))
}
+ if (language === 'ansible') {
+ await initWasmYaml()
+ return JSON.parse(parse_assets_ansible(code))
+ }
} catch (e) {
console.error('error parsing assets', e)
return []
@@ -110,6 +114,16 @@ export async function inferAssets(
return []
}
+export async function inferAnsibleExecutionMode(code: string) {
+ try {
+ await initWasmYaml()
+ return JSON.parse(parse_ansible_delegate(code))
+ } catch (e) {
+ console.error('error parsing git repo for ansible', e)
+ return undefined
+ }
+}
+
const SQL_LANGUAGES = [
'postgresql',
'mysql',
From aeb6829011c029ebb8c8f727ae6d26657d3f3f69 Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Thu, 16 Oct 2025 09:19:44 +0000
Subject: [PATCH 10/33] use excluded where relevant
---
...8fe7fd014adbf40cc4bc8c041f5864423367.json} | 4 +-
...861f3483fa70b5d58dd2838a5cb6dabe9cc1.json} | 4 +-
...3df5f38fb8892226f04cf700f60eb45199ef.json} | 4 +-
...b3e03bd44048af4d69fab854f97fa821649b.json} | 4 +-
...ab38dfe15284a6a7f54366ba25d7eb75a74a.json} | 4 +-
...b1af55045c1b8c2a138ca995882dde955971.json} | 4 +-
...72e7b051adb368b3685181047f1f4522a473.json} | 4 +-
...cfba31cfdd64a0f68001e123694ed5cde5ed.json} | 4 +-
...48910d900b9575d986143823781e1d005d57.json} | 4 +-
...a7d07ee06d417a5bd9f02d64fcb467cf362a.json} | 4 +-
...f6cb506c75e84c65438c9026831eb10d340b.json} | 4 +-
...fa6325fcae4dc3bf559f2ecc683fb40537e8.json} | 4 +-
...b7cbc891e55d29d30d1ca7b13f25ed1fdc87.json} | 4 +-
...a2eb517e82943bb8c078c7bb2e60def3cbf1.json} | 4 +-
...0d622c9d2d844a2ab3938e6ec23c6150fb40.json} | 4 +-
...5bc14039aff6f023d79139b20519a9cdbe7d.json} | 4 +-
...270e5e8a52540da1726aa875fcbe2517f16a.json} | 4 +-
...dacb12266f606b5ce7e8ed6110f3f381f945.json} | 4 +-
...0af413027dbd39aa2c4182e3d219640d397e.json} | 4 +-
...4af00edc86a9bc498f319c1e5bee55d77a8a.json} | 4 +-
...db2b1061c22698393c2e42d94da11e697b8b.json} | 4 +-
...e059481091ac173414418b8678f6beadf2ac.json} | 4 +-
...66512801c20ad685e8ed544fe4b86601aaa8.json} | 4 +-
...ef762fecf782424c5b61b89f32918b8d6971.json} | 4 +-
...f3db1fb30c9564ed039ad4c952333ec29b39.json} | 4 +-
...13481db39eea0b52cc513b3ee6571cf44ed4.json} | 4 +-
...63ce8ce9fbe89abe83989f52ff3657690fef.json} | 4 +-
...474d2f8513202dfb64767b30e7b50833b857.json} | 4 +-
...243b590b2dd0cfe41aa5100a7e829afad9d3.json} | 4 +-
...16448202c1181ca0b36936a9c8840c14a95d.json} | 4 +-
...cd6b45dc161423abab22cddb2b13f6fb9833.json} | 4 +-
...b0692a02dec4c8415fd7d364b0cc088905c2.json} | 4 +-
...676d0c8ae31982865cb2c8103ed735c42c69.json} | 4 +-
backend/ee-repo-ref.txt | 2 +-
backend/src/monitor.rs | 2 +-
backend/windmill-api/src/apps.rs | 2 +-
backend/windmill-api/src/configs.rs | 2 +-
backend/windmill-api/src/drafts.rs | 2 +-
backend/windmill-api/src/flows.rs | 2 +-
backend/windmill-api/src/resources.rs | 2 +-
backend/windmill-api/src/scripts.rs | 3 +-
backend/windmill-api/src/settings.rs | 2 +-
backend/windmill-api/src/users.rs | 44 +++++++++++++------
backend/windmill-api/src/workspaces.rs | 4 +-
backend/windmill-common/src/result_stream.rs | 2 +-
backend/windmill-common/src/worker.rs | 3 +-
backend/windmill-queue/src/jobs.rs | 8 ++--
backend/windmill-worker/src/common.rs | 2 +-
backend/windmill-worker/src/go_executor.rs | 7 ++-
backend/windmill-worker/src/java_executor.rs | 2 +-
backend/windmill-worker/src/job_logger.rs | 2 +-
.../windmill-worker/src/python_executor.rs | 2 +-
backend/windmill-worker/src/ruby_executor.rs | 2 +-
53 files changed, 125 insertions(+), 104 deletions(-)
rename backend/.sqlx/{query-db558b5ecdc4c3b1af0def511f1bcd91a548f00376f644c8ba38f73812b462d0.json => query-07335b75233811352fb898cf3d6c8fe7fd014adbf40cc4bc8c041f5864423367.json} (62%)
rename backend/.sqlx/{query-a59b70164dc87224d09a04d5469ca217eb19a15a250c3b83ca63f606f89b9681.json => query-08c1121171b98889f188ea6b33b1861f3483fa70b5d58dd2838a5cb6dabe9cc1.json} (59%)
rename backend/.sqlx/{query-8bd028c8b5f8a4d566f89eebc2e63fd04beaf2b0b49e07c7df42ecddd70737f3.json => query-19e4625de06b8bab10039280a6213df5f38fb8892226f04cf700f60eb45199ef.json} (75%)
rename backend/.sqlx/{query-9f1f388924176dbe3dea882e0c62728a82ba256029096812dd705ccb1a552cfe.json => query-1e426c8a06d7bbed7af67a105f74b3e03bd44048af4d69fab854f97fa821649b.json} (78%)
rename backend/.sqlx/{query-a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a.json => query-24f38f0642b49626c8c8417e1846ab38dfe15284a6a7f54366ba25d7eb75a74a.json} (62%)
rename backend/.sqlx/{query-af00c212f509076e37538be52f582ba09e47db50ba93af322649ccddbb05cc49.json => query-25bf02e605e9e8e708a5dcfbb898b1af55045c1b8c2a138ca995882dde955971.json} (64%)
rename backend/.sqlx/{query-55a2f170823f1d1abce76287d8817a6cf34de92b9b4079c00b75423a9ff835b9.json => query-2b34ae324f90924dba6c4562024f72e7b051adb368b3685181047f1f4522a473.json} (76%)
rename backend/.sqlx/{query-8d119104337bf99e9aa9dcbac0a54154267a7db96cc0fb3ebaac95635e24da29.json => query-2e131a019051bdac7c9c65f7c504cfba31cfdd64a0f68001e123694ed5cde5ed.json} (75%)
rename backend/.sqlx/{query-652835b2b7f801532a591988ac76d385188991c6654d529f6d65f6f03794844a.json => query-384753e7ceb602790646b0f269df48910d900b9575d986143823781e1d005d57.json} (74%)
rename backend/.sqlx/{query-42df4b40b3bbf14010f07e29892776992bfeb383d590e379244ad23703e536a4.json => query-3904d59575e05df6e414be03e84ca7d07ee06d417a5bd9f02d64fcb467cf362a.json} (73%)
rename backend/.sqlx/{query-c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e.json => query-39426bd3018b390ea2073419884cf6cb506c75e84c65438c9026831eb10d340b.json} (64%)
rename backend/.sqlx/{query-9a9e4a8779b0bf8a275d029221dfa1465e5d44cd8a7be5879219ffc8cd7ae6b1.json => query-4d3b8726656ebf6eb78d0c4c23fdfa6325fcae4dc3bf559f2ecc683fb40537e8.json} (69%)
rename backend/.sqlx/{query-c7dd35561e9b1cfd86238d410139f50e4d87c762e4867d8b32c11bd8c74846eb.json => query-5585bc46fe2b9d5aebddad300f43b7cbc891e55d29d30d1ca7b13f25ed1fdc87.json} (74%)
rename backend/.sqlx/{query-08dd2ea6b17a52bce352d6443d7d009cfc9da0d3b2bd1f40d422b550779e5324.json => query-5701ee0b862dbdb44990702af270a2eb517e82943bb8c078c7bb2e60def3cbf1.json} (72%)
rename backend/.sqlx/{query-14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d.json => query-6d7a4185063dbcca0dbea1b002330d622c9d2d844a2ab3938e6ec23c6150fb40.json} (80%)
rename backend/.sqlx/{query-d970a0b07a2b5840d1feb1baacb834dbaf91c633d3e7e1e29c8eb7eedc53e888.json => query-7dc75cb67922e31ffe0f88b03a8a5bc14039aff6f023d79139b20519a9cdbe7d.json} (74%)
rename backend/.sqlx/{query-43b376a2eff086a32cd76e54361ce3631feee1565935d2a6ddbecc17950758d1.json => query-7dcc77eb6da5863f7a25ab6ad83d270e5e8a52540da1726aa875fcbe2517f16a.json} (67%)
rename backend/.sqlx/{query-b8c66d905a6c7ffa6441c84b14ea897040069dac7367895813cc2d64a9867193.json => query-7dcf840fc5b329f4a591a51e24c9dacb12266f606b5ce7e8ed6110f3f381f945.json} (75%)
rename backend/.sqlx/{query-4fb3881cdbb4b9e93e28f460a9b3715bdc6a52b76c89f3a3913023b13c4e085c.json => query-8fda0400ec5ba04a2a1469672bbb0af413027dbd39aa2c4182e3d219640d397e.json} (69%)
rename backend/.sqlx/{query-e40f7e0b61567f948bfea0b6f50518564634885ccc2c0d30ccca79fc13bdcf07.json => query-99e6bffe177e69448b09e82b30d24af00edc86a9bc498f319c1e5bee55d77a8a.json} (60%)
rename backend/.sqlx/{query-f9e1334ecb17b313924587d96fc8b0310b14d75e85a558c8cb70f2761923d175.json => query-9aebf706529889dc044e0ef41da8db2b1061c22698393c2e42d94da11e697b8b.json} (78%)
rename backend/.sqlx/{query-20fcdcd2674a52ee9bd8d1de518d6bce075f20bcd5d2328f183d8a59331f6bb1.json => query-a5bf005e0f7c9a86a136e049445de059481091ac173414418b8678f6beadf2ac.json} (75%)
rename backend/.sqlx/{query-366609f7e7fbd73ea807128b931eff2f1ab763fa630c8531f590fed2110c03d9.json => query-b474ae4401b3d4c95add2d3353eb66512801c20ad685e8ed544fe4b86601aaa8.json} (86%)
rename backend/.sqlx/{query-8d67ed8e1271a27b072a8e8ae9973e372949f5ec7d71b80d1ba3eaddf851adce.json => query-bf2163c542fb8c4e173167a8f333ef762fecf782424c5b61b89f32918b8d6971.json} (87%)
rename backend/.sqlx/{query-afc7c23c057748f6d4a61dbef17e433b8875c6588b91e38e8141d12189118412.json => query-cc8e10a4f39e118b145cc8f6fbd5f3db1fb30c9564ed039ad4c952333ec29b39.json} (74%)
rename backend/.sqlx/{query-7b48820a08fbb3bee0fb2dc802a0b4f28ed5507797165e8c577ce6a95d11694c.json => query-ceb10ca124d9425b24d81d8a279a13481db39eea0b52cc513b3ee6571cf44ed4.json} (77%)
rename backend/.sqlx/{query-9bfc2a821b25641af48b0e3954163078922340294f1c6515400fb2c896666fde.json => query-d142ff8b56f4b69c20815230b5b763ce8ce9fbe89abe83989f52ff3657690fef.json} (73%)
rename backend/.sqlx/{query-fb581eb6f883ebb5909a00c65a6e1217f088290fce0e052171230bcd88f945b9.json => query-da7e23a32f284c9760614735d459474d2f8513202dfb64767b30e7b50833b857.json} (57%)
rename backend/.sqlx/{query-c4e0f3eab227a798d9cd7478db50a7c1a588e69c2c8d9a1def9276ba326453d2.json => query-db201730803047bfabccf5f10456243b590b2dd0cfe41aa5100a7e829afad9d3.json} (63%)
rename backend/.sqlx/{query-a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c.json => query-e16f464c7e302e80f1017ff6550716448202c1181ca0b36936a9c8840c14a95d.json} (76%)
rename backend/.sqlx/{query-f37140fcdc721a8b199471b30c2baf124affa2eaf56c801c8dac3264c584f981.json => query-e572fa64eec9188368d7c271ac7ecd6b45dc161423abab22cddb2b13f6fb9833.json} (74%)
rename backend/.sqlx/{query-6afc5c7cbb3abe11ade0cedf1f7328005ce4de3165cdd998e5a0d27e044c7153.json => query-e5f8830450e90f678494ae2b0f86b0692a02dec4c8415fd7d364b0cc088905c2.json} (70%)
rename backend/.sqlx/{query-2367e7c0f7fbafe0971a187c0909617da55251e97180babf6ac9e8068f26d73d.json => query-e8e33f599eae064011232f9f715e676d0c8ae31982865cb2c8103ed735c42c69.json} (74%)
diff --git a/backend/.sqlx/query-db558b5ecdc4c3b1af0def511f1bcd91a548f00376f644c8ba38f73812b462d0.json b/backend/.sqlx/query-07335b75233811352fb898cf3d6c8fe7fd014adbf40cc4bc8c041f5864423367.json
similarity index 62%
rename from backend/.sqlx/query-db558b5ecdc4c3b1af0def511f1bcd91a548f00376f644c8ba38f73812b462d0.json
rename to backend/.sqlx/query-07335b75233811352fb898cf3d6c8fe7fd014adbf40cc4bc8c041f5864423367.json
index 1e36101464..9c85043e7c 100644
--- a/backend/.sqlx/query-db558b5ecdc4c3b1af0def511f1bcd91a548f00376f644c8ba38f73812b462d0.json
+++ b/backend/.sqlx/query-07335b75233811352fb898cf3d6c8fe7fd014adbf40cc4bc8c041f5864423367.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET deployment_msg = $4",
+ "query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL\n DO UPDATE SET deployment_msg = EXCLUDED.deployment_msg",
"describe": {
"columns": [],
"parameters": {
@@ -13,5 +13,5 @@
},
"nullable": []
},
- "hash": "db558b5ecdc4c3b1af0def511f1bcd91a548f00376f644c8ba38f73812b462d0"
+ "hash": "07335b75233811352fb898cf3d6c8fe7fd014adbf40cc4bc8c041f5864423367"
}
diff --git a/backend/.sqlx/query-a59b70164dc87224d09a04d5469ca217eb19a15a250c3b83ca63f606f89b9681.json b/backend/.sqlx/query-08c1121171b98889f188ea6b33b1861f3483fa70b5d58dd2838a5cb6dabe9cc1.json
similarity index 59%
rename from backend/.sqlx/query-a59b70164dc87224d09a04d5469ca217eb19a15a250c3b83ca63f606f89b9681.json
rename to backend/.sqlx/query-08c1121171b98889f188ea6b33b1861f3483fa70b5d58dd2838a5cb6dabe9cc1.json
index da8842f9a7..16a6101127 100644
--- a/backend/.sqlx/query-a59b70164dc87224d09a04d5469ca217eb19a15a250c3b83ca63f606f89b9681.json
+++ b/backend/.sqlx/query-08c1121171b98889f188ea6b33b1861f3483fa70b5d58dd2838a5cb6dabe9cc1.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2, updated_at = now()",
+ "query": "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = EXCLUDED.value, updated_at = now()",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "a59b70164dc87224d09a04d5469ca217eb19a15a250c3b83ca63f606f89b9681"
+ "hash": "08c1121171b98889f188ea6b33b1861f3483fa70b5d58dd2838a5cb6dabe9cc1"
}
diff --git a/backend/.sqlx/query-8bd028c8b5f8a4d566f89eebc2e63fd04beaf2b0b49e07c7df42ecddd70737f3.json b/backend/.sqlx/query-19e4625de06b8bab10039280a6213df5f38fb8892226f04cf700f60eb45199ef.json
similarity index 75%
rename from backend/.sqlx/query-8bd028c8b5f8a4d566f89eebc2e63fd04beaf2b0b49e07c7df42ecddd70737f3.json
rename to backend/.sqlx/query-19e4625de06b8bab10039280a6213df5f38fb8892226f04cf700f60eb45199ef.json
index f1f7a0b56c..bec2b14783 100644
--- a/backend/.sqlx/query-8bd028c8b5f8a4d566f89eebc2e63fd04beaf2b0b49e07c7df42ecddd70737f3.json
+++ b/backend/.sqlx/query-19e4625de06b8bab10039280a6213df5f38fb8892226f04cf700f60eb45199ef.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO resource\n (workspace_id, path, value, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, edited_at = now()",
+ "query": "INSERT INTO resource\n (workspace_id, path, value, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = EXCLUDED.value, edited_at = now()",
"describe": {
"columns": [],
"parameters": {
@@ -14,5 +14,5 @@
},
"nullable": []
},
- "hash": "8bd028c8b5f8a4d566f89eebc2e63fd04beaf2b0b49e07c7df42ecddd70737f3"
+ "hash": "19e4625de06b8bab10039280a6213df5f38fb8892226f04cf700f60eb45199ef"
}
diff --git a/backend/.sqlx/query-9f1f388924176dbe3dea882e0c62728a82ba256029096812dd705ccb1a552cfe.json b/backend/.sqlx/query-1e426c8a06d7bbed7af67a105f74b3e03bd44048af4d69fab854f97fa821649b.json
similarity index 78%
rename from backend/.sqlx/query-9f1f388924176dbe3dea882e0c62728a82ba256029096812dd705ccb1a552cfe.json
rename to backend/.sqlx/query-1e426c8a06d7bbed7af67a105f74b3e03bd44048af4d69fab854f97fa821649b.json
index 51ddd8af63..0f4f6a0a90 100644
--- a/backend/.sqlx/query-9f1f388924176dbe3dea882e0c62728a82ba256029096812dd705ccb1a552cfe.json
+++ b/backend/.sqlx/query-1e426c8a06d7bbed7af67a105f74b3e03bd44048af4d69fab854f97fa821649b.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, account, is_oauth)\n VALUES ($1, $2, $3, $4, $5, $6)\n ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3",
+ "query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, account, is_oauth)\n VALUES ($1, $2, $3, $4, $5, $6)\n ON CONFLICT (workspace_id, path) DO UPDATE SET value = EXCLUDED.value",
"describe": {
"columns": [],
"parameters": {
@@ -15,5 +15,5 @@
},
"nullable": []
},
- "hash": "9f1f388924176dbe3dea882e0c62728a82ba256029096812dd705ccb1a552cfe"
+ "hash": "1e426c8a06d7bbed7af67a105f74b3e03bd44048af4d69fab854f97fa821649b"
}
diff --git a/backend/.sqlx/query-a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a.json b/backend/.sqlx/query-24f38f0642b49626c8c8417e1846ab38dfe15284a6a7f54366ba25d7eb75a74a.json
similarity index 62%
rename from backend/.sqlx/query-a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a.json
rename to backend/.sqlx/query-24f38f0642b49626c8c8417e1846ab38dfe15284a6a7f54366ba25d7eb75a74a.json
index 9f95781136..265ac4fba1 100644
--- a/backend/.sqlx/query-a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a.json
+++ b/backend/.sqlx/query-24f38f0642b49626c8c8417e1846ab38dfe15284a6a7f54366ba25d7eb75a74a.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2",
+ "query": "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = EXCLUDED.value",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "a00e61e770e20157bbd9e4cdedf7fb5f9de7c8c9e50282e3ecf2e3ce917ec37a"
+ "hash": "24f38f0642b49626c8c8417e1846ab38dfe15284a6a7f54366ba25d7eb75a74a"
}
diff --git a/backend/.sqlx/query-af00c212f509076e37538be52f582ba09e47db50ba93af322649ccddbb05cc49.json b/backend/.sqlx/query-25bf02e605e9e8e708a5dcfbb898b1af55045c1b8c2a138ca995882dde955971.json
similarity index 64%
rename from backend/.sqlx/query-af00c212f509076e37538be52f582ba09e47db50ba93af322649ccddbb05cc49.json
rename to backend/.sqlx/query-25bf02e605e9e8e708a5dcfbb898b1af55045c1b8c2a138ca995882dde955971.json
index 2c3c76ca31..660044efb2 100644
--- a/backend/.sqlx/query-af00c212f509076e37538be52f582ba09e47db50ba93af322649ccddbb05cc49.json
+++ b/backend/.sqlx/query-25bf02e605e9e8e708a5dcfbb898b1af55045c1b8c2a138ca995882dde955971.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO config (name, config) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET config = $2",
+ "query": "INSERT INTO config (name, config) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET config = EXCLUDED.config",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "af00c212f509076e37538be52f582ba09e47db50ba93af322649ccddbb05cc49"
+ "hash": "25bf02e605e9e8e708a5dcfbb898b1af55045c1b8c2a138ca995882dde955971"
}
diff --git a/backend/.sqlx/query-55a2f170823f1d1abce76287d8817a6cf34de92b9b4079c00b75423a9ff835b9.json b/backend/.sqlx/query-2b34ae324f90924dba6c4562024f72e7b051adb368b3685181047f1f4522a473.json
similarity index 76%
rename from backend/.sqlx/query-55a2f170823f1d1abce76287d8817a6cf34de92b9b4079c00b75423a9ff835b9.json
rename to backend/.sqlx/query-2b34ae324f90924dba6c4562024f72e7b051adb368b3685181047f1f4522a473.json
index 7b7dd7fe29..302c0b461d 100644
--- a/backend/.sqlx/query-55a2f170823f1d1abce76287d8817a6cf34de92b9b4079c00b75423a9ff835b9.json
+++ b/backend/.sqlx/query-2b34ae324f90924dba6c4562024f72e7b051adb368b3685181047f1f4522a473.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, description, account, is_oauth)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3",
+ "query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, description, account, is_oauth)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (workspace_id, path) DO UPDATE SET value = EXCLUDED.value",
"describe": {
"columns": [],
"parameters": {
@@ -16,5 +16,5 @@
},
"nullable": []
},
- "hash": "55a2f170823f1d1abce76287d8817a6cf34de92b9b4079c00b75423a9ff835b9"
+ "hash": "2b34ae324f90924dba6c4562024f72e7b051adb368b3685181047f1f4522a473"
}
diff --git a/backend/.sqlx/query-8d119104337bf99e9aa9dcbac0a54154267a7db96cc0fb3ebaac95635e24da29.json b/backend/.sqlx/query-2e131a019051bdac7c9c65f7c504cfba31cfdd64a0f68001e123694ed5cde5ed.json
similarity index 75%
rename from backend/.sqlx/query-8d119104337bf99e9aa9dcbac0a54154267a7db96cc0fb3ebaac95635e24da29.json
rename to backend/.sqlx/query-2e131a019051bdac7c9c65f7c504cfba31cfdd64a0f68001e123694ed5cde5ed.json
index 1c2f9a9f33..347ad02c31 100644
--- a/backend/.sqlx/query-8d119104337bf99e9aa9dcbac0a54154267a7db96cc0fb3ebaac95635e24da29.json
+++ b/backend/.sqlx/query-2e131a019051bdac7c9c65f7c504cfba31cfdd64a0f68001e123694ed5cde5ed.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5) \n ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET callback_job_ids = $4, deployment_msg = $5",
+ "query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5) \n ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET callback_job_ids = EXCLUDED.callback_job_ids, deployment_msg = EXCLUDED.deployment_msg",
"describe": {
"columns": [],
"parameters": {
@@ -14,5 +14,5 @@
},
"nullable": []
},
- "hash": "8d119104337bf99e9aa9dcbac0a54154267a7db96cc0fb3ebaac95635e24da29"
+ "hash": "2e131a019051bdac7c9c65f7c504cfba31cfdd64a0f68001e123694ed5cde5ed"
}
diff --git a/backend/.sqlx/query-652835b2b7f801532a591988ac76d385188991c6654d529f6d65f6f03794844a.json b/backend/.sqlx/query-384753e7ceb602790646b0f269df48910d900b9575d986143823781e1d005d57.json
similarity index 74%
rename from backend/.sqlx/query-652835b2b7f801532a591988ac76d385188991c6654d529f6d65f6f03794844a.json
rename to backend/.sqlx/query-384753e7ceb602790646b0f269df48910d900b9575d986143823781e1d005d57.json
index 91e268889d..d90cb38e55 100644
--- a/backend/.sqlx/query-652835b2b7f801532a591988ac76d385188991c6654d529f6d65f6f03794844a.json
+++ b/backend/.sqlx/query-384753e7ceb602790646b0f269df48910d900b9575d986143823781e1d005d57.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO deployment_metadata (workspace_id, path, app_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
+ "query": "INSERT INTO deployment_metadata (workspace_id, path, app_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET deployment_msg = EXCLUDED.deployment_msg",
"describe": {
"columns": [],
"parameters": {
@@ -13,5 +13,5 @@
},
"nullable": []
},
- "hash": "652835b2b7f801532a591988ac76d385188991c6654d529f6d65f6f03794844a"
+ "hash": "384753e7ceb602790646b0f269df48910d900b9575d986143823781e1d005d57"
}
diff --git a/backend/.sqlx/query-42df4b40b3bbf14010f07e29892776992bfeb383d590e379244ad23703e536a4.json b/backend/.sqlx/query-3904d59575e05df6e414be03e84ca7d07ee06d417a5bd9f02d64fcb467cf362a.json
similarity index 73%
rename from backend/.sqlx/query-42df4b40b3bbf14010f07e29892776992bfeb383d590e379244ad23703e536a4.json
rename to backend/.sqlx/query-3904d59575e05df6e414be03e84ca7d07ee06d417a5bd9f02d64fcb467cf362a.json
index 695b4d2969..f6284b98a7 100644
--- a/backend/.sqlx/query-42df4b40b3bbf14010f07e29892776992bfeb383d590e379244ad23703e536a4.json
+++ b/backend/.sqlx/query-3904d59575e05df6e414be03e84ca7d07ee06d417a5bd9f02d64fcb467cf362a.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3, edited_at = now()",
+ "query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path) DO UPDATE SET value = EXCLUDED.value, edited_at = now()",
"describe": {
"columns": [],
"parameters": {
@@ -15,5 +15,5 @@
},
"nullable": []
},
- "hash": "42df4b40b3bbf14010f07e29892776992bfeb383d590e379244ad23703e536a4"
+ "hash": "3904d59575e05df6e414be03e84ca7d07ee06d417a5bd9f02d64fcb467cf362a"
}
diff --git a/backend/.sqlx/query-c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e.json b/backend/.sqlx/query-39426bd3018b390ea2073419884cf6cb506c75e84c65438c9026831eb10d340b.json
similarity index 64%
rename from backend/.sqlx/query-c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e.json
rename to backend/.sqlx/query-39426bd3018b390ea2073419884cf6cb506c75e84c65438c9026831eb10d340b.json
index d3634c6e96..69064f7d45 100644
--- a/backend/.sqlx/query-c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e.json
+++ b/backend/.sqlx/query-39426bd3018b390ea2073419884cf6cb506c75e84c65438c9026831eb10d340b.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = $3, description = $4, resource_type = $5, edited_at = now()",
+ "query": "INSERT INTO resource\n (workspace_id, path, value, description, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)\n DO UPDATE SET value = EXCLUDED.value, description = EXCLUDED.description, resource_type = EXCLUDED.resource_type, edited_at = now()",
"describe": {
"columns": [],
"parameters": {
@@ -15,5 +15,5 @@
},
"nullable": []
},
- "hash": "c69719d0a63b0ca434c3317529e00e4d0df0104b6c1dbdf6d0f68f5047a2ad5e"
+ "hash": "39426bd3018b390ea2073419884cf6cb506c75e84c65438c9026831eb10d340b"
}
diff --git a/backend/.sqlx/query-9a9e4a8779b0bf8a275d029221dfa1465e5d44cd8a7be5879219ffc8cd7ae6b1.json b/backend/.sqlx/query-4d3b8726656ebf6eb78d0c4c23fdfa6325fcae4dc3bf559f2ecc683fb40537e8.json
similarity index 69%
rename from backend/.sqlx/query-9a9e4a8779b0bf8a275d029221dfa1465e5d44cd8a7be5879219ffc8cd7ae6b1.json
rename to backend/.sqlx/query-4d3b8726656ebf6eb78d0c4c23fdfa6325fcae4dc3bf559f2ecc683fb40537e8.json
index 4a85852957..83c02ba868 100644
--- a/backend/.sqlx/query-9a9e4a8779b0bf8a275d029221dfa1465e5d44cd8a7be5879219ffc8cd7ae6b1.json
+++ b/backend/.sqlx/query-4d3b8726656ebf6eb78d0c4c23fdfa6325fcae4dc3bf559f2ecc683fb40537e8.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('5 mins')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2",
+ "query": "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('5 mins')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = EXCLUDED.lockfile",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "9a9e4a8779b0bf8a275d029221dfa1465e5d44cd8a7be5879219ffc8cd7ae6b1"
+ "hash": "4d3b8726656ebf6eb78d0c4c23fdfa6325fcae4dc3bf559f2ecc683fb40537e8"
}
diff --git a/backend/.sqlx/query-c7dd35561e9b1cfd86238d410139f50e4d87c762e4867d8b32c11bd8c74846eb.json b/backend/.sqlx/query-5585bc46fe2b9d5aebddad300f43b7cbc891e55d29d30d1ca7b13f25ed1fdc87.json
similarity index 74%
rename from backend/.sqlx/query-c7dd35561e9b1cfd86238d410139f50e4d87c762e4867d8b32c11bd8c74846eb.json
rename to backend/.sqlx/query-5585bc46fe2b9d5aebddad300f43b7cbc891e55d29d30d1ca7b13f25ed1fdc87.json
index 73a1911081..0df8109669 100644
--- a/backend/.sqlx/query-c7dd35561e9b1cfd86238d410139f50e4d87c762e4867d8b32c11bd8c74846eb.json
+++ b/backend/.sqlx/query-5585bc46fe2b9d5aebddad300f43b7cbc891e55d29d30d1ca7b13f25ed1fdc87.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, $1::text)",
+ "query": "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, EXCLUDED.logs)",
"describe": {
"columns": [],
"parameters": {
@@ -12,5 +12,5 @@
},
"nullable": []
},
- "hash": "c7dd35561e9b1cfd86238d410139f50e4d87c762e4867d8b32c11bd8c74846eb"
+ "hash": "5585bc46fe2b9d5aebddad300f43b7cbc891e55d29d30d1ca7b13f25ed1fdc87"
}
diff --git a/backend/.sqlx/query-08dd2ea6b17a52bce352d6443d7d009cfc9da0d3b2bd1f40d422b550779e5324.json b/backend/.sqlx/query-5701ee0b862dbdb44990702af270a2eb517e82943bb8c078c7bb2e60def3cbf1.json
similarity index 72%
rename from backend/.sqlx/query-08dd2ea6b17a52bce352d6443d7d009cfc9da0d3b2bd1f40d422b550779e5324.json
rename to backend/.sqlx/query-5701ee0b862dbdb44990702af270a2eb517e82943bb8c078c7bb2e60def3cbf1.json
index b56784fd64..e1384a4e70 100644
--- a/backend/.sqlx/query-08dd2ea6b17a52bce352d6443d7d009cfc9da0d3b2bd1f40d422b550779e5324.json
+++ b/backend/.sqlx/query-5701ee0b862dbdb44990702af270a2eb517e82943bb8c078c7bb2e60def3cbf1.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, account, is_oauth, expires_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3, expires_at = $7",
+ "query": "INSERT INTO variable\n (workspace_id, path, value, is_secret, account, is_oauth, expires_at)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ON CONFLICT (workspace_id, path) DO UPDATE SET value = EXCLUDED.value, expires_at = EXCLUDED.expires_at",
"describe": {
"columns": [],
"parameters": {
@@ -16,5 +16,5 @@
},
"nullable": []
},
- "hash": "08dd2ea6b17a52bce352d6443d7d009cfc9da0d3b2bd1f40d422b550779e5324"
+ "hash": "5701ee0b862dbdb44990702af270a2eb517e82943bb8c078c7bb2e60def3cbf1"
}
diff --git a/backend/.sqlx/query-14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d.json b/backend/.sqlx/query-6d7a4185063dbcca0dbea1b002330d622c9d2d844a2ab3938e6ec23c6150fb40.json
similarity index 80%
rename from backend/.sqlx/query-14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d.json
rename to backend/.sqlx/query-6d7a4185063dbcca0dbea1b002330d622c9d2d844a2ab3938e6ec23c6150fb40.json
index 29aaf47e88..0161985a7f 100644
--- a/backend/.sqlx/query-14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d.json
+++ b/backend/.sqlx/query-6d7a4185063dbcca0dbea1b002330d622c9d2d844a2ab3938e6ec23c6150fb40.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO concurrency_locks (id, last_locked_at, owner)\n VALUES ($1, now(), $2)\n ON CONFLICT (id)\n DO UPDATE SET\n last_locked_at = now(),\n owner = $2",
+ "query": "INSERT INTO concurrency_locks (id, last_locked_at, owner)\n VALUES ($1, now(), $2)\n ON CONFLICT (id)\n DO UPDATE SET\n last_locked_at = now(),\n owner = EXCLUDED.owner",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "14abf759dae7ba5c38017ba6001927c6df0653a02b87bcea939066e39ebcf24d"
+ "hash": "6d7a4185063dbcca0dbea1b002330d622c9d2d844a2ab3938e6ec23c6150fb40"
}
diff --git a/backend/.sqlx/query-d970a0b07a2b5840d1feb1baacb834dbaf91c633d3e7e1e29c8eb7eedc53e888.json b/backend/.sqlx/query-7dc75cb67922e31ffe0f88b03a8a5bc14039aff6f023d79139b20519a9cdbe7d.json
similarity index 74%
rename from backend/.sqlx/query-d970a0b07a2b5840d1feb1baacb834dbaf91c633d3e7e1e29c8eb7eedc53e888.json
rename to backend/.sqlx/query-7dc75cb67922e31ffe0f88b03a8a5bc14039aff6f023d79139b20519a9cdbe7d.json
index c6324a22d5..33e330c061 100644
--- a/backend/.sqlx/query-d970a0b07a2b5840d1feb1baacb834dbaf91c633d3e7e1e29c8eb7eedc53e888.json
+++ b/backend/.sqlx/query-7dc75cb67922e31ffe0f88b03a8a5bc14039aff6f023d79139b20519a9cdbe7d.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO workspace_invite\n (workspace_id, email, is_admin, operator)\n VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, email)\n DO UPDATE SET is_admin = $3, operator = $4",
+ "query": "INSERT INTO workspace_invite\n (workspace_id, email, is_admin, operator)\n VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, email)\n DO UPDATE SET is_admin = EXCLUDED.is_admin, operator = EXCLUDED.operator",
"describe": {
"columns": [],
"parameters": {
@@ -13,5 +13,5 @@
},
"nullable": []
},
- "hash": "d970a0b07a2b5840d1feb1baacb834dbaf91c633d3e7e1e29c8eb7eedc53e888"
+ "hash": "7dc75cb67922e31ffe0f88b03a8a5bc14039aff6f023d79139b20519a9cdbe7d"
}
diff --git a/backend/.sqlx/query-43b376a2eff086a32cd76e54361ce3631feee1565935d2a6ddbecc17950758d1.json b/backend/.sqlx/query-7dcc77eb6da5863f7a25ab6ad83d270e5e8a52540da1726aa875fcbe2517f16a.json
similarity index 67%
rename from backend/.sqlx/query-43b376a2eff086a32cd76e54361ce3631feee1565935d2a6ddbecc17950758d1.json
rename to backend/.sqlx/query-7dcc77eb6da5863f7a25ab6ad83d270e5e8a52540da1726aa875fcbe2517f16a.json
index 5b9eeaa790..d7b8988edf 100644
--- a/backend/.sqlx/query-43b376a2eff086a32cd76e54361ce3631feee1565935d2a6ddbecc17950758d1.json
+++ b/backend/.sqlx/query-7dcc77eb6da5863f7a25ab6ad83d270e5e8a52540da1726aa875fcbe2517f16a.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO workspace_settings\n (workspace_id, slack_team_id, slack_name, slack_email)\n VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id) DO UPDATE SET slack_team_id = $2, slack_name = $3, slack_email = $4",
+ "query": "INSERT INTO workspace_settings\n (workspace_id, slack_team_id, slack_name, slack_email)\n VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id) DO UPDATE SET slack_team_id = EXCLUDED.slack_team_id, slack_name = EXCLUDED.slack_name, slack_email = EXCLUDED.slack_email",
"describe": {
"columns": [],
"parameters": {
@@ -13,5 +13,5 @@
},
"nullable": []
},
- "hash": "43b376a2eff086a32cd76e54361ce3631feee1565935d2a6ddbecc17950758d1"
+ "hash": "7dcc77eb6da5863f7a25ab6ad83d270e5e8a52540da1726aa875fcbe2517f16a"
}
diff --git a/backend/.sqlx/query-b8c66d905a6c7ffa6441c84b14ea897040069dac7367895813cc2d64a9867193.json b/backend/.sqlx/query-7dcf840fc5b329f4a591a51e24c9dacb12266f606b5ce7e8ed6110f3f381f945.json
similarity index 75%
rename from backend/.sqlx/query-b8c66d905a6c7ffa6441c84b14ea897040069dac7367895813cc2d64a9867193.json
rename to backend/.sqlx/query-7dcf840fc5b329f4a591a51e24c9dacb12266f606b5ce7e8ed6110f3f381f945.json
index 265a3c96a4..5084c21af8 100644
--- a/backend/.sqlx/query-b8c66d905a6c7ffa6441c84b14ea897040069dac7367895813cc2d64a9867193.json
+++ b/backend/.sqlx/query-7dcf840fc5b329f4a591a51e24c9dacb12266f606b5ce7e8ed6110f3f381f945.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO workspace_env (workspace_id, name, value) VALUES ($1, $2, $3) ON CONFLICT (workspace_id, name) DO UPDATE SET value = $3",
+ "query": "INSERT INTO workspace_env (workspace_id, name, value) VALUES ($1, $2, $3) ON CONFLICT (workspace_id, name) DO UPDATE SET value = EXCLUDED.value",
"describe": {
"columns": [],
"parameters": {
@@ -12,5 +12,5 @@
},
"nullable": []
},
- "hash": "b8c66d905a6c7ffa6441c84b14ea897040069dac7367895813cc2d64a9867193"
+ "hash": "7dcf840fc5b329f4a591a51e24c9dacb12266f606b5ce7e8ed6110f3f381f945"
}
diff --git a/backend/.sqlx/query-4fb3881cdbb4b9e93e28f460a9b3715bdc6a52b76c89f3a3913023b13c4e085c.json b/backend/.sqlx/query-8fda0400ec5ba04a2a1469672bbb0af413027dbd39aa2c4182e3d219640d397e.json
similarity index 69%
rename from backend/.sqlx/query-4fb3881cdbb4b9e93e28f460a9b3715bdc6a52b76c89f3a3913023b13c4e085c.json
rename to backend/.sqlx/query-8fda0400ec5ba04a2a1469672bbb0af413027dbd39aa2c4182e3d219640d397e.json
index 9709a354cf..226bcb9083 100644
--- a/backend/.sqlx/query-4fb3881cdbb4b9e93e28f460a9b3715bdc6a52b76c89f3a3913023b13c4e085c.json
+++ b/backend/.sqlx/query-8fda0400ec5ba04a2a1469672bbb0af413027dbd39aa2c4182e3d219640d397e.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2",
+ "query": "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = EXCLUDED.lockfile",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "4fb3881cdbb4b9e93e28f460a9b3715bdc6a52b76c89f3a3913023b13c4e085c"
+ "hash": "8fda0400ec5ba04a2a1469672bbb0af413027dbd39aa2c4182e3d219640d397e"
}
diff --git a/backend/.sqlx/query-e40f7e0b61567f948bfea0b6f50518564634885ccc2c0d30ccca79fc13bdcf07.json b/backend/.sqlx/query-99e6bffe177e69448b09e82b30d24af00edc86a9bc498f319c1e5bee55d77a8a.json
similarity index 60%
rename from backend/.sqlx/query-e40f7e0b61567f948bfea0b6f50518564634885ccc2c0d30ccca79fc13bdcf07.json
rename to backend/.sqlx/query-99e6bffe177e69448b09e82b30d24af00edc86a9bc498f319c1e5bee55d77a8a.json
index be7ab6923c..c5a256e0d7 100644
--- a/backend/.sqlx/query-e40f7e0b61567f948bfea0b6f50518564634885ccc2c0d30ccca79fc13bdcf07.json
+++ b/backend/.sqlx/query-99e6bffe177e69448b09e82b30d24af00edc86a9bc498f319c1e5bee55d77a8a.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO tutorial_progress VALUES ($2, $1::bigint::bit(64)) ON CONFLICT (email) DO UPDATE SET progress = $1::bigint::bit(64)",
+ "query": "INSERT INTO tutorial_progress VALUES ($2, $1::bigint::bit(64)) ON CONFLICT (email) DO UPDATE SET progress = EXCLUDED.progress",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "e40f7e0b61567f948bfea0b6f50518564634885ccc2c0d30ccca79fc13bdcf07"
+ "hash": "99e6bffe177e69448b09e82b30d24af00edc86a9bc498f319c1e5bee55d77a8a"
}
diff --git a/backend/.sqlx/query-f9e1334ecb17b313924587d96fc8b0310b14d75e85a558c8cb70f2761923d175.json b/backend/.sqlx/query-9aebf706529889dc044e0ef41da8db2b1061c22698393c2e42d94da11e697b8b.json
similarity index 78%
rename from backend/.sqlx/query-f9e1334ecb17b313924587d96fc8b0310b14d75e85a558c8cb70f2761923d175.json
rename to backend/.sqlx/query-9aebf706529889dc044e0ef41da8db2b1061c22698393c2e42d94da11e697b8b.json
index f21a486c83..1cdf7354e2 100644
--- a/backend/.sqlx/query-f9e1334ecb17b313924587d96fc8b0310b14d75e85a558c8cb70f2761923d175.json
+++ b/backend/.sqlx/query-9aebf706529889dc044e0ef41da8db2b1061c22698393c2e42d94da11e697b8b.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO usage (id, is_workspace, month_, usage) \n VALUES ($1, TRUE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), $2) \n ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + $2",
+ "query": "INSERT INTO usage (id, is_workspace, month_, usage) \n VALUES ($1, TRUE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), $2) \n ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + EXCLUDED.usage",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "f9e1334ecb17b313924587d96fc8b0310b14d75e85a558c8cb70f2761923d175"
+ "hash": "9aebf706529889dc044e0ef41da8db2b1061c22698393c2e42d94da11e697b8b"
}
diff --git a/backend/.sqlx/query-20fcdcd2674a52ee9bd8d1de518d6bce075f20bcd5d2328f183d8a59331f6bb1.json b/backend/.sqlx/query-a5bf005e0f7c9a86a136e049445de059481091ac173414418b8678f6beadf2ac.json
similarity index 75%
rename from backend/.sqlx/query-20fcdcd2674a52ee9bd8d1de518d6bce075f20bcd5d2328f183d8a59331f6bb1.json
rename to backend/.sqlx/query-a5bf005e0f7c9a86a136e049445de059481091ac173414418b8678f6beadf2ac.json
index 824335990f..6317641989 100644
--- a/backend/.sqlx/query-20fcdcd2674a52ee9bd8d1de518d6bce075f20bcd5d2328f183d8a59331f6bb1.json
+++ b/backend/.sqlx/query-a5bf005e0f7c9a86a136e049445de059481091ac173414418b8678f6beadf2ac.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO resource\n (workspace_id, path, value, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path) DO UPDATE SET value = $3, edited_at = now()",
+ "query": "INSERT INTO resource\n (workspace_id, path, value, resource_type, created_by, edited_at)\n VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path) DO UPDATE SET value = EXCLUDED.value, edited_at = now()",
"describe": {
"columns": [],
"parameters": {
@@ -14,5 +14,5 @@
},
"nullable": []
},
- "hash": "20fcdcd2674a52ee9bd8d1de518d6bce075f20bcd5d2328f183d8a59331f6bb1"
+ "hash": "a5bf005e0f7c9a86a136e049445de059481091ac173414418b8678f6beadf2ac"
}
diff --git a/backend/.sqlx/query-366609f7e7fbd73ea807128b931eff2f1ab763fa630c8531f590fed2110c03d9.json b/backend/.sqlx/query-b474ae4401b3d4c95add2d3353eb66512801c20ad685e8ed544fe4b86601aaa8.json
similarity index 86%
rename from backend/.sqlx/query-366609f7e7fbd73ea807128b931eff2f1ab763fa630c8531f590fed2110c03d9.json
rename to backend/.sqlx/query-b474ae4401b3d4c95add2d3353eb66512801c20ad685e8ed544fe4b86601aaa8.json
index e9ec98136b..1341337cc5 100644
--- a/backend/.sqlx/query-366609f7e7fbd73ea807128b931eff2f1ab763fa630c8531f590fed2110c03d9.json
+++ b/backend/.sqlx/query-b474ae4401b3d4c95add2d3353eb66512801c20ad685e8ed544fe4b86601aaa8.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO draft\n (workspace_id, path, value, typ)\n VALUES ($1, $2, $3::text::json, $4)\n ON CONFLICT (workspace_id, path, typ) DO UPDATE SET value = $3::text::json",
+ "query": "INSERT INTO draft\n (workspace_id, path, value, typ)\n VALUES ($1, $2, $3::text::json, $4)\n ON CONFLICT (workspace_id, path, typ) DO UPDATE SET value = EXCLUDED.value",
"describe": {
"columns": [],
"parameters": {
@@ -24,5 +24,5 @@
},
"nullable": []
},
- "hash": "366609f7e7fbd73ea807128b931eff2f1ab763fa630c8531f590fed2110c03d9"
+ "hash": "b474ae4401b3d4c95add2d3353eb66512801c20ad685e8ed544fe4b86601aaa8"
}
diff --git a/backend/.sqlx/query-8d67ed8e1271a27b072a8e8ae9973e372949f5ec7d71b80d1ba3eaddf851adce.json b/backend/.sqlx/query-bf2163c542fb8c4e173167a8f333ef762fecf782424c5b61b89f32918b8d6971.json
similarity index 87%
rename from backend/.sqlx/query-8d67ed8e1271a27b072a8e8ae9973e372949f5ec7d71b80d1ba3eaddf851adce.json
rename to backend/.sqlx/query-bf2163c542fb8c4e173167a8f333ef762fecf782424c5b61b89f32918b8d6971.json
index 2c70bdd6b6..46db07e1e0 100644
--- a/backend/.sqlx/query-8d67ed8e1271a27b072a8e8ae9973e372949f5ec7d71b80d1ba3eaddf851adce.json
+++ b/backend/.sqlx/query-bf2163c542fb8c4e173167a8f333ef762fecf782424c5b61b89f32918b8d6971.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "WITH inserted_job AS (\n INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job,\n created_by, permissioned_as, runnable_id, runnable_path, args, kind, trigger,\n script_lang, same_worker, pre_run_error, permissioned_as_email, visible_to_owner,\n flow_innermost_root_job, root_job, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id,\n cache_ttl, priority, trigger_kind, script_entrypoint_override, preprocessed)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,\n $19, $20, $38, $21, $22, $23, $24, $25, $26, $39::job_trigger_kind,\n ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27)\n ),\n inserted_runtime AS (\n INSERT INTO v2_job_runtime (id, ping) VALUES ($1, null)\n ),\n inserted_job_perms AS (\n INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id, end_user_email) \n values ($1, $32, $33, $34, $35, $36, $37, $2, $41) \n ON CONFLICT (job_id) DO UPDATE SET email = $32, username = $33, is_admin = $34, is_operator = $35, folders = $36, groups = $37, workspace_id = $2\n )\n INSERT INTO v2_job_queue\n (workspace_id, id, running, scheduled_for, started_at, tag, priority)\n VALUES ($2, $1, $28, COALESCE($29, now()), CASE WHEN $27 OR $40 THEN now() END, $30, $31)",
+ "query": "WITH inserted_job AS (\n INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job,\n created_by, permissioned_as, runnable_id, runnable_path, args, kind, trigger,\n script_lang, same_worker, pre_run_error, permissioned_as_email, visible_to_owner,\n flow_innermost_root_job, root_job, concurrent_limit, concurrency_time_window_s, timeout, flow_step_id,\n cache_ttl, priority, trigger_kind, script_entrypoint_override, preprocessed)\n VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18,\n $19, $20, $38, $21, $22, $23, $24, $25, $26, $39::job_trigger_kind,\n ($12::JSONB)->>'_ENTRYPOINT_OVERRIDE', $27)\n ),\n inserted_runtime AS (\n INSERT INTO v2_job_runtime (id, ping) VALUES ($1, null)\n ),\n inserted_job_perms AS (\n INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id, end_user_email) \n values ($1, $32, $33, $34, $35, $36, $37, $2, $41) \n ON CONFLICT (job_id) DO UPDATE SET email = EXCLUDED.email, username = EXCLUDED.username, is_admin = EXCLUDED.is_admin, is_operator = EXCLUDED.is_operator, folders = EXCLUDED.folders, groups = EXCLUDED.groups, workspace_id = EXCLUDED.workspace_id, end_user_email = EXCLUDED.end_user_email\n )\n INSERT INTO v2_job_queue\n (workspace_id, id, running, scheduled_for, started_at, tag, priority)\n VALUES ($2, $1, $28, COALESCE($29, now()), CASE WHEN $27 OR $40 THEN now() END, $30, $31)",
"describe": {
"columns": [],
"parameters": {
@@ -129,5 +129,5 @@
},
"nullable": []
},
- "hash": "8d67ed8e1271a27b072a8e8ae9973e372949f5ec7d71b80d1ba3eaddf851adce"
+ "hash": "bf2163c542fb8c4e173167a8f333ef762fecf782424c5b61b89f32918b8d6971"
}
diff --git a/backend/.sqlx/query-afc7c23c057748f6d4a61dbef17e433b8875c6588b91e38e8141d12189118412.json b/backend/.sqlx/query-cc8e10a4f39e118b145cc8f6fbd5f3db1fb30c9564ed039ad4c952333ec29b39.json
similarity index 74%
rename from backend/.sqlx/query-afc7c23c057748f6d4a61dbef17e433b8875c6588b91e38e8141d12189118412.json
rename to backend/.sqlx/query-cc8e10a4f39e118b145cc8f6fbd5f3db1fb30c9564ed039ad4c952333ec29b39.json
index 08ea345138..49df64b555 100644
--- a/backend/.sqlx/query-afc7c23c057748f6d4a61dbef17e433b8875c6588b91e38e8141d12189118412.json
+++ b/backend/.sqlx/query-cc8e10a4f39e118b145cc8f6fbd5f3db1fb30c9564ed039ad4c952333ec29b39.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
+ "query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET deployment_msg = EXCLUDED.deployment_msg",
"describe": {
"columns": [],
"parameters": {
@@ -13,5 +13,5 @@
},
"nullable": []
},
- "hash": "afc7c23c057748f6d4a61dbef17e433b8875c6588b91e38e8141d12189118412"
+ "hash": "cc8e10a4f39e118b145cc8f6fbd5f3db1fb30c9564ed039ad4c952333ec29b39"
}
diff --git a/backend/.sqlx/query-7b48820a08fbb3bee0fb2dc802a0b4f28ed5507797165e8c577ce6a95d11694c.json b/backend/.sqlx/query-ceb10ca124d9425b24d81d8a279a13481db39eea0b52cc513b3ee6571cf44ed4.json
similarity index 77%
rename from backend/.sqlx/query-7b48820a08fbb3bee0fb2dc802a0b4f28ed5507797165e8c577ce6a95d11694c.json
rename to backend/.sqlx/query-ceb10ca124d9425b24d81d8a279a13481db39eea0b52cc513b3ee6571cf44ed4.json
index 56481d8eb0..0b5137067a 100644
--- a/backend/.sqlx/query-7b48820a08fbb3bee0fb2dc802a0b4f28ed5507797165e8c577ce6a95d11694c.json
+++ b/backend/.sqlx/query-ceb10ca124d9425b24d81d8a279a13481db39eea0b52cc513b3ee6571cf44ed4.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO usage (id, is_workspace, month_, usage) \n VALUES ($1, FALSE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), $2) \n ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + $2",
+ "query": "INSERT INTO usage (id, is_workspace, month_, usage) \n VALUES ($1, FALSE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), $2) \n ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + EXCLUDED.usage",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "7b48820a08fbb3bee0fb2dc802a0b4f28ed5507797165e8c577ce6a95d11694c"
+ "hash": "ceb10ca124d9425b24d81d8a279a13481db39eea0b52cc513b3ee6571cf44ed4"
}
diff --git a/backend/.sqlx/query-9bfc2a821b25641af48b0e3954163078922340294f1c6515400fb2c896666fde.json b/backend/.sqlx/query-d142ff8b56f4b69c20815230b5b763ce8ce9fbe89abe83989f52ff3657690fef.json
similarity index 73%
rename from backend/.sqlx/query-9bfc2a821b25641af48b0e3954163078922340294f1c6515400fb2c896666fde.json
rename to backend/.sqlx/query-d142ff8b56f4b69c20815230b5b763ce8ce9fbe89abe83989f52ff3657690fef.json
index de16cf3146..8d92b42219 100644
--- a/backend/.sqlx/query-9bfc2a821b25641af48b0e3954163078922340294f1c6515400fb2c896666fde.json
+++ b/backend/.sqlx/query-d142ff8b56f4b69c20815230b5b763ce8ce9fbe89abe83989f52ff3657690fef.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "\n INSERT INTO job_result_stream_v2 (workspace_id, job_id, stream, idx)\n VALUES (\n $1, \n $2,\n $3, \n $4\n )\n ON CONFLICT (job_id, idx) DO UPDATE SET stream = job_result_stream_v2.stream || $3\n ",
+ "query": "\n INSERT INTO job_result_stream_v2 (workspace_id, job_id, stream, idx)\n VALUES (\n $1, \n $2,\n $3, \n $4\n )\n ON CONFLICT (job_id, idx) DO UPDATE SET stream = job_result_stream_v2.stream || EXCLUDED.stream\n ",
"describe": {
"columns": [],
"parameters": {
@@ -13,5 +13,5 @@
},
"nullable": []
},
- "hash": "9bfc2a821b25641af48b0e3954163078922340294f1c6515400fb2c896666fde"
+ "hash": "d142ff8b56f4b69c20815230b5b763ce8ce9fbe89abe83989f52ff3657690fef"
}
diff --git a/backend/.sqlx/query-fb581eb6f883ebb5909a00c65a6e1217f088290fce0e052171230bcd88f945b9.json b/backend/.sqlx/query-da7e23a32f284c9760614735d459474d2f8513202dfb64767b30e7b50833b857.json
similarity index 57%
rename from backend/.sqlx/query-fb581eb6f883ebb5909a00c65a6e1217f088290fce0e052171230bcd88f945b9.json
rename to backend/.sqlx/query-da7e23a32f284c9760614735d459474d2f8513202dfb64767b30e7b50833b857.json
index b3f640dd76..e54180983e 100644
--- a/backend/.sqlx/query-fb581eb6f883ebb5909a00c65a6e1217f088290fce0e052171230bcd88f945b9.json
+++ b/backend/.sqlx/query-da7e23a32f284c9760614735d459474d2f8513202dfb64767b30e7b50833b857.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO global_settings (name, value) VALUES ('slack', $1) ON CONFLICT (name) DO UPDATE SET value = $1, updated_at = now()",
+ "query": "INSERT INTO global_settings (name, value) VALUES ('slack', $1) ON CONFLICT (name) DO UPDATE SET value = EXCLUDED.value, updated_at = now()",
"describe": {
"columns": [],
"parameters": {
@@ -10,5 +10,5 @@
},
"nullable": []
},
- "hash": "fb581eb6f883ebb5909a00c65a6e1217f088290fce0e052171230bcd88f945b9"
+ "hash": "da7e23a32f284c9760614735d459474d2f8513202dfb64767b30e7b50833b857"
}
diff --git a/backend/.sqlx/query-c4e0f3eab227a798d9cd7478db50a7c1a588e69c2c8d9a1def9276ba326453d2.json b/backend/.sqlx/query-db201730803047bfabccf5f10456243b590b2dd0cfe41aa5100a7e829afad9d3.json
similarity index 63%
rename from backend/.sqlx/query-c4e0f3eab227a798d9cd7478db50a7c1a588e69c2c8d9a1def9276ba326453d2.json
rename to backend/.sqlx/query-db201730803047bfabccf5f10456243b590b2dd0cfe41aa5100a7e829afad9d3.json
index bd16dab3bf..033d77862e 100644
--- a/backend/.sqlx/query-c4e0f3eab227a798d9cd7478db50a7c1a588e69c2c8d9a1def9276ba326453d2.json
+++ b/backend/.sqlx/query-db201730803047bfabccf5f10456243b590b2dd0cfe41aa5100a7e829afad9d3.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO cloud_workspace_settings (workspace_id, threshold_alert_amount) VALUES ($1, $2) ON CONFLICT (workspace_id) DO UPDATE SET threshold_alert_amount = $2, last_alert_sent = NULL",
+ "query": "INSERT INTO cloud_workspace_settings (workspace_id, threshold_alert_amount) VALUES ($1, $2) ON CONFLICT (workspace_id) DO UPDATE SET threshold_alert_amount = EXCLUDED.threshold_alert_amount, last_alert_sent = NULL",
"describe": {
"columns": [],
"parameters": {
@@ -11,5 +11,5 @@
},
"nullable": []
},
- "hash": "c4e0f3eab227a798d9cd7478db50a7c1a588e69c2c8d9a1def9276ba326453d2"
+ "hash": "db201730803047bfabccf5f10456243b590b2dd0cfe41aa5100a7e829afad9d3"
}
diff --git a/backend/.sqlx/query-a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c.json b/backend/.sqlx/query-e16f464c7e302e80f1017ff6550716448202c1181ca0b36936a9c8840c14a95d.json
similarity index 76%
rename from backend/.sqlx/query-a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c.json
rename to backend/.sqlx/query-e16f464c7e302e80f1017ff6550716448202c1181ca0b36936a9c8840c14a95d.json
index 4e0d53b0f3..3dc6dd5339 100644
--- a/backend/.sqlx/query-a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c.json
+++ b/backend/.sqlx/query-e16f464c7e302e80f1017ff6550716448202c1181ca0b36936a9c8840c14a95d.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, $1::text) RETURNING length(logs)",
+ "query": "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, EXCLUDED.logs) RETURNING length(logs)",
"describe": {
"columns": [
{
@@ -20,5 +20,5 @@
null
]
},
- "hash": "a2e86f169ffbf8acee5f7c7b71db5859ac94ffbad267c9cd6c652e8ce8fc5d3c"
+ "hash": "e16f464c7e302e80f1017ff6550716448202c1181ca0b36936a9c8840c14a95d"
}
diff --git a/backend/.sqlx/query-f37140fcdc721a8b199471b30c2baf124affa2eaf56c801c8dac3264c584f981.json b/backend/.sqlx/query-e572fa64eec9188368d7c271ac7ecd6b45dc161423abab22cddb2b13f6fb9833.json
similarity index 74%
rename from backend/.sqlx/query-f37140fcdc721a8b199471b30c2baf124affa2eaf56c801c8dac3264c584f981.json
rename to backend/.sqlx/query-e572fa64eec9188368d7c271ac7ecd6b45dc161423abab22cddb2b13f6fb9833.json
index ace3edec94..2ee8c422fd 100644
--- a/backend/.sqlx/query-f37140fcdc721a8b199471b30c2baf124affa2eaf56c801c8dac3264c584f981.json
+++ b/backend/.sqlx/query-e572fa64eec9188368d7c271ac7ecd6b45dc161423abab22cddb2b13f6fb9833.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO deployment_metadata (workspace_id, path, app_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET callback_job_ids = $4, deployment_msg = $5",
+ "query": "INSERT INTO deployment_metadata (workspace_id, path, app_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET callback_job_ids = EXCLUDED.callback_job_ids, deployment_msg = EXCLUDED.deployment_msg",
"describe": {
"columns": [],
"parameters": {
@@ -14,5 +14,5 @@
},
"nullable": []
},
- "hash": "f37140fcdc721a8b199471b30c2baf124affa2eaf56c801c8dac3264c584f981"
+ "hash": "e572fa64eec9188368d7c271ac7ecd6b45dc161423abab22cddb2b13f6fb9833"
}
diff --git a/backend/.sqlx/query-6afc5c7cbb3abe11ade0cedf1f7328005ce4de3165cdd998e5a0d27e044c7153.json b/backend/.sqlx/query-e5f8830450e90f678494ae2b0f86b0692a02dec4c8415fd7d364b0cc088905c2.json
similarity index 70%
rename from backend/.sqlx/query-6afc5c7cbb3abe11ade0cedf1f7328005ce4de3165cdd998e5a0d27e044c7153.json
rename to backend/.sqlx/query-e5f8830450e90f678494ae2b0f86b0692a02dec4c8415fd7d364b0cc088905c2.json
index 0bbe880941..3867319977 100644
--- a/backend/.sqlx/query-6afc5c7cbb3abe11ade0cedf1f7328005ce4de3165cdd998e5a0d27e044c7153.json
+++ b/backend/.sqlx/query-e5f8830450e90f678494ae2b0f86b0692a02dec4c8415fd7d364b0cc088905c2.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO worker_ping (worker_instance, worker, ip, custom_tags, worker_group, dedicated_worker, wm_version, vcpus, memory) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (worker) DO UPDATE set ip = $3, custom_tags = $4, worker_group = $5",
+ "query": "INSERT INTO worker_ping (worker_instance, worker, ip, custom_tags, worker_group, dedicated_worker, wm_version, vcpus, memory) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (worker) \n DO UPDATE set ip = EXCLUDED.ip, custom_tags = EXCLUDED.custom_tags, worker_group = EXCLUDED.worker_group",
"describe": {
"columns": [],
"parameters": {
@@ -18,5 +18,5 @@
},
"nullable": []
},
- "hash": "6afc5c7cbb3abe11ade0cedf1f7328005ce4de3165cdd998e5a0d27e044c7153"
+ "hash": "e5f8830450e90f678494ae2b0f86b0692a02dec4c8415fd7d364b0cc088905c2"
}
diff --git a/backend/.sqlx/query-2367e7c0f7fbafe0971a187c0909617da55251e97180babf6ac9e8068f26d73d.json b/backend/.sqlx/query-e8e33f599eae064011232f9f715e676d0c8ae31982865cb2c8103ed735c42c69.json
similarity index 74%
rename from backend/.sqlx/query-2367e7c0f7fbafe0971a187c0909617da55251e97180babf6ac9e8068f26d73d.json
rename to backend/.sqlx/query-e8e33f599eae064011232f9f715e676d0c8ae31982865cb2c8103ed735c42c69.json
index d643e4b8f8..84645b71b0 100644
--- a/backend/.sqlx/query-2367e7c0f7fbafe0971a187c0909617da55251e97180babf6ac9e8068f26d73d.json
+++ b/backend/.sqlx/query-e8e33f599eae064011232f9f715e676d0c8ae31982865cb2c8103ed735c42c69.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET callback_job_ids = $4, deployment_msg = $5",
+ "query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, callback_job_ids, deployment_msg) VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET callback_job_ids = EXCLUDED.callback_job_ids, deployment_msg = EXCLUDED.deployment_msg",
"describe": {
"columns": [],
"parameters": {
@@ -14,5 +14,5 @@
},
"nullable": []
},
- "hash": "2367e7c0f7fbafe0971a187c0909617da55251e97180babf6ac9e8068f26d73d"
+ "hash": "e8e33f599eae064011232f9f715e676d0c8ae31982865cb2c8103ed735c42c69"
}
diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt
index 48777ef499..253d116503 100644
--- a/backend/ee-repo-ref.txt
+++ b/backend/ee-repo-ref.txt
@@ -1 +1 @@
-af5cfc6b0f0bd42f95a5842dff92cf598d3aa6d8
+0f18d7155059dccebbe690162b52133d526e6fb5
\ No newline at end of file
diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs
index 6d66d2ad66..e4d3c380af 100644
--- a/backend/src/monitor.rs
+++ b/backend/src/monitor.rs
@@ -2724,7 +2724,7 @@ pub async fn reload_critical_alerts_on_db_oversize(conn: &DB) -> error::Result<(
async fn generate_and_save_jwt_secret(db: &DB) -> error::Result {
let secret = rd_string(32);
sqlx::query!(
- "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2",
+ "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = EXCLUDED.value",
JWT_SECRET_SETTING,
serde_json::to_value(&secret).unwrap()
).execute(db).await?;
diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs
index 3043be10ce..15a89d762f 100644
--- a/backend/windmill-api/src/apps.rs
+++ b/backend/windmill-api/src/apps.rs
@@ -699,7 +699,7 @@ async fn update_app_history(
check_scopes(&authed, || format!("apps:write:{}", &app_path))?;
sqlx::query!(
- "INSERT INTO deployment_metadata (workspace_id, path, app_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
+ "INSERT INTO deployment_metadata (workspace_id, path, app_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, app_version) WHERE app_version IS NOT NULL DO UPDATE SET deployment_msg = EXCLUDED.deployment_msg",
w_id,
app_path,
app_version,
diff --git a/backend/windmill-api/src/configs.rs b/backend/windmill-api/src/configs.rs
index 5a1e597592..719437b29c 100644
--- a/backend/windmill-api/src/configs.rs
+++ b/backend/windmill-api/src/configs.rs
@@ -173,7 +173,7 @@ async fn update_config(
let mut tx = db.begin().await?;
sqlx::query!(
- "INSERT INTO config (name, config) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET config = $2",
+ "INSERT INTO config (name, config) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET config = EXCLUDED.config",
&name,
config
)
diff --git a/backend/windmill-api/src/drafts.rs b/backend/windmill-api/src/drafts.rs
index 02d3b3c4fb..41e8d4709d 100644
--- a/backend/windmill-api/src/drafts.rs
+++ b/backend/windmill-api/src/drafts.rs
@@ -79,7 +79,7 @@ async fn create_draft(
"INSERT INTO draft
(workspace_id, path, value, typ)
VALUES ($1, $2, $3::text::json, $4)
- ON CONFLICT (workspace_id, path, typ) DO UPDATE SET value = $3::text::json",
+ ON CONFLICT (workspace_id, path, typ) DO UPDATE SET value = EXCLUDED.value",
&w_id,
draft.path,
//to preserve key orders
diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs
index cd21e5b3c8..c983fb8d1f 100644
--- a/backend/windmill-api/src/flows.rs
+++ b/backend/windmill-api/src/flows.rs
@@ -722,7 +722,7 @@ async fn update_flow_history(
}
sqlx::query!(
- "INSERT INTO deployment_metadata (workspace_id, path, flow_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET deployment_msg = $4",
+ "INSERT INTO deployment_metadata (workspace_id, path, flow_version, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL DO UPDATE SET deployment_msg = EXCLUDED.deployment_msg",
w_id,
path_o.unwrap(),
version,
diff --git a/backend/windmill-api/src/resources.rs b/backend/windmill-api/src/resources.rs
index df31ed058e..9decd976f8 100644
--- a/backend/windmill-api/src/resources.rs
+++ b/backend/windmill-api/src/resources.rs
@@ -740,7 +740,7 @@ async fn create_resource(
"INSERT INTO resource
(workspace_id, path, value, description, resource_type, created_by, edited_at)
VALUES ($1, $2, $3, $4, $5, $6, now()) ON CONFLICT (workspace_id, path)
- DO UPDATE SET value = $3, description = $4, resource_type = $5, edited_at = now()",
+ DO UPDATE SET value = EXCLUDED.value, description = EXCLUDED.description, resource_type = EXCLUDED.resource_type, edited_at = now()",
w_id,
resource.path,
raw_json as sqlx::types::Json<&RawValue>,
diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs
index 6aaf77366b..56107f055e 100644
--- a/backend/windmill-api/src/scripts.rs
+++ b/backend/windmill-api/src/scripts.rs
@@ -1249,7 +1249,8 @@ async fn update_script_history(
let mut tx = user_db.begin(&authed).await?;
sqlx::query!(
- "INSERT INTO deployment_metadata (workspace_id, path, script_hash, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL DO UPDATE SET deployment_msg = $4",
+ "INSERT INTO deployment_metadata (workspace_id, path, script_hash, deployment_msg) VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL
+ DO UPDATE SET deployment_msg = EXCLUDED.deployment_msg",
w_id,
script_path,
script_hash.0,
diff --git a/backend/windmill-api/src/settings.rs b/backend/windmill-api/src/settings.rs
index c7c3aba0d3..b1c9df8536 100644
--- a/backend/windmill-api/src/settings.rs
+++ b/backend/windmill-api/src/settings.rs
@@ -334,7 +334,7 @@ pub async fn set_global_setting_internal(
}
v => {
sqlx::query!(
- "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2, updated_at = now()",
+ "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = EXCLUDED.value, updated_at = now()",
key,
v
)
diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs
index 722c05ebce..67a946f16b 100644
--- a/backend/windmill-api/src/users.rs
+++ b/backend/windmill-api/src/users.rs
@@ -583,7 +583,7 @@ async fn update_tutorial_progress(
Json(progress): Json,
) -> Result {
sqlx::query_scalar!(
- "INSERT INTO tutorial_progress VALUES ($2, $1::bigint::bit(64)) ON CONFLICT (email) DO UPDATE SET progress = $1::bigint::bit(64)",
+ "INSERT INTO tutorial_progress VALUES ($2, $1::bigint::bit(64)) ON CONFLICT (email) DO UPDATE SET progress = EXCLUDED.progress",
progress.progress as i64,
authed.email
)
@@ -1385,7 +1385,9 @@ async fn convert_user_to_group(
// Check if user is already a group user
if let Some(added_via) = &user_info.added_via {
if added_via.get("source").and_then(|v| v.as_str()) == Some("instance_group") {
- return Err(Error::BadRequest("User is already a group user".to_string()));
+ return Err(Error::BadRequest(
+ "User is already a group user".to_string(),
+ ));
}
}
@@ -1408,16 +1410,18 @@ async fn convert_user_to_group(
if eligible_groups.is_empty() {
return Err(Error::BadRequest(
- "User is not a member of any instance groups configured for auto-add in this workspace".to_string()
+ "User is not a member of any instance groups configured for auto-add in this workspace"
+ .to_string(),
));
}
// Determine the group with highest precedence (same logic as process_instance_group_auto_adds)
- let roles: std::collections::HashMap = if let Some(roles_json) = &eligible_groups[0].auto_add_instance_groups_roles {
- serde_json::from_value(roles_json.clone()).unwrap_or_default()
- } else {
- std::collections::HashMap::new()
- };
+ let roles: std::collections::HashMap =
+ if let Some(roles_json) = &eligible_groups[0].auto_add_instance_groups_roles {
+ serde_json::from_value(roles_json.clone()).unwrap_or_default()
+ } else {
+ std::collections::HashMap::new()
+ };
let mut best_group = &eligible_groups[0].group_name;
let mut best_precedence = 0u8;
@@ -1443,7 +1447,10 @@ async fn convert_user_to_group(
// Determine role from group configuration using the selected primary group
let default_role = "developer".to_string();
- let role = roles.get(primary_group_name).unwrap_or(&default_role).as_str();
+ let role = roles
+ .get(primary_group_name)
+ .unwrap_or(&default_role)
+ .as_str();
let (is_admin, is_operator) = match role {
"admin" => (true, false),
@@ -1487,12 +1494,18 @@ async fn convert_user_to_group(
&db,
&w_id,
windmill_git_sync::DeployedObject::User { email: user_info.email.clone() },
- Some(format!("Converted user '{}' to group user (group: {}, role: {})", &user_info.email, primary_group_name, role)),
+ Some(format!(
+ "Converted user '{}' to group user (group: {}, role: {})",
+ &user_info.email, primary_group_name, role
+ )),
true,
)
.await?;
- Ok(format!("User {} converted to group user (group: {}, role: {})", username_to_convert, primary_group_name, role))
+ Ok(format!(
+ "User {} converted to group user (group: {}, role: {})",
+ username_to_convert, primary_group_name, role
+ ))
}
async fn update_user(
@@ -1594,9 +1607,12 @@ async fn delete_user(
}
// Remove user from all instance groups email_to_igroup
- sqlx::query!("DELETE FROM email_to_igroup WHERE email = $1", &email_to_delete)
- .execute(&mut *tx)
- .await?;
+ sqlx::query!(
+ "DELETE FROM email_to_igroup WHERE email = $1",
+ &email_to_delete
+ )
+ .execute(&mut *tx)
+ .await?;
audit_log(
&mut *tx,
diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs
index b5a166bf96..ff399ee7c6 100644
--- a/backend/windmill-api/src/workspaces.rs
+++ b/backend/windmill-api/src/workspaces.rs
@@ -1901,7 +1901,7 @@ async fn set_environment_variable(
match value {
Some(value) => {
sqlx::query!(
- "INSERT INTO workspace_env (workspace_id, name, value) VALUES ($1, $2, $3) ON CONFLICT (workspace_id, name) DO UPDATE SET value = $3",
+ "INSERT INTO workspace_env (workspace_id, name, value) VALUES ($1, $2, $3) ON CONFLICT (workspace_id, name) DO UPDATE SET value = EXCLUDED.value",
&w_id,
name,
value
@@ -3155,7 +3155,7 @@ async fn invite_user(
"INSERT INTO workspace_invite
(workspace_id, email, is_admin, operator)
VALUES ($1, $2, $3, $4) ON CONFLICT (workspace_id, email)
- DO UPDATE SET is_admin = $3, operator = $4",
+ DO UPDATE SET is_admin = EXCLUDED.is_admin, operator = EXCLUDED.operator",
&w_id,
nu.email,
nu.is_admin,
diff --git a/backend/windmill-common/src/result_stream.rs b/backend/windmill-common/src/result_stream.rs
index 02bfa75b92..c44c2d556e 100644
--- a/backend/windmill-common/src/result_stream.rs
+++ b/backend/windmill-common/src/result_stream.rs
@@ -31,7 +31,7 @@ pub async fn append_result_stream_db(
$3,
$4
)
- ON CONFLICT (job_id, idx) DO UPDATE SET stream = job_result_stream_v2.stream || $3
+ ON CONFLICT (job_id, idx) DO UPDATE SET stream = job_result_stream_v2.stream || EXCLUDED.stream
"#,
workspace_id,
job_id,
diff --git a/backend/windmill-common/src/worker.rs b/backend/windmill-common/src/worker.rs
index 704a31e6e0..4568e910e4 100644
--- a/backend/windmill-common/src/worker.rs
+++ b/backend/windmill-common/src/worker.rs
@@ -1277,7 +1277,8 @@ pub async fn insert_ping_query(
db: &DB,
) -> anyhow::Result<()> {
sqlx::query!(
- "INSERT INTO worker_ping (worker_instance, worker, ip, custom_tags, worker_group, dedicated_worker, wm_version, vcpus, memory) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (worker) DO UPDATE set ip = $3, custom_tags = $4, worker_group = $5",
+ "INSERT INTO worker_ping (worker_instance, worker, ip, custom_tags, worker_group, dedicated_worker, wm_version, vcpus, memory) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (worker)
+ DO UPDATE set ip = EXCLUDED.ip, custom_tags = EXCLUDED.custom_tags, worker_group = EXCLUDED.worker_group",
worker_instance,
worker_name,
ip,
diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs
index 326c5a964c..000e15ff00 100644
--- a/backend/windmill-queue/src/jobs.rs
+++ b/backend/windmill-queue/src/jobs.rs
@@ -374,7 +374,7 @@ pub async fn append_logs(
match conn {
Connection::Sql(pool) => {
if let Err(err) = sqlx::query!(
- "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, $1::text)",
+ "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, EXCLUDED.logs)",
logs.as_ref(),
job_id,
workspace.as_ref(),
@@ -1429,7 +1429,7 @@ fn apply_completed_job_cloud_usage(
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)
- ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + $2",
+ ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + EXCLUDED.usage",
w_id,
additional_usage as i32
)
@@ -1443,7 +1443,7 @@ fn apply_completed_job_cloud_usage(
let _ = sqlx::query!(
"INSERT INTO usage (id, is_workspace, month_, usage)
VALUES ($1, FALSE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), $2)
- ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + $2",
+ ON CONFLICT (id, is_workspace, month_) DO UPDATE SET usage = usage.usage + EXCLUDED.usage",
email,
additional_usage as i32
)
@@ -4328,7 +4328,7 @@ pub async fn push<'c, 'd>(
inserted_job_perms AS (
INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id, end_user_email)
values ($1, $32, $33, $34, $35, $36, $37, $2, $41)
- ON CONFLICT (job_id) DO UPDATE SET email = $32, username = $33, is_admin = $34, is_operator = $35, folders = $36, groups = $37, workspace_id = $2
+ ON CONFLICT (job_id) DO UPDATE SET email = EXCLUDED.email, username = EXCLUDED.username, is_admin = EXCLUDED.is_admin, is_operator = EXCLUDED.is_operator, folders = EXCLUDED.folders, groups = EXCLUDED.groups, workspace_id = EXCLUDED.workspace_id, end_user_email = EXCLUDED.end_user_email
)
INSERT INTO v2_job_queue
(workspace_id, id, running, scheduled_for, started_at, tag, priority)
diff --git a/backend/windmill-worker/src/common.rs b/backend/windmill-worker/src/common.rs
index 38e7b366f6..eea0a8fbb9 100644
--- a/backend/windmill-worker/src/common.rs
+++ b/backend/windmill-worker/src/common.rs
@@ -997,7 +997,7 @@ pub async fn save_in_cache(
"INSERT INTO resource
(workspace_id, path, value, resource_type, created_by, edited_at)
VALUES ($1, $2, $3, $4, $5, now()) ON CONFLICT (workspace_id, path)
- DO UPDATE SET value = $3, edited_at = now()",
+ DO UPDATE SET value = EXCLUDED.value, edited_at = now()",
job.workspace_id,
&cached_path,
raw_json as Json<&CachedResource>,
diff --git a/backend/windmill-worker/src/go_executor.rs b/backend/windmill-worker/src/go_executor.rs
index 7a758051ab..0409ceba95 100644
--- a/backend/windmill-worker/src/go_executor.rs
+++ b/backend/windmill-worker/src/go_executor.rs
@@ -406,7 +406,10 @@ func Run(req Req) (interface{{}}, error){{
#[cfg(windows)]
set_windows_env_vars(&mut run_go);
- run_go.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped());
+ run_go
+ .stdin(Stdio::null())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped());
start_child_process(run_go, &compiled_executable_name, false).await?
};
let handle_result = handle_child(
@@ -647,7 +650,7 @@ pub async fn install_go_dependencies(
if non_dep_job {
if let Some(db) = conn.as_sql() {
sqlx::query!(
- "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('5 mins')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2",
+ "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('5 mins')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = EXCLUDED.lockfile",
hash,
req_content
)
diff --git a/backend/windmill-worker/src/java_executor.rs b/backend/windmill-worker/src/java_executor.rs
index d133308489..92ef69da16 100644
--- a/backend/windmill-worker/src/java_executor.rs
+++ b/backend/windmill-worker/src/java_executor.rs
@@ -247,7 +247,7 @@ pub async fn resolve<'a>(
if let Connection::Sql(db) = conn {
sqlx::query!(
- "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('5 mins')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2",
+ "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('5 mins')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = EXCLUDED.lockfile",
req_hash,
lock.clone(),
)
diff --git a/backend/windmill-worker/src/job_logger.rs b/backend/windmill-worker/src/job_logger.rs
index 11e7785ee5..364c352a21 100644
--- a/backend/windmill-worker/src/job_logger.rs
+++ b/backend/windmill-worker/src/job_logger.rs
@@ -108,7 +108,7 @@ pub async fn append_logs_with_compaction(
worker_name: &str,
) {
let log_length = sqlx::query_scalar!(
- "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, $1::text) RETURNING length(logs)",
+ "INSERT INTO job_logs (logs, job_id, workspace_id) VALUES ($1, $2, $3) ON CONFLICT (job_id) DO UPDATE SET logs = concat(job_logs.logs, EXCLUDED.logs) RETURNING length(logs)",
logs,
job_id,
&w_id,
diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs
index c4e88e0853..7266ead92d 100644
--- a/backend/windmill-worker/src/python_executor.rs
+++ b/backend/windmill-worker/src/python_executor.rs
@@ -413,7 +413,7 @@ pub async fn uv_pip_compile(
);
if let Some(db) = conn.as_sql() {
sqlx::query!(
- "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('5 mins')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2",
+ "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('5 mins')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = EXCLUDED.lockfile",
req_hash,
lockfile
).fetch_optional(db).await?;
diff --git a/backend/windmill-worker/src/ruby_executor.rs b/backend/windmill-worker/src/ruby_executor.rs
index b635a6bade..4dbc5bcf8a 100644
--- a/backend/windmill-worker/src/ruby_executor.rs
+++ b/backend/windmill-worker/src/ruby_executor.rs
@@ -433,7 +433,7 @@ Your Gemfile syntax will continue to work as-is."
if let Some(db) = conn.as_sql() {
sqlx::query!(
- "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = $2",
+ "INSERT INTO pip_resolution_cache (hash, lockfile, expiration) VALUES ($1, $2, now() + ('3 days')::interval) ON CONFLICT (hash) DO UPDATE SET lockfile = EXCLUDED.lockfile",
req_hash,
lock.clone(),
).fetch_optional(db).await?;
From 3b5c96247350b70fe947d204e9bff61f81be219c Mon Sep 17 00:00:00 2001
From: hugocasa
Date: Thu, 16 Oct 2025 16:05:06 +0200
Subject: [PATCH 11/33] fix(backend): revert flow node opti for ai agents
(#6840)
* Revert "feat(backend): use flow nodes opti for ai agent steps (#6808)"
This reverts commit 8d5acda340cd105c5b0dfc2bfe59b7e996bd2707.
* fix(backend): revert flow node opti for ai agents
* keep standard base64
---
backend/windmill-common/src/cache.rs | 38 +++--
backend/windmill-common/src/flows.rs | 13 --
backend/windmill-common/src/jobs.rs | 1 -
backend/windmill-queue/src/jobs.rs | 9 +-
backend/windmill-worker/src/ai_executor.rs | 135 +++++-------------
backend/windmill-worker/src/worker_flow.rs | 4 +-
.../windmill-worker/src/worker_lockfiles.rs | 48 ++-----
7 files changed, 76 insertions(+), 172 deletions(-)
diff --git a/backend/windmill-common/src/cache.rs b/backend/windmill-common/src/cache.rs
index f46259f457..3d1524916a 100644
--- a/backend/windmill-common/src/cache.rs
+++ b/backend/windmill-common/src/cache.rs
@@ -15,6 +15,7 @@ use crate::{
scripts::{ScriptHash, ScriptLang},
};
use anyhow::anyhow;
+use serde_json::value::to_raw_value;
#[cfg(feature = "scoped_cache")]
use std::thread::ThreadId;
@@ -282,25 +283,32 @@ pub mod future {
pub struct FlowData {
pub raw_flow: Box,
pub flow: FlowValue,
- pub summary: Option,
+}
+
+/// !!!Shouldn't be used. Reverted optimization for ai agent steps.!!!
+#[derive(Deserialize)]
+struct RevertedFlowNodeFlow {
+ value: FlowValue,
}
impl FlowData {
pub fn from_raw(raw_flow: Box) -> error::Result {
- let (flow, summary) = if let Ok(parsed) =
- serde_json::from_str::(raw_flow.get())
- {
- (parsed.value, parsed.summary)
- } else {
- // fallback to plain FlowValue
- (
- serde_json::from_str::(raw_flow.get()).map_err(|e| {
- error::Error::internal_err(format!("Failed to parse as FlowValue: {}", e))
- })?,
- None,
- )
- };
- Ok(Self { raw_flow, flow, summary })
+ match serde_json::from_str::(raw_flow.get()) {
+ Ok(flow) => Ok(FlowData { raw_flow, flow }),
+ _ => {
+ // fallback for compatibility with bad version 1.560.0
+ // TODO: remove this in a future version. Reverted optimization for ai agent steps.
+ let flow_node_flow = serde_json::from_str::(raw_flow.get())
+ .map_err(|e| {
+ error::Error::internal_err(format!(
+ "Failed to parse as RevertedFlowNodeFlow: {}",
+ e
+ ))
+ })?;
+ let raw_flow = to_raw_value(&flow_node_flow.value)?;
+ Ok(FlowData { raw_flow, flow: flow_node_flow.value })
+ }
+ }
}
pub fn value(&self) -> &FlowValue {
diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs
index 99093cc107..810368f901 100644
--- a/backend/windmill-common/src/flows.rs
+++ b/backend/windmill-common/src/flows.rs
@@ -141,13 +141,6 @@ pub struct FlowValue {
pub chat_input_enabled: Option,
}
-#[derive(Serialize, Deserialize)]
-pub struct FlowNodeFlow {
- pub value: FlowValue,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub summary: Option,
-}
-
impl FlowValue {
pub fn get_flow_module_at_step(&self, step: Step) -> anyhow::Result<&FlowModule> {
let flow_module = match step {
@@ -733,8 +726,6 @@ pub enum FlowModuleValue {
AIAgent {
input_transforms: HashMap,
tools: Vec,
- #[serde(skip_serializing_if = "Option::is_none")]
- modules_node: Option,
},
}
@@ -872,7 +863,6 @@ impl<'de> Deserialize<'de> for FlowModuleValue {
tools: untagged
.tools
.ok_or_else(|| serde::de::Error::missing_field("tools"))?,
- modules_node: untagged.modules_node,
}),
other => Err(serde::de::Error::unknown_variant(
other,
@@ -1064,9 +1054,6 @@ pub async fn resolve_module(
.await?;
}
}
- AIAgent { tools, modules_node, .. } => {
- resolve_modules(db, workspace_id, tools, modules_node.take(), with_code).await?;
- }
_ => {}
}
*value = to_raw_value(&val);
diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs
index b4d4f0acc7..4608fe72b3 100644
--- a/backend/windmill-common/src/jobs.rs
+++ b/backend/windmill-common/src/jobs.rs
@@ -425,7 +425,6 @@ pub enum JobPayload {
Noop,
AIAgent {
path: String,
- flow_node_id: Option,
},
}
diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs
index 000e15ff00..5cec1ac6f2 100644
--- a/backend/windmill-queue/src/jobs.rs
+++ b/backend/windmill-queue/src/jobs.rs
@@ -3737,8 +3737,7 @@ pub async fn push<'c, 'd>(
if let Some(skip_handler) = skip_handler {
let mut skip_input_transforms = HashMap::::new();
for (arg_name, arg_value) in skip_handler.args {
- skip_input_transforms
- .insert(arg_name, InputTransform::Static { value: arg_value });
+ skip_input_transforms.insert(arg_name, InputTransform::Static { value: arg_value });
}
modules.push(FlowModule {
@@ -3873,7 +3872,7 @@ pub async fn push<'c, 'd>(
// this is a new flow being pushed, flow_status is set to flow_value:
let flow_status: FlowStatus = FlowStatus::new(&flow_value);
(
- None, // No version needed - flow is stored in raw_flow like FlowPreview
+ None, // No version needed - flow is stored in raw_flow like FlowPreview
Some(path),
None,
JobKind::SingleStepFlow,
@@ -4072,8 +4071,8 @@ pub async fn push<'c, 'd>(
None,
None,
),
- JobPayload::AIAgent { path, flow_node_id } => (
- flow_node_id.map(|id| id.0),
+ JobPayload::AIAgent { path } => (
+ None,
Some(path),
None,
JobKind::AIAgent,
diff --git a/backend/windmill-worker/src/ai_executor.rs b/backend/windmill-worker/src/ai_executor.rs
index 8fda760a83..01a4b36b71 100644
--- a/backend/windmill-worker/src/ai_executor.rs
+++ b/backend/windmill-worker/src/ai_executor.rs
@@ -1,7 +1,4 @@
-use crate::{
- memory_oss::{read_from_memory, write_to_memory},
- worker_flow::JobPayloadWithTag,
-};
+use crate::memory_oss::{read_from_memory, write_to_memory};
use anyhow::Context;
use async_recursion::async_recursion;
use regex::Regex;
@@ -17,9 +14,9 @@ use windmill_common::{
error::{self, to_anyhow, Error},
flow_conversations::{add_message_to_conversation_tx, MessageType},
flow_status::AgentAction,
- flows::{FlowModuleValue, FlowNodeId, Step},
+ flows::{FlowModuleValue, FlowValue, Step},
get_latest_hash_for_path,
- jobs::{JobKind, JobPayload},
+ jobs::JobKind,
scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang},
utils::{StripPath, HTTP_CLIENT},
worker::{to_raw_value, Connection},
@@ -209,51 +206,36 @@ pub async fn handle_ai_agent_job(
));
};
- let (tools, summary) = if let Some(ScriptHash(flow_node_id)) = job.runnable_id {
- tracing::debug!(
- "Fetching AI Agent flow data using flow node id {}",
- flow_node_id
- );
- let flow_data = cache::flow::fetch_flow(db, FlowNodeId(flow_node_id)).await?;
+ let flow_job = get_flow_job_runnable_and_raw_flow(db, &parent_job).await?;
- let value = flow_data.value();
-
- (value.modules.clone(), flow_data.summary.clone())
- } else {
- tracing::debug!("Fetching flow data for parent job of AI Agent job");
- let flow_job = get_flow_job_runnable_and_raw_flow(db, &parent_job).await?;
-
- let flow_data = match flow_job.kind {
- JobKind::Flow | JobKind::FlowNode => {
- cache::job::fetch_flow(db, &flow_job.kind, flow_job.runnable_id).await?
- }
- JobKind::FlowPreview => {
- cache::job::fetch_preview_flow(db, &parent_job, flow_job.raw_flow).await?
- }
- _ => {
- return Err(Error::internal_err(
- "expected parent flow, flow preview or flow node for ai agent job".to_string(),
- ));
- }
- };
-
- let value = flow_data.value();
-
- let module = value.modules.iter().find(|m| m.id == *flow_step_id);
-
- let Some(module) = module else {
+ let flow_data = match flow_job.kind {
+ JobKind::Flow | JobKind::FlowNode => {
+ cache::job::fetch_flow(db, &flow_job.kind, flow_job.runnable_id).await?
+ }
+ JobKind::FlowPreview => {
+ cache::job::fetch_preview_flow(db, &parent_job, flow_job.raw_flow).await?
+ }
+ _ => {
return Err(Error::internal_err(
- "AI agent module not found in flow".to_string(),
+ "expected parent flow, flow preview or flow node for ai agent job".to_string(),
));
- };
+ }
+ };
- let FlowModuleValue::AIAgent { tools, .. } = module.get_value()? else {
- return Err(Error::internal_err(
- "AI agent module is not an AI agent".to_string(),
- ));
- };
+ let value = flow_data.value();
- (tools, module.summary.clone())
+ let module = value.modules.iter().find(|m| m.id == *flow_step_id);
+
+ let Some(module) = module else {
+ return Err(Error::internal_err(
+ "AI agent module not found in flow".to_string(),
+ ));
+ };
+
+ let FlowModuleValue::AIAgent { tools, .. } = module.get_value()? else {
+ return Err(Error::internal_err(
+ "AI agent module is not an AI agent".to_string(),
+ ));
};
let tools = futures::future::try_join_all(tools.into_iter().map(|mut t| {
@@ -321,10 +303,6 @@ pub async fn handle_ai_agent_job(
Ok(FlowModuleValue::RawScript { content, language, .. }) => {
Ok(Some(parse_raw_script_schema(&content, &language)?))
}
- Ok(FlowModuleValue::FlowScript { id, language, .. }) => {
- let script_data = cache::flow::fetch_script(conn, id.clone()).await?;
- Ok(Some(parse_raw_script_schema(&script_data.code, &language)?))
- }
Err(e) => {
return Err(Error::internal_err(format!(
"Invalid tool {}: {}",
@@ -376,7 +354,7 @@ pub async fn handle_ai_agent_job(
parent_job,
&args,
&tools,
- summary.as_deref(),
+ value,
client,
&mut inner_occupancy_metrics,
job_completed_tx,
@@ -491,12 +469,14 @@ async fn update_flow_status_module_with_actions_success(
}
/// Get step name from the flow module (summary if exists, else id)
-fn get_step_name_from_flow(summary: Option<&str>, flow_step_id: Option<&str>) -> Option {
+fn get_step_name_from_flow(flow_value: &FlowValue, flow_step_id: Option<&str>) -> Option {
let flow_step_id = flow_step_id?;
+ let module = flow_value.modules.iter().find(|m| m.id == flow_step_id)?;
Some(
- summary
- .map(|s| s.to_string())
- .unwrap_or_else(|| format!("AI Agent Step {}", flow_step_id)),
+ module
+ .summary
+ .clone()
+ .unwrap_or_else(|| format!("AI Agent Step {}", module.id)),
)
}
@@ -519,7 +499,7 @@ pub async fn run_agent(
parent_job: &Uuid,
args: &AIAgentArgs,
tools: &[Tool],
- summary: Option<&str>,
+ flow_value: &FlowValue,
// job execution context
client: &AuthedClient,
@@ -771,7 +751,7 @@ pub async fn run_agent(
let db_clone = db.clone();
let message_content = response_content.clone();
let step_name = get_step_name_from_flow(
- summary,
+ flow_value,
job.flow_step_id.as_deref(),
);
@@ -934,43 +914,6 @@ pub async fn run_agent(
);
payload
}
- FlowModuleValue::FlowScript {
- id,
- language,
- custom_concurrency_key,
- concurrent_limit,
- concurrency_time_window_s,
- tag,
- ..
- } => {
- let path = format!(
- "{}/tools/{}",
- job.runnable_path(),
- tool.module.id
- );
-
- let payload = JobPayloadWithTag {
- payload: JobPayload::FlowScript {
- id,
- language,
- custom_concurrency_key: custom_concurrency_key
- .clone(),
- concurrent_limit,
- concurrency_time_window_s,
- cache_ttl: tool.module.cache_ttl.map(|x| x as i32),
- dedicated_worker: None,
- path,
- },
- tag: tag.clone(),
- delete_after_use: tool
- .module
- .delete_after_use
- .unwrap_or(false),
- timeout: None,
- on_behalf_of: None,
- };
- payload
- }
_ => {
return Err(Error::internal_err(format!(
"Unsupported tool: {}",
@@ -1178,7 +1121,7 @@ pub async fn run_agent(
let tool_job_id = job_id;
let db_clone = db.clone();
let step_name = get_step_name_from_flow(
- summary,
+ flow_value,
job.flow_step_id.as_deref(),
);
@@ -1285,7 +1228,7 @@ pub async fn run_agent(
let db_clone = db.clone();
let tool_name = tool_call.function.name.clone();
let step_name = get_step_name_from_flow(
- summary,
+ flow_value,
job.flow_step_id.as_deref(),
);
let content = if success {
diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs
index 7a7f6cb52a..ad5b40ee4a 100644
--- a/backend/windmill-worker/src/worker_flow.rs
+++ b/backend/windmill-worker/src/worker_flow.rs
@@ -3701,9 +3701,9 @@ async fn compute_next_flow_transform(
NextStatus::NextStep,
))
}
- FlowModuleValue::AIAgent { modules_node, .. } => {
+ FlowModuleValue::AIAgent { .. } => {
let path = get_path(flow_job, status, module);
- let payload = JobPayload::AIAgent { path, flow_node_id: modules_node };
+ let payload = JobPayload::AIAgent { path };
Ok(NextFlowTransform::Continue(
ContinuePayload::SingleJob(JobPayloadWithTag {
payload,
diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs
index 621012aa9a..59f0f87552 100644
--- a/backend/windmill-worker/src/worker_lockfiles.rs
+++ b/backend/windmill-worker/src/worker_lockfiles.rs
@@ -16,7 +16,7 @@ use uuid::Uuid;
use windmill_common::assets::{clear_asset_usage, insert_asset_usage, AssetUsageKind};
use windmill_common::error::Error;
use windmill_common::error::Result;
-use windmill_common::flows::{FlowModule, FlowModuleValue, FlowNodeFlow, FlowNodeId};
+use windmill_common::flows::{FlowModule, FlowModuleValue, FlowNodeId};
use windmill_common::get_latest_deployed_hash_for_path;
use windmill_common::jobs::JobPayload;
use windmill_common::scripts::{hash_script, NewScript, ScriptHash};
@@ -1635,10 +1635,8 @@ async fn insert_flow_modules<'c>(
workspace_id: &str,
failure_module: Option<&Box>,
same_worker: bool,
- summary: Option,
modules: &mut Vec,
modules_node: &mut Option,
- force_insert: bool,
) -> Result> {
tx = Box::pin(reduce_flow(
tx,
@@ -1649,22 +1647,9 @@ async fn insert_flow_modules<'c>(
same_worker,
))
.await?;
- if !force_insert
- && (modules.is_empty() || crate::worker_flow::is_simple_modules(modules, failure_module))
- {
+ if modules.is_empty() || crate::worker_flow::is_simple_modules(modules, failure_module) {
return Ok(tx);
}
-
- let flow_node_flow = FlowNodeFlow {
- value: FlowValue {
- modules: std::mem::take(modules),
- failure_module: failure_module.cloned(),
- same_worker,
- ..Default::default()
- },
- summary,
- };
-
let id;
(tx, id) = insert_flow_node(
tx,
@@ -1672,7 +1657,12 @@ async fn insert_flow_modules<'c>(
workspace_id,
None,
None,
- Some(&Json(to_raw_value(&flow_node_flow))),
+ Some(&Json(to_raw_value(&FlowValue {
+ modules: std::mem::take(modules),
+ failure_module: failure_module.cloned(),
+ same_worker,
+ ..Default::default()
+ }))),
None,
)
.await?;
@@ -1749,10 +1739,8 @@ async fn reduce_flow<'c>(
workspace_id,
failure_module,
same_worker,
- None,
modules,
modules_node,
- false,
)
.await?;
}
@@ -1764,10 +1752,8 @@ async fn reduce_flow<'c>(
workspace_id,
failure_module,
same_worker,
- None,
&mut branch.modules,
&mut branch.modules_node,
- false,
)
.await?;
}
@@ -1777,10 +1763,8 @@ async fn reduce_flow<'c>(
workspace_id,
failure_module,
same_worker,
- None,
default,
default_node,
- false,
)
.await?;
}
@@ -1792,28 +1776,12 @@ async fn reduce_flow<'c>(
workspace_id,
failure_module,
same_worker,
- None,
&mut branch.modules,
&mut branch.modules_node,
- false,
)
.await?;
}
}
- AIAgent { tools, modules_node, .. } => {
- tx = insert_flow_modules(
- tx,
- path,
- workspace_id,
- failure_module,
- same_worker,
- module.summary.clone(), // we only include summary for ai agents modules
- tools,
- modules_node,
- true,
- )
- .await?;
- }
_ => {}
}
module.value = to_raw_value(&val);
From a2387505544a04675a7c9fddf2ed8c042f8bfa42 Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Thu, 16 Oct 2025 14:17:42 +0000
Subject: [PATCH 12/33] fix: fix job loader in public apps with jwt token
---
.../src/routes/public/[workspace]/[...secret]/+page.svelte | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/frontend/src/routes/public/[workspace]/[...secret]/+page.svelte b/frontend/src/routes/public/[workspace]/[...secret]/+page.svelte
index bdad54f27a..df72207624 100644
--- a/frontend/src/routes/public/[workspace]/[...secret]/+page.svelte
+++ b/frontend/src/routes/public/[workspace]/[...secret]/+page.svelte
@@ -66,7 +66,9 @@
async function loadUser() {
if (parsedSecret.jwt) {
- OpenAPI.TOKEN = 'jwt_ext_' + parsedSecret.jwt
+ const token = 'jwt_ext_' + parsedSecret.jwt
+ OpenAPI.TOKEN = token
+ setContext<{ token?: string }>('AuthToken', { token })
jwtError = false
}
try {
From 25e7a2ea544ab69a2f184704c6ef42fcb0623891 Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Thu, 16 Oct 2025 14:41:12 +0000
Subject: [PATCH 13/33] Is Password nit
---
frontend/src/lib/components/StringTypeNarrowing.svelte | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/frontend/src/lib/components/StringTypeNarrowing.svelte b/frontend/src/lib/components/StringTypeNarrowing.svelte
index c2cc77dc41..a4936dcf97 100644
--- a/frontend/src/lib/components/StringTypeNarrowing.svelte
+++ b/frontend/src/lib/components/StringTypeNarrowing.svelte
@@ -375,10 +375,14 @@
/>
{/if}
- {#if kind == 'none' || kind == 'pattern'}
+ {#if kind == 'none' || kind == 'pattern' || kind == 'format'}
{
if (e.detail) {
From 0fe81f5b98904e9400c35646750df5702d6d8ebe Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Thu, 16 Oct 2025 15:07:59 +0000
Subject: [PATCH 14/33] chore(main): release 1.561.0 (#6838)
* chore(main): release 1.561.0
* Apply automatic changes
---------
Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com>
---
CHANGELOG.md | 13 ++++
backend/Cargo.lock | 68 +++++++++----------
backend/Cargo.toml | 4 +-
backend/windmill-api/openapi.yaml | 2 +-
benchmarks/lib.ts | 2 +-
cli/src/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, 64 insertions(+), 51 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bcac5dfdfa..30c724f781 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
# Changelog
+## [1.561.0](https://github.com/windmill-labs/windmill/compare/v1.560.0...v1.561.0) (2025-10-16)
+
+
+### Features
+
+* ansible playbook execution git repo mode (repo viewer + UI utils) ([#6831](https://github.com/windmill-labs/windmill/issues/6831)) ([32fae7a](https://github.com/windmill-labs/windmill/commit/32fae7a10c769473c708970e18c1f8268d62183f))
+
+
+### Bug Fixes
+
+* **backend:** revert flow node opti for ai agents ([#6840](https://github.com/windmill-labs/windmill/issues/6840)) ([3b5c962](https://github.com/windmill-labs/windmill/commit/3b5c96247350b70fe947d204e9bff61f81be219c))
+* fix job loader in public apps with jwt token ([a238750](https://github.com/windmill-labs/windmill/commit/a2387505544a04675a7c9fddf2ed8c042f8bfa42))
+
## [1.560.0](https://github.com/windmill-labs/windmill/compare/v1.559.0...v1.560.0) (2025-10-15)
diff --git a/backend/Cargo.lock b/backend/Cargo.lock
index 50b1a4a4f1..730e4d390f 100644
--- a/backend/Cargo.lock
+++ b/backend/Cargo.lock
@@ -4831,9 +4831,9 @@ dependencies = [
[[package]]
name = "dyn-stack-macros"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00140340c29b813fdf6ff2237c4407405baefc72cacbe8f1e2277a75b90e5d30"
+checksum = "e1d926b4d407d372f141f93bb444696142c29d32962ccbd3531117cf3aa0bfa9"
[[package]]
name = "dynasm"
@@ -5943,9 +5943,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "globset"
-version = "0.4.16"
+version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
+checksum = "eab69130804d941f8075cfd713bf8848a2c3b3f201a9457a11e6f87e1ab62305"
dependencies = [
"aho-corasick",
"bstr",
@@ -6911,9 +6911,9 @@ checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb"
[[package]]
name = "ignore"
-version = "0.4.23"
+version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
+checksum = "81776e6f9464432afcc28d03e52eb101c93b6f0566f52aef2427663e700f0403"
dependencies = [
"crossbeam-deque",
"globset",
@@ -15122,7 +15122,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windmill"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"aws-sdk-config",
@@ -15182,7 +15182,7 @@ dependencies = [
[[package]]
name = "windmill-api"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"argon2",
@@ -15302,7 +15302,7 @@ dependencies = [
[[package]]
name = "windmill-api-client"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"base64 0.22.1",
"chrono",
@@ -15317,7 +15317,7 @@ dependencies = [
[[package]]
name = "windmill-audit"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"chrono",
"serde",
@@ -15330,7 +15330,7 @@ dependencies = [
[[package]]
name = "windmill-autoscaling"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"axum",
@@ -15349,7 +15349,7 @@ dependencies = [
[[package]]
name = "windmill-common"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -15433,7 +15433,7 @@ dependencies = [
[[package]]
name = "windmill-git-sync"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"regex",
"serde",
@@ -15448,7 +15448,7 @@ dependencies = [
[[package]]
name = "windmill-indexer"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"bytes",
@@ -15472,7 +15472,7 @@ dependencies = [
[[package]]
name = "windmill-macros"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"itertools 0.14.0",
"lazy_static",
@@ -15484,7 +15484,7 @@ dependencies = [
[[package]]
name = "windmill-parser"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"convert_case 0.6.0",
"serde",
@@ -15493,7 +15493,7 @@ dependencies = [
[[package]]
name = "windmill-parser-bash"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15505,7 +15505,7 @@ dependencies = [
[[package]]
name = "windmill-parser-csharp"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"serde_json",
@@ -15517,7 +15517,7 @@ dependencies = [
[[package]]
name = "windmill-parser-go"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"gosyn",
@@ -15529,7 +15529,7 @@ dependencies = [
[[package]]
name = "windmill-parser-graphql"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15541,7 +15541,7 @@ dependencies = [
[[package]]
name = "windmill-parser-java"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"serde_json",
@@ -15553,7 +15553,7 @@ dependencies = [
[[package]]
name = "windmill-parser-nu"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"nu-parser",
@@ -15564,7 +15564,7 @@ dependencies = [
[[package]]
name = "windmill-parser-php"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -15575,7 +15575,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"itertools 0.14.0",
@@ -15587,7 +15587,7 @@ dependencies = [
[[package]]
name = "windmill-parser-py-imports"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -15610,7 +15610,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ruby"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15624,7 +15624,7 @@ dependencies = [
[[package]]
name = "windmill-parser-rust"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"convert_case 0.6.0",
@@ -15641,7 +15641,7 @@ dependencies = [
[[package]]
name = "windmill-parser-sql"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15655,7 +15655,7 @@ dependencies = [
[[package]]
name = "windmill-parser-ts"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"lazy_static",
@@ -15673,7 +15673,7 @@ dependencies = [
[[package]]
name = "windmill-parser-wasm"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"getrandom 0.2.16",
@@ -15698,7 +15698,7 @@ dependencies = [
[[package]]
name = "windmill-parser-yaml"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"serde",
@@ -15709,7 +15709,7 @@ dependencies = [
[[package]]
name = "windmill-queue"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"async-recursion",
@@ -15742,7 +15742,7 @@ dependencies = [
[[package]]
name = "windmill-sql-datatype-parser-wasm"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"wasm-bindgen",
"wasm-bindgen-test",
@@ -15752,7 +15752,7 @@ dependencies = [
[[package]]
name = "windmill-worker"
-version = "1.560.0"
+version = "1.561.0"
dependencies = [
"anyhow",
"async-once-cell",
diff --git a/backend/Cargo.toml b/backend/Cargo.toml
index 04a3464307..855287e63c 100644
--- a/backend/Cargo.toml
+++ b/backend/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "windmill"
-version = "1.560.0"
+version = "1.561.0"
authors.workspace = true
edition.workspace = true
@@ -34,7 +34,7 @@ members = [
exclude = ["./windmill-duckdb-ffi-internal"]
[workspace.package]
-version = "1.560.0"
+version = "1.561.0"
authors = ["Ruben Fiszel "]
edition = "2021"
diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml
index 37c46fff9a..ea799547a1 100644
--- a/backend/windmill-api/openapi.yaml
+++ b/backend/windmill-api/openapi.yaml
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
- version: 1.560.0
+ version: 1.561.0
title: Windmill API
contact:
diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts
index 71f1371085..1ed1ed65e4 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.560.0";
+export const VERSION = "v1.561.0";
export async function login(email: string, password: string): Promise {
return await windmill.UserService.login({
diff --git a/cli/src/main.ts b/cli/src/main.ts
index 0aab1e0045..5b0f7493eb 100644
--- a/cli/src/main.ts
+++ b/cli/src/main.ts
@@ -68,7 +68,7 @@ export {
// }
// });
-export const VERSION = "1.560.0";
+export const VERSION = "1.561.0";
export const WM_FORK_PREFIX = "wm-fork";
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 3910920c3f..4f95518603 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "windmill-components",
- "version": "1.560.0",
+ "version": "1.561.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "windmill-components",
- "version": "1.560.0",
+ "version": "1.561.0",
"hasInstallScript": true,
"license": "AGPL-3.0",
"dependencies": {
diff --git a/frontend/package.json b/frontend/package.json
index e90f3672c2..3fda798c10 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "windmill-components",
- "version": "1.560.0",
+ "version": "1.561.0",
"scripts": {
"dev": "vite dev",
"build": "vite build",
diff --git a/lsp/Pipfile b/lsp/Pipfile
index c970c8e1c6..c736768087 100644
--- a/lsp/Pipfile
+++ b/lsp/Pipfile
@@ -4,8 +4,8 @@ verify_ssl = true
name = "pypi"
[packages]
-wmill = ">=1.560.0"
-wmill_pg = ">=1.560.0"
+wmill = ">=1.561.0"
+wmill_pg = ">=1.561.0"
sendgrid = "*"
mysql-connector-python = "*"
pymongo = "*"
diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml
index 54d0a7d450..ff3fca7d07 100644
--- a/openflow.openapi.yaml
+++ b/openflow.openapi.yaml
@@ -1,7 +1,7 @@
openapi: "3.0.3"
info:
- version: 1.560.0
+ version: 1.561.0
title: OpenFlow Spec
contact:
name: Ruben Fiszel
diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1
index ace7c69295..b698f03b85 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.560.0'
+ ModuleVersion = '1.561.0'
# Supported PSEditions
# CompatiblePSEditions = @()
diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml
index d867810f7d..b02878072d 100644
--- a/python-client/wmill/pyproject.toml
+++ b/python-client/wmill/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "wmill"
-version = "1.560.0"
+version = "1.561.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 1a905f263b..807e5d7ab9 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.560.0"
+version = "1.561.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 70372d564c..8aec478d53 100644
--- a/typescript-client/jsr.json
+++ b/typescript-client/jsr.json
@@ -1,6 +1,6 @@
{
"name": "@windmill/windmill",
- "version": "1.560.0",
+ "version": "1.561.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 6e99cab791..76fd97760f 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.560.0",
+ "version": "1.561.0",
"author": "Ruben Fiszel",
"license": "Apache 2.0",
"devDependencies": {
diff --git a/version.txt b/version.txt
index 3d75838ea8..cb4c46e580 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-1.560.0
+1.561.0
From f723a1fb7227ae45661fea5cf2e6f9928a39672b Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Thu, 16 Oct 2025 15:29:49 +0000
Subject: [PATCH 15/33] fix: add configurable timeout sse stream
---
backend/windmill-api/src/jobs.rs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs
index 52e3b23f06..47c3260fc1 100644
--- a/backend/windmill-api/src/jobs.rs
+++ b/backend/windmill-api/src/jobs.rs
@@ -6889,6 +6889,12 @@ enum JobUpdateSSEStream {
Ping,
}
+lazy_static::lazy_static! {
+ pub static ref TIMEOUT_SSE_STREAM: u64 =
+ std::env::var("TIMEOUT_SSE_STREAM").unwrap_or("60".to_string()).parse::().unwrap_or(60);
+}
+
+
fn start_job_update_sse_stream(
opt_authed: Option,
opt_tokened: OptTokened,
@@ -7021,7 +7027,7 @@ fn start_job_update_sse_stream(
last_ping = Instant::now();
}
- if start.elapsed().as_secs() > 30 {
+ if start.elapsed().as_secs() > *TIMEOUT_SSE_STREAM {
if tx.send(JobUpdateSSEStream::Timeout).await.is_err() {
tracing::warn!("Failed to send job timeout for job {job_id}");
}
From d16cc56f58819af2aa4304fa8a7c29e82ad1399e Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Thu, 16 Oct 2025 16:24:36 +0000
Subject: [PATCH 16/33] nit compile
---
backend/windmill-worker/src/ansible_executor.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/backend/windmill-worker/src/ansible_executor.rs b/backend/windmill-worker/src/ansible_executor.rs
index bcd058836e..395341d698 100644
--- a/backend/windmill-worker/src/ansible_executor.rs
+++ b/backend/windmill-worker/src/ansible_executor.rs
@@ -951,9 +951,11 @@ pub async fn handle_ansible_job(
let mut secret_url = git_repo_resource.get("url").and_then(|s| s.as_str()).map(|s| s.to_string())
.ok_or(anyhow!("Failed to get url from git repo resource, please check that the resource has the correct type (git_repository)"))?;
+ #[cfg(feature = "enterprise")]
let is_github_app = git_repo_resource.get("is_github_app").and_then(|s| s.as_bool())
.ok_or(anyhow!("Failed to get `is_github_app` field from git repo resource, please check that the resource has the correct type (git_repository)"))?;
+ #[cfg(feature = "enterprise")]
if is_github_app {
if let Connection::Sql(db) = conn {
let token = get_github_app_token_internal(db, &client.token).await?;
From 0c1fbc6f0d1f9ef21c11ad271edb7a16b200c1ae Mon Sep 17 00:00:00 2001
From: Ruben Fiszel
Date: Thu, 16 Oct 2025 16:28:04 +0000
Subject: [PATCH 17/33] fix compile
---
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 253d116503..85cd80d864 100644
--- a/backend/ee-repo-ref.txt
+++ b/backend/ee-repo-ref.txt
@@ -1 +1 @@
-0f18d7155059dccebbe690162b52133d526e6fb5
\ No newline at end of file
+656c0418f9959154738e3583ade5e2a80c4a4daa
\ No newline at end of file
From defb6c9694ac294dbf19ba5cd42ce7399ad1b9ac Mon Sep 17 00:00:00 2001
From: Pyra <92104930+pyranota@users.noreply.github.com>
Date: Thu, 16 Oct 2025 18:49:35 +0200
Subject: [PATCH 18/33] feat: dependency job debouncing (#6769)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* v0
Signed-off-by: pyranota
* optimize relocks
* make it work with relative relative imports
Signed-off-by: pyranota
* use fallback
Signed-off-by: pyranota
* remove dbg and todos
Signed-off-by: pyranota
* future proof a bit
Signed-off-by: pyranota
* cleanup
Signed-off-by: pyranota
* more cleanup
Signed-off-by: pyranota
* remove final TODO
Signed-off-by: pyranota
* do not use bytemuck
Signed-off-by: pyranota
* optimize hashing
Signed-off-by: pyranota
* implementation 1
Signed-off-by: pyranota
* almost v0
Signed-off-by: pyranota
* v0
Signed-off-by: pyranota
* add comments and use fallback
Signed-off-by: pyranota
* call dissolve for apps
Signed-off-by: pyranota
* add comms
Signed-off-by: pyranota
* refactor v0 (partially tested + dirty)
Signed-off-by: pyranota
* finishing
Signed-off-by: pyranota
* remove TODO
Signed-off-by: pyranota
* Update SQLx metadata
* silence unused argument
Signed-off-by: pyranota
* cleanup
Signed-off-by: pyranota
* implement rebuild_map endpoint
Signed-off-by: pyranota
* update windmill api client
Signed-off-by: pyranota
* almost finish with tests
Signed-off-by: pyranota
* add proper testing
Signed-off-by: pyranota
* remove unused fixtures
Signed-off-by: pyranota
* Update SQLx metadata
* partial cleanup
Signed-off-by: pyranota
* Update backend/windmill-worker/src/scoped_dependency_map.rs
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
* Update backend/windmill-common/src/scripts.rs
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
* evil doings
Signed-off-by: pyranota
* more cleanup
* Update SQLx metadata
* more cleanup
Signed-off-by: pyranota
* fixing CI
Signed-off-by: pyranota
* remove python from default features
Signed-off-by: pyranota
* feat: dependency job debouncing
* checkpoint
Signed-off-by: pyranota
* more improvements
Signed-off-by: pyranota
* refactor: clean up dependency job debouncing implementation
- Add comprehensive comments explaining the debouncing mechanism
- Replace debug statements (dbg!) with proper tracing calls
- Extract helper functions to reduce code duplication:
- extract_to_relock_from_args() for extracting nodes/components
- accumulate_debounce_stale_data() for updating stale data
- Improve code readability and maintainability
Co-authored-by: Pyra
* cleanup
Signed-off-by: pyranota
* Update SQLx metadata
* Update backend/windmill-common/src/jobs.rs
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
* Update backend/windmill-common/src/jobs.rs
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
* Update backend/windmill-queue/src/jobs.rs
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
* better error handling for helper
Signed-off-by: pyranota
* update ee-ref
Signed-off-by: pyranota
* test: add race condition test for dependency job debouncing
- Implement test_2 for scripts to test the race condition edge case
- Add comprehensive documentation comments to all test functions
- Remove empty test_2 stubs for flows (not needed)
- Keep test_2 stub for apps with TODO comment
The race condition test simulates the scenario where a job is marked
as running but debounce_key hasn't been cleaned up yet, forcing the
system to create a new job while reusing the existing debounce_key.
This edge case can occur due to the lack of transactions in the pull
function for performance reasons (see jobs.rs:4415-4425).
Co-authored-by: Pyra
* test: implement race condition test for dependency job debouncing
- Add comprehensive test_2 for script module that tests the race condition edge case
- Remove empty test_2 stubs from flows and apps modules
- Fix unused variable warning in worker_lockfiles.rs
- Add detailed comments explaining the race condition scenario and test logic
Co-authored-by: Pyra
* implement fallback
Signed-off-by: pyranota
* make it mostly work
* all tests are almost working
Signed-off-by: pyranota
* add comments a bit
Signed-off-by: pyranota
* Update backend/windmill-common/src/scripts.rs
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
* Update backend/windmill-worker/src/worker_lockfiles.rs
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
* Update backend/windmill-worker/src/worker_lockfiles.rs
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
* feat: improve debouncing documentation, tracing, and error handling
- Add comprehensive 3-phase debouncing documentation explaining push/accumulation/pull
- Enhance all tracing with structured logging (job_id, workspace_id, node_count, etc.)
- Add proper error handling with .map_err() and contextual messages
- Replace dbg!() with proper tracing::debug!()
- Replace todo!() with proper error handling
- Fix typos: 'and edge case' → 'an edge case', 'bc' → 'because'
- Fix debug variable name: 'debounce_job_id_0' → 'debounce_job_id_o'
- Add documentation for debounce cleanup and stale data retrieval
- Add trace-level logging for non-error paths to reduce noise
Co-authored-by: Pyra
* do some work for future improvements
Signed-off-by: pyranota
* fix tests
Signed-off-by: pyranota
* clippy
Signed-off-by: pyranota
* update sqlx
Signed-off-by: pyranota
* clippy
Signed-off-by: pyranota
* update ee ref
Signed-off-by: pyranota
* flag tests behind the feature, add timeout
Signed-off-by: pyranota
* fix timeout + cleanup
Signed-off-by: pyranota
* cleanup
Signed-off-by: pyranota
* row lock debounce_key
Signed-off-by: pyranota
* addressing TODOs
Signed-off-by: pyranota
* fix test
Signed-off-by: pyranota
* ee ref
Signed-off-by: pyranota
* ee repo
Signed-off-by: pyranota
---------
Signed-off-by: pyranota
Co-authored-by: Pyra <92104930+pyranye@users.noreply.github.com>
Co-authored-by: GitHub Action
Co-authored-by: windmill-internal-app[bot]
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Pyra
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel
---
...d7d1a2e10342bbbc7f8486df0b73f5657a493.json | 20 +
...71dcc58cb037a59afe08cd1372b51791b4165.json | 20 +
...54334eb7375b483e8ffd80364c60c3aad04b4.json | 20 +
...dc18a4db630616b3aa80c27b54e4bb4e20f30.json | 20 +
...9f0d1ecabb750d3d508f5c6a5cb3eaf3cd209.json | 20 +
...3b465b4bbf83bc86c0efacd6133bb432ee13d.json | 20 +
...2fd7cb4a7be6dc50c9f7507eaa15f138484dc.json | 20 +
...df8567579218d5c4332b01e169732c836ebdc.json | 20 +
...dafc044d1861048857ec2fc70f929ab358373.json | 20 +
...583902596c4faed6d83e668be85aba8eb644a.json | 20 +
...8f8ca36db091c67cf9ec5e7cec40486532ab9.json | 20 +
...a626245abe58c306392a76710252d16d0bd44.json | 20 +
...46fed29a76e579e72d6c8539cd0de73b424db.json | 20 +
...73fc77421710992b10f526aa36bc64ff0930.json} | 5 +-
...a0fd360da8a9f06e0ab59d9e724851ced4247.json | 20 +
...88f9d694bc37caacef7269e3738de8b5f6013.json | 22 +
...dc17b17372b7292e39d3888a93ff4fe49e4f5.json | 14 +
...476226c66fe42c9f992bd4c6c232a68ade2bd.json | 20 +
...f5d7df9acf74de6dcc566924de02f1807af2.json} | 8 +-
...bed6cae29f361167deee879b8b683ad1bf684.json | 15 +
...4f9e3e416e1baf20bdb25c9a8ac9efc288931.json | 15 +
...a4fc58818c109deda6678c79474bf45428966.json | 22 +
...23c85a6e6490c2d9e1dc6ac39112d763f0f75.json | 23 +
...467dcabc2c3367fb33592e5608bd985c9e436.json | 26 +
...423b891b57e9a65a02b52966377c1960ab89e.json | 20 +
...0b45df00a1ae022b3c29730af2347bd76deef.json | 20 +
...3e0822cb91bde2324327f1828236112d278b1.json | 20 +
...98a5e50bc98a8360387bd4158f9bc1289319f.json | 24 -
...f231912db86c05c881aff1ffb5460f9711390.json | 20 +
...0f6929f5dcbf29f3938e40c7f93d98aa7f49c.json | 20 +
...f178ba8a1b8c1e01e0fec1dfd6a54ea7a894.json} | 4 +-
...7694378b2be15f3a40e7e6690f31157bdb5af.json | 20 +
...3eff389e0d44b7a58b69fd5c79629599902fc.json | 22 +
...4d21d38edb827d2c08b0ca7d72311b78d574.json} | 8 +-
...24d3b06170d3a02a06a4c209e49e5ef175916.json | 20 +
...4d0ab538043fff345a0438a40708c4066771b.json | 20 +
...928e1be4696120c5d78c1649d5e430a5dae4c.json | 20 +
...a0854975ad4c3f6fe24557b87a197485dff39.json | 20 +
...f97f2301599a39668fcd20e1642fd828e3eec.json | 23 +
...2680159db11c8aeef82056ec30b498f8129da.json | 20 +
...768ab79733e33bf8f9110a4f4d75a3c07da67.json | 20 +
...39ae72faf0c8fe097e6ad6d309aee9a8aede2.json | 24 +
...5fe3964efb8551ad6d74f141fc457034cc5b9.json | 22 +
backend/Cargo.toml | 1 +
backend/ee-repo-ref.txt | 2 +-
.../20250925142554_job_debouncing.down.sql | 3 +
.../20250925142554_job_debouncing.up.sql | 19 +
backend/src/monitor.rs | 2 +
backend/tests/common/mod.rs | 60 +-
backend/tests/fixtures/djob_debouncing.sql | 242 ++
backend/tests/job_payload.rs | 1 +
backend/tests/relative_imports.rs | 1981 ++++++++++++++++-
backend/tests/worker.rs | 21 +-
backend/windmill-api/src/apps.rs | 13 +-
backend/windmill-api/src/flows.rs | 15 +-
backend/windmill-api/src/jobs.rs | 15 +
backend/windmill-api/src/scripts.rs | 10 +
.../src/triggers/trigger_helpers.rs | 1 +
backend/windmill-common/src/error.rs | 6 +
backend/windmill-common/src/jobs.rs | 48 +-
backend/windmill-common/src/scripts.rs | 103 +-
backend/windmill-queue/src/jobs.rs | 595 ++++-
backend/windmill-queue/src/schedule.rs | 57 +-
backend/windmill-worker/src/ai_executor.rs | 1 +
backend/windmill-worker/src/worker.rs | 60 +-
backend/windmill-worker/src/worker_flow.rs | 1 +
.../windmill-worker/src/worker_lockfiles.rs | 526 ++---
67 files changed, 4061 insertions(+), 539 deletions(-)
create mode 100644 backend/.sqlx/query-04ce5c530c80ae6f911dfe0dc9ed7d1a2e10342bbbc7f8486df0b73f5657a493.json
create mode 100644 backend/.sqlx/query-08e5832c4002a0970d4105bb80371dcc58cb037a59afe08cd1372b51791b4165.json
create mode 100644 backend/.sqlx/query-1c047fef05e8cfc07aef7ea9a5454334eb7375b483e8ffd80364c60c3aad04b4.json
create mode 100644 backend/.sqlx/query-1c739fddea33331fdb5acbfe614dc18a4db630616b3aa80c27b54e4bb4e20f30.json
create mode 100644 backend/.sqlx/query-3528fbb71569b9bfd2a66d0692f9f0d1ecabb750d3d508f5c6a5cb3eaf3cd209.json
create mode 100644 backend/.sqlx/query-3af3c1080cde18cfbeb08e290f63b465b4bbf83bc86c0efacd6133bb432ee13d.json
create mode 100644 backend/.sqlx/query-421c7e0388326889b33fae4c8fa2fd7cb4a7be6dc50c9f7507eaa15f138484dc.json
create mode 100644 backend/.sqlx/query-44d3e7dce67967471fa638f2beedf8567579218d5c4332b01e169732c836ebdc.json
create mode 100644 backend/.sqlx/query-47a55edc0f5c54ac9f5e48665c8dafc044d1861048857ec2fc70f929ab358373.json
create mode 100644 backend/.sqlx/query-53b236c65e8790b7474e919daa0583902596c4faed6d83e668be85aba8eb644a.json
create mode 100644 backend/.sqlx/query-5848d416e2d96eb20c7417dec608f8ca36db091c67cf9ec5e7cec40486532ab9.json
create mode 100644 backend/.sqlx/query-5bedbfe98b3cacd1c6f4b2344a9a626245abe58c306392a76710252d16d0bd44.json
create mode 100644 backend/.sqlx/query-6641d63a691d712bc24be9c972646fed29a76e579e72d6c8539cd0de73b424db.json
rename backend/.sqlx/{query-b5fbd7893950610f1285662df24f438c9855ba860e23befd88c2544ef86e9133.json => query-69550451b86f221a3d2ef626be7073fc77421710992b10f526aa36bc64ff0930.json} (60%)
create mode 100644 backend/.sqlx/query-720d7f0d258d52a6b89e2f4b32ea0fd360da8a9f06e0ab59d9e724851ced4247.json
create mode 100644 backend/.sqlx/query-73fcf81d272c1613e094d60c0a088f9d694bc37caacef7269e3738de8b5f6013.json
create mode 100644 backend/.sqlx/query-76774e6f72c8c8b7473487e4176dc17b17372b7292e39d3888a93ff4fe49e4f5.json
create mode 100644 backend/.sqlx/query-77f33de1d95e38a44968ea7f026476226c66fe42c9f992bd4c6c232a68ade2bd.json
rename backend/.sqlx/{query-526bfaccaafbe2e6f70dd5e6cd21c0c60d4ec155f79d067a8b74cf24eebad88c.json => query-7bfb3b210d23f2c00a1d6a653e9df5d7df9acf74de6dcc566924de02f1807af2.json} (52%)
create mode 100644 backend/.sqlx/query-7ec724b84479c2f737637e91b8cbed6cae29f361167deee879b8b683ad1bf684.json
create mode 100644 backend/.sqlx/query-7ed404f3a8b23f98fb7c15a26b14f9e3e416e1baf20bdb25c9a8ac9efc288931.json
create mode 100644 backend/.sqlx/query-91a13927f7e0577e52755fd8463a4fc58818c109deda6678c79474bf45428966.json
create mode 100644 backend/.sqlx/query-99e11c04bcc436ec7a75f46365423c85a6e6490c2d9e1dc6ac39112d763f0f75.json
create mode 100644 backend/.sqlx/query-9b37ec5aa9b979393c6e5c8d98f467dcabc2c3367fb33592e5608bd985c9e436.json
create mode 100644 backend/.sqlx/query-9d1bd189940d345f27e4850651c423b891b57e9a65a02b52966377c1960ab89e.json
create mode 100644 backend/.sqlx/query-a14270c6a2af936539c7d7e4a950b45df00a1ae022b3c29730af2347bd76deef.json
create mode 100644 backend/.sqlx/query-a206326b6c13c88b773adcd1f7b3e0822cb91bde2324327f1828236112d278b1.json
delete mode 100644 backend/.sqlx/query-bf252ea52aeb57664ad5054b39998a5e50bc98a8360387bd4158f9bc1289319f.json
create mode 100644 backend/.sqlx/query-bfc96c870fe0c50ebb9e9bc5ccff231912db86c05c881aff1ffb5460f9711390.json
create mode 100644 backend/.sqlx/query-c42326e2121b79d1381a3c91ef90f6929f5dcbf29f3938e40c7f93d98aa7f49c.json
rename backend/.sqlx/{query-9f4811fe735d401b62f4b7bf3db2b5cd13eb3364a8a8546007dec7ab528b1f9d.json => query-c6ef0acdf20bd71dd26de981fb49f178ba8a1b8c1e01e0fec1dfd6a54ea7a894.json} (50%)
create mode 100644 backend/.sqlx/query-cbc8e3e22862a76e1a8386cca247694378b2be15f3a40e7e6690f31157bdb5af.json
create mode 100644 backend/.sqlx/query-cfe06702916362aaf5122bb95593eff389e0d44b7a58b69fd5c79629599902fc.json
rename backend/.sqlx/{query-ca15fe5d43f0e94f50408efe5c9e359770b759e8661687b4503c4b692ecd245e.json => query-d5661c7557cf3a8dee7cf799cd364d21d38edb827d2c08b0ca7d72311b78d574.json} (50%)
create mode 100644 backend/.sqlx/query-d6a060b255f02a2a776a6a3ac4024d3b06170d3a02a06a4c209e49e5ef175916.json
create mode 100644 backend/.sqlx/query-db7a756d541dbedbdeb23ab8f914d0ab538043fff345a0438a40708c4066771b.json
create mode 100644 backend/.sqlx/query-e49b72cf5a05b47f76ebcdc5306928e1be4696120c5d78c1649d5e430a5dae4c.json
create mode 100644 backend/.sqlx/query-e7bc612ccdbb2532a321ed83e26a0854975ad4c3f6fe24557b87a197485dff39.json
create mode 100644 backend/.sqlx/query-eaeeb4708a6d9bf6be3265dcaaaf97f2301599a39668fcd20e1642fd828e3eec.json
create mode 100644 backend/.sqlx/query-ee96e97f1a8bd2ac592665ad3e02680159db11c8aeef82056ec30b498f8129da.json
create mode 100644 backend/.sqlx/query-eff9926fa211497ddbc53866444768ab79733e33bf8f9110a4f4d75a3c07da67.json
create mode 100644 backend/.sqlx/query-f0efa383f2025158de160577ad839ae72faf0c8fe097e6ad6d309aee9a8aede2.json
create mode 100644 backend/.sqlx/query-fe1539db7384c8edc6d8ec672495fe3964efb8551ad6d74f141fc457034cc5b9.json
create mode 100644 backend/migrations/20250925142554_job_debouncing.down.sql
create mode 100644 backend/migrations/20250925142554_job_debouncing.up.sql
create mode 100644 backend/tests/fixtures/djob_debouncing.sql
diff --git a/backend/.sqlx/query-04ce5c530c80ae6f911dfe0dc9ed7d1a2e10342bbbc7f8486df0b73f5657a493.json b/backend/.sqlx/query-04ce5c530c80ae6f911dfe0dc9ed7d1a2e10342bbbc7f8486df0b73f5657a493.json
new file mode 100644
index 0000000000..2878e54920
--- /dev/null
+++ b/backend/.sqlx/query-04ce5c530c80ae6f911dfe0dc9ed7d1a2e10342bbbc7f8486df0b73f5657a493.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) FROM v2_job",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "04ce5c530c80ae6f911dfe0dc9ed7d1a2e10342bbbc7f8486df0b73f5657a493"
+}
diff --git a/backend/.sqlx/query-08e5832c4002a0970d4105bb80371dcc58cb037a59afe08cd1372b51791b4165.json b/backend/.sqlx/query-08e5832c4002a0970d4105bb80371dcc58cb037a59afe08cd1372b51791b4165.json
new file mode 100644
index 0000000000..2fdaa1a63d
--- /dev/null
+++ b/backend/.sqlx/query-08e5832c4002a0970d4105bb80371dcc58cb037a59afe08cd1372b51791b4165.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) FROM v2_job_completed",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "08e5832c4002a0970d4105bb80371dcc58cb037a59afe08cd1372b51791b4165"
+}
diff --git a/backend/.sqlx/query-1c047fef05e8cfc07aef7ea9a5454334eb7375b483e8ffd80364c60c3aad04b4.json b/backend/.sqlx/query-1c047fef05e8cfc07aef7ea9a5454334eb7375b483e8ffd80364c60c3aad04b4.json
new file mode 100644
index 0000000000..7162b07bef
--- /dev/null
+++ b/backend/.sqlx/query-1c047fef05e8cfc07aef7ea9a5454334eb7375b483e8ffd80364c60c3aad04b4.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) FROM app_version WHERE app_id = '2'",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "1c047fef05e8cfc07aef7ea9a5454334eb7375b483e8ffd80364c60c3aad04b4"
+}
diff --git a/backend/.sqlx/query-1c739fddea33331fdb5acbfe614dc18a4db630616b3aa80c27b54e4bb4e20f30.json b/backend/.sqlx/query-1c739fddea33331fdb5acbfe614dc18a4db630616b3aa80c27b54e4bb4e20f30.json
new file mode 100644
index 0000000000..3d444ae09b
--- /dev/null
+++ b/backend/.sqlx/query-1c739fddea33331fdb5acbfe614dc18a4db630616b3aa80c27b54e4bb4e20f30.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT runnable_id FROM v2_job ORDER BY created_at DESC",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "runnable_id",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "1c739fddea33331fdb5acbfe614dc18a4db630616b3aa80c27b54e4bb4e20f30"
+}
diff --git a/backend/.sqlx/query-3528fbb71569b9bfd2a66d0692f9f0d1ecabb750d3d508f5c6a5cb3eaf3cd209.json b/backend/.sqlx/query-3528fbb71569b9bfd2a66d0692f9f0d1ecabb750d3d508f5c6a5cb3eaf3cd209.json
new file mode 100644
index 0000000000..fbeff75f56
--- /dev/null
+++ b/backend/.sqlx/query-3528fbb71569b9bfd2a66d0692f9f0d1ecabb750d3d508f5c6a5cb3eaf3cd209.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT versions FROM flow WHERE path = 'f/dre/flow'",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "versions",
+ "type_info": "Int8Array"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "3528fbb71569b9bfd2a66d0692f9f0d1ecabb750d3d508f5c6a5cb3eaf3cd209"
+}
diff --git a/backend/.sqlx/query-3af3c1080cde18cfbeb08e290f63b465b4bbf83bc86c0efacd6133bb432ee13d.json b/backend/.sqlx/query-3af3c1080cde18cfbeb08e290f63b465b4bbf83bc86c0efacd6133bb432ee13d.json
new file mode 100644
index 0000000000..623fab4416
--- /dev/null
+++ b/backend/.sqlx/query-3af3c1080cde18cfbeb08e290f63b465b4bbf83bc86c0efacd6133bb432ee13d.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT (scheduled_for - created_at) FROM v2_job_queue WHERE running = false",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "?column?",
+ "type_info": "Interval"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "3af3c1080cde18cfbeb08e290f63b465b4bbf83bc86c0efacd6133bb432ee13d"
+}
diff --git a/backend/.sqlx/query-421c7e0388326889b33fae4c8fa2fd7cb4a7be6dc50c9f7507eaa15f138484dc.json b/backend/.sqlx/query-421c7e0388326889b33fae4c8fa2fd7cb4a7be6dc50c9f7507eaa15f138484dc.json
new file mode 100644
index 0000000000..8d828b265a
--- /dev/null
+++ b/backend/.sqlx/query-421c7e0388326889b33fae4c8fa2fd7cb4a7be6dc50c9f7507eaa15f138484dc.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) FROM flow_version WHERE path = 'f/dre/flow'",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "421c7e0388326889b33fae4c8fa2fd7cb4a7be6dc50c9f7507eaa15f138484dc"
+}
diff --git a/backend/.sqlx/query-44d3e7dce67967471fa638f2beedf8567579218d5c4332b01e169732c836ebdc.json b/backend/.sqlx/query-44d3e7dce67967471fa638f2beedf8567579218d5c4332b01e169732c836ebdc.json
new file mode 100644
index 0000000000..41c08565b0
--- /dev/null
+++ b/backend/.sqlx/query-44d3e7dce67967471fa638f2beedf8567579218d5c4332b01e169732c836ebdc.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT hash FROM script WHERE path = 'f/dre_script/script' AND archived = false",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "hash",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "44d3e7dce67967471fa638f2beedf8567579218d5c4332b01e169732c836ebdc"
+}
diff --git a/backend/.sqlx/query-47a55edc0f5c54ac9f5e48665c8dafc044d1861048857ec2fc70f929ab358373.json b/backend/.sqlx/query-47a55edc0f5c54ac9f5e48665c8dafc044d1861048857ec2fc70f929ab358373.json
new file mode 100644
index 0000000000..7876a6f7f0
--- /dev/null
+++ b/backend/.sqlx/query-47a55edc0f5c54ac9f5e48665c8dafc044d1861048857ec2fc70f929ab358373.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT id FROM v2_job_queue WHERE running = false",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Uuid"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "47a55edc0f5c54ac9f5e48665c8dafc044d1861048857ec2fc70f929ab358373"
+}
diff --git a/backend/.sqlx/query-53b236c65e8790b7474e919daa0583902596c4faed6d83e668be85aba8eb644a.json b/backend/.sqlx/query-53b236c65e8790b7474e919daa0583902596c4faed6d83e668be85aba8eb644a.json
new file mode 100644
index 0000000000..d567e90159
--- /dev/null
+++ b/backend/.sqlx/query-53b236c65e8790b7474e919daa0583902596c4faed6d83e668be85aba8eb644a.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) from debounce_key",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "53b236c65e8790b7474e919daa0583902596c4faed6d83e668be85aba8eb644a"
+}
diff --git a/backend/.sqlx/query-5848d416e2d96eb20c7417dec608f8ca36db091c67cf9ec5e7cec40486532ab9.json b/backend/.sqlx/query-5848d416e2d96eb20c7417dec608f8ca36db091c67cf9ec5e7cec40486532ab9.json
new file mode 100644
index 0000000000..ae9532cdee
--- /dev/null
+++ b/backend/.sqlx/query-5848d416e2d96eb20c7417dec608f8ca36db091c67cf9ec5e7cec40486532ab9.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) FROM v2_job_queue WHERE running = false",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "5848d416e2d96eb20c7417dec608f8ca36db091c67cf9ec5e7cec40486532ab9"
+}
diff --git a/backend/.sqlx/query-5bedbfe98b3cacd1c6f4b2344a9a626245abe58c306392a76710252d16d0bd44.json b/backend/.sqlx/query-5bedbfe98b3cacd1c6f4b2344a9a626245abe58c306392a76710252d16d0bd44.json
new file mode 100644
index 0000000000..a7733f5403
--- /dev/null
+++ b/backend/.sqlx/query-5bedbfe98b3cacd1c6f4b2344a9a626245abe58c306392a76710252d16d0bd44.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT versions FROM app WHERE path = 'f/dre_app/app'",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "versions",
+ "type_info": "Int8Array"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "5bedbfe98b3cacd1c6f4b2344a9a626245abe58c306392a76710252d16d0bd44"
+}
diff --git a/backend/.sqlx/query-6641d63a691d712bc24be9c972646fed29a76e579e72d6c8539cd0de73b424db.json b/backend/.sqlx/query-6641d63a691d712bc24be9c972646fed29a76e579e72d6c8539cd0de73b424db.json
new file mode 100644
index 0000000000..a193aa5b98
--- /dev/null
+++ b/backend/.sqlx/query-6641d63a691d712bc24be9c972646fed29a76e579e72d6c8539cd0de73b424db.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) FROM debounce_stale_data",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "6641d63a691d712bc24be9c972646fed29a76e579e72d6c8539cd0de73b424db"
+}
diff --git a/backend/.sqlx/query-b5fbd7893950610f1285662df24f438c9855ba860e23befd88c2544ef86e9133.json b/backend/.sqlx/query-69550451b86f221a3d2ef626be7073fc77421710992b10f526aa36bc64ff0930.json
similarity index 60%
rename from backend/.sqlx/query-b5fbd7893950610f1285662df24f438c9855ba860e23befd88c2544ef86e9133.json
rename to backend/.sqlx/query-69550451b86f221a3d2ef626be7073fc77421710992b10f526aa36bc64ff0930.json
index 766748fa60..caf6213fa2 100644
--- a/backend/.sqlx/query-b5fbd7893950610f1285662df24f438c9855ba860e23befd88c2544ef86e9133.json
+++ b/backend/.sqlx/query-69550451b86f221a3d2ef626be7073fc77421710992b10f526aa36bc64ff0930.json
@@ -1,17 +1,16 @@
{
"db_name": "PostgreSQL",
- "query": "\n INSERT INTO script\n (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets)\n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, $4, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets\n\n FROM script WHERE hash = $2 AND workspace_id = $3;\n ",
+ "query": "\n INSERT INTO script\n (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets)\n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets\n\n FROM script WHERE hash = $2 AND workspace_id = $3;\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Int8",
"Int8",
- "Text",
"Text"
]
},
"nullable": []
},
- "hash": "b5fbd7893950610f1285662df24f438c9855ba860e23befd88c2544ef86e9133"
+ "hash": "69550451b86f221a3d2ef626be7073fc77421710992b10f526aa36bc64ff0930"
}
diff --git a/backend/.sqlx/query-720d7f0d258d52a6b89e2f4b32ea0fd360da8a9f06e0ab59d9e724851ced4247.json b/backend/.sqlx/query-720d7f0d258d52a6b89e2f4b32ea0fd360da8a9f06e0ab59d9e724851ced4247.json
new file mode 100644
index 0000000000..8239f32a08
--- /dev/null
+++ b/backend/.sqlx/query-720d7f0d258d52a6b89e2f4b32ea0fd360da8a9f06e0ab59d9e724851ced4247.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) from debounce_stale_data",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "720d7f0d258d52a6b89e2f4b32ea0fd360da8a9f06e0ab59d9e724851ced4247"
+}
diff --git a/backend/.sqlx/query-73fcf81d272c1613e094d60c0a088f9d694bc37caacef7269e3738de8b5f6013.json b/backend/.sqlx/query-73fcf81d272c1613e094d60c0a088f9d694bc37caacef7269e3738de8b5f6013.json
new file mode 100644
index 0000000000..00f7ec50d2
--- /dev/null
+++ b/backend/.sqlx/query-73fcf81d272c1613e094d60c0a088f9d694bc37caacef7269e3738de8b5f6013.json
@@ -0,0 +1,22 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT key FROM debounce_key WHERE job_id = $1",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "key",
+ "type_info": "Varchar"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Uuid"
+ ]
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "73fcf81d272c1613e094d60c0a088f9d694bc37caacef7269e3738de8b5f6013"
+}
diff --git a/backend/.sqlx/query-76774e6f72c8c8b7473487e4176dc17b17372b7292e39d3888a93ff4fe49e4f5.json b/backend/.sqlx/query-76774e6f72c8c8b7473487e4176dc17b17372b7292e39d3888a93ff4fe49e4f5.json
new file mode 100644
index 0000000000..694ed1887f
--- /dev/null
+++ b/backend/.sqlx/query-76774e6f72c8c8b7473487e4176dc17b17372b7292e39d3888a93ff4fe49e4f5.json
@@ -0,0 +1,14 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "DELETE FROM debounce_key WHERE key = $1",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Text"
+ ]
+ },
+ "nullable": []
+ },
+ "hash": "76774e6f72c8c8b7473487e4176dc17b17372b7292e39d3888a93ff4fe49e4f5"
+}
diff --git a/backend/.sqlx/query-77f33de1d95e38a44968ea7f026476226c66fe42c9f992bd4c6c232a68ade2bd.json b/backend/.sqlx/query-77f33de1d95e38a44968ea7f026476226c66fe42c9f992bd4c6c232a68ade2bd.json
new file mode 100644
index 0000000000..6d3b24293f
--- /dev/null
+++ b/backend/.sqlx/query-77f33de1d95e38a44968ea7f026476226c66fe42c9f992bd4c6c232a68ade2bd.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT hash FROM script WHERE path = 'f/dre_script/script' AND archived = true",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "hash",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "77f33de1d95e38a44968ea7f026476226c66fe42c9f992bd4c6c232a68ade2bd"
+}
diff --git a/backend/.sqlx/query-526bfaccaafbe2e6f70dd5e6cd21c0c60d4ec155f79d067a8b74cf24eebad88c.json b/backend/.sqlx/query-7bfb3b210d23f2c00a1d6a653e9df5d7df9acf74de6dcc566924de02f1807af2.json
similarity index 52%
rename from backend/.sqlx/query-526bfaccaafbe2e6f70dd5e6cd21c0c60d4ec155f79d067a8b74cf24eebad88c.json
rename to backend/.sqlx/query-7bfb3b210d23f2c00a1d6a653e9df5d7df9acf74de6dcc566924de02f1807af2.json
index 08e38953ab..19ec28bbe3 100644
--- a/backend/.sqlx/query-526bfaccaafbe2e6f70dd5e6cd21c0c60d4ec155f79d067a8b74cf24eebad88c.json
+++ b/backend/.sqlx/query-7bfb3b210d23f2c00a1d6a653e9df5d7df9acf74de6dcc566924de02f1807af2.json
@@ -1,11 +1,11 @@
{
"db_name": "PostgreSQL",
- "query": "SELECT versions[array_upper(versions, 1)] FROM flow WHERE path = $1 AND workspace_id = $2",
+ "query": "SELECT id FROM flow_version WHERE path = $1 AND workspace_id = $2 ORDER BY created_at DESC LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
- "name": "versions",
+ "name": "id",
"type_info": "Int8"
}
],
@@ -16,8 +16,8 @@
]
},
"nullable": [
- null
+ false
]
},
- "hash": "526bfaccaafbe2e6f70dd5e6cd21c0c60d4ec155f79d067a8b74cf24eebad88c"
+ "hash": "7bfb3b210d23f2c00a1d6a653e9df5d7df9acf74de6dcc566924de02f1807af2"
}
diff --git a/backend/.sqlx/query-7ec724b84479c2f737637e91b8cbed6cae29f361167deee879b8b683ad1bf684.json b/backend/.sqlx/query-7ec724b84479c2f737637e91b8cbed6cae29f361167deee879b8b683ad1bf684.json
new file mode 100644
index 0000000000..a979af6114
--- /dev/null
+++ b/backend/.sqlx/query-7ec724b84479c2f737637e91b8cbed6cae29f361167deee879b8b683ad1bf684.json
@@ -0,0 +1,15 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "\n INSERT INTO debounce_stale_data (job_id, to_relock)\n VALUES ($1, $2)\n ON CONFLICT (job_id)\n DO UPDATE SET to_relock = (\n SELECT array_agg(DISTINCT x)\n FROM unnest(\n -- Combine existing array with new values, removing duplicates\n array_cat(debounce_stale_data.to_relock, EXCLUDED.to_relock)\n ) AS x\n )\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Uuid",
+ "TextArray"
+ ]
+ },
+ "nullable": []
+ },
+ "hash": "7ec724b84479c2f737637e91b8cbed6cae29f361167deee879b8b683ad1bf684"
+}
diff --git a/backend/.sqlx/query-7ed404f3a8b23f98fb7c15a26b14f9e3e416e1baf20bdb25c9a8ac9efc288931.json b/backend/.sqlx/query-7ed404f3a8b23f98fb7c15a26b14f9e3e416e1baf20bdb25c9a8ac9efc288931.json
new file mode 100644
index 0000000000..9216f73a7e
--- /dev/null
+++ b/backend/.sqlx/query-7ed404f3a8b23f98fb7c15a26b14f9e3e416e1baf20bdb25c9a8ac9efc288931.json
@@ -0,0 +1,15 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "INSERT INTO debounce_key (key, job_id) VALUES ($1, $2)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Left": [
+ "Varchar",
+ "Uuid"
+ ]
+ },
+ "nullable": []
+ },
+ "hash": "7ed404f3a8b23f98fb7c15a26b14f9e3e416e1baf20bdb25c9a8ac9efc288931"
+}
diff --git a/backend/.sqlx/query-91a13927f7e0577e52755fd8463a4fc58818c109deda6678c79474bf45428966.json b/backend/.sqlx/query-91a13927f7e0577e52755fd8463a4fc58818c109deda6678c79474bf45428966.json
new file mode 100644
index 0000000000..d41b7811ee
--- /dev/null
+++ b/backend/.sqlx/query-91a13927f7e0577e52755fd8463a4fc58818c109deda6678c79474bf45428966.json
@@ -0,0 +1,22 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT running FROM v2_job_queue WHERE id = $1",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "running",
+ "type_info": "Bool"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Uuid"
+ ]
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "91a13927f7e0577e52755fd8463a4fc58818c109deda6678c79474bf45428966"
+}
diff --git a/backend/.sqlx/query-99e11c04bcc436ec7a75f46365423c85a6e6490c2d9e1dc6ac39112d763f0f75.json b/backend/.sqlx/query-99e11c04bcc436ec7a75f46365423c85a6e6490c2d9e1dc6ac39112d763f0f75.json
new file mode 100644
index 0000000000..6fb4205e32
--- /dev/null
+++ b/backend/.sqlx/query-99e11c04bcc436ec7a75f46365423c85a6e6490c2d9e1dc6ac39112d763f0f75.json
@@ -0,0 +1,23 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT id FROM app_version WHERE app_id = (SELECT id FROM app WHERE path = $1 AND workspace_id = $2) ORDER BY created_at DESC LIMIT 1",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text",
+ "Text"
+ ]
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "99e11c04bcc436ec7a75f46365423c85a6e6490c2d9e1dc6ac39112d763f0f75"
+}
diff --git a/backend/.sqlx/query-9b37ec5aa9b979393c6e5c8d98f467dcabc2c3367fb33592e5608bd985c9e436.json b/backend/.sqlx/query-9b37ec5aa9b979393c6e5c8d98f467dcabc2c3367fb33592e5608bd985c9e436.json
new file mode 100644
index 0000000000..751c25d839
--- /dev/null
+++ b/backend/.sqlx/query-9b37ec5aa9b979393c6e5c8d98f467dcabc2c3367fb33592e5608bd985c9e436.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT\n dsd.to_relock,\n dk.key\n FROM debounce_key dk\n JOIN debounce_stale_data dsd ON dk.job_id = dsd.job_id",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "to_relock",
+ "type_info": "TextArray"
+ },
+ {
+ "ordinal": 1,
+ "name": "key",
+ "type_info": "Varchar"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ true,
+ false
+ ]
+ },
+ "hash": "9b37ec5aa9b979393c6e5c8d98f467dcabc2c3367fb33592e5608bd985c9e436"
+}
diff --git a/backend/.sqlx/query-9d1bd189940d345f27e4850651c423b891b57e9a65a02b52966377c1960ab89e.json b/backend/.sqlx/query-9d1bd189940d345f27e4850651c423b891b57e9a65a02b52966377c1960ab89e.json
new file mode 100644
index 0000000000..5b0998c259
--- /dev/null
+++ b/backend/.sqlx/query-9d1bd189940d345f27e4850651c423b891b57e9a65a02b52966377c1960ab89e.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT versions[1] FROM flow WHERE path = 'f/dre/flow'",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "versions",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "9d1bd189940d345f27e4850651c423b891b57e9a65a02b52966377c1960ab89e"
+}
diff --git a/backend/.sqlx/query-a14270c6a2af936539c7d7e4a950b45df00a1ae022b3c29730af2347bd76deef.json b/backend/.sqlx/query-a14270c6a2af936539c7d7e4a950b45df00a1ae022b3c29730af2347bd76deef.json
new file mode 100644
index 0000000000..3df9d93ef0
--- /dev/null
+++ b/backend/.sqlx/query-a14270c6a2af936539c7d7e4a950b45df00a1ae022b3c29730af2347bd76deef.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT id FROM v2_job_completed",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Uuid"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "a14270c6a2af936539c7d7e4a950b45df00a1ae022b3c29730af2347bd76deef"
+}
diff --git a/backend/.sqlx/query-a206326b6c13c88b773adcd1f7b3e0822cb91bde2324327f1828236112d278b1.json b/backend/.sqlx/query-a206326b6c13c88b773adcd1f7b3e0822cb91bde2324327f1828236112d278b1.json
new file mode 100644
index 0000000000..cdcab1c509
--- /dev/null
+++ b/backend/.sqlx/query-a206326b6c13c88b773adcd1f7b3e0822cb91bde2324327f1828236112d278b1.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT (scheduled_for - created_at) FROM v2_job_queue",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "?column?",
+ "type_info": "Interval"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "a206326b6c13c88b773adcd1f7b3e0822cb91bde2324327f1828236112d278b1"
+}
diff --git a/backend/.sqlx/query-bf252ea52aeb57664ad5054b39998a5e50bc98a8360387bd4158f9bc1289319f.json b/backend/.sqlx/query-bf252ea52aeb57664ad5054b39998a5e50bc98a8360387bd4158f9bc1289319f.json
deleted file mode 100644
index 97929c71e3..0000000000
--- a/backend/.sqlx/query-bf252ea52aeb57664ad5054b39998a5e50bc98a8360387bd4158f9bc1289319f.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "db_name": "PostgreSQL",
- "query": "INSERT INTO flow_version\n (workspace_id, path, value, schema, created_by)\n\n SELECT workspace_id, path, value, schema, created_by\n FROM flow_version WHERE path = $1 AND workspace_id = $2 AND id = $3\n\n RETURNING id",
- "describe": {
- "columns": [
- {
- "ordinal": 0,
- "name": "id",
- "type_info": "Int8"
- }
- ],
- "parameters": {
- "Left": [
- "Text",
- "Text",
- "Int8"
- ]
- },
- "nullable": [
- false
- ]
- },
- "hash": "bf252ea52aeb57664ad5054b39998a5e50bc98a8360387bd4158f9bc1289319f"
-}
diff --git a/backend/.sqlx/query-bfc96c870fe0c50ebb9e9bc5ccff231912db86c05c881aff1ffb5460f9711390.json b/backend/.sqlx/query-bfc96c870fe0c50ebb9e9bc5ccff231912db86c05c881aff1ffb5460f9711390.json
new file mode 100644
index 0000000000..bfd7cd16bd
--- /dev/null
+++ b/backend/.sqlx/query-bfc96c870fe0c50ebb9e9bc5ccff231912db86c05c881aff1ffb5460f9711390.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT parent_hashes FROM script WHERE path = 'f/dre_script/script' AND archived = false",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "parent_hashes",
+ "type_info": "Int8Array"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "bfc96c870fe0c50ebb9e9bc5ccff231912db86c05c881aff1ffb5460f9711390"
+}
diff --git a/backend/.sqlx/query-c42326e2121b79d1381a3c91ef90f6929f5dcbf29f3938e40c7f93d98aa7f49c.json b/backend/.sqlx/query-c42326e2121b79d1381a3c91ef90f6929f5dcbf29f3938e40c7f93d98aa7f49c.json
new file mode 100644
index 0000000000..ddc6263aff
--- /dev/null
+++ b/backend/.sqlx/query-c42326e2121b79d1381a3c91ef90f6929f5dcbf29f3938e40c7f93d98aa7f49c.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT runnable_path FROM v2_job",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "runnable_path",
+ "type_info": "Varchar"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "c42326e2121b79d1381a3c91ef90f6929f5dcbf29f3938e40c7f93d98aa7f49c"
+}
diff --git a/backend/.sqlx/query-9f4811fe735d401b62f4b7bf3db2b5cd13eb3364a8a8546007dec7ab528b1f9d.json b/backend/.sqlx/query-c6ef0acdf20bd71dd26de981fb49f178ba8a1b8c1e01e0fec1dfd6a54ea7a894.json
similarity index 50%
rename from backend/.sqlx/query-9f4811fe735d401b62f4b7bf3db2b5cd13eb3364a8a8546007dec7ab528b1f9d.json
rename to backend/.sqlx/query-c6ef0acdf20bd71dd26de981fb49f178ba8a1b8c1e01e0fec1dfd6a54ea7a894.json
index 4c9693ce27..0bd82635da 100644
--- a/backend/.sqlx/query-9f4811fe735d401b62f4b7bf3db2b5cd13eb3364a8a8546007dec7ab528b1f9d.json
+++ b/backend/.sqlx/query-c6ef0acdf20bd71dd26de981fb49f178ba8a1b8c1e01e0fec1dfd6a54ea7a894.json
@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
- "query": "\nWITH rows_to_delete AS (\n SELECT concurrency_id\n FROM concurrency_counter\n WHERE job_uuids = '{}'::jsonb\n FOR UPDATE SKIP LOCKED\n)\nDELETE FROM concurrency_counter\nWHERE concurrency_id IN (SELECT concurrency_id FROM rows_to_delete) RETURNING concurrency_id",
+ "query": "\nWITH rows_to_delete AS (\n SELECT concurrency_id\n FROM concurrency_counter\n \n WHERE job_uuids = '{}'::jsonb\n FOR UPDATE SKIP LOCKED\n)\nDELETE FROM concurrency_counter\nWHERE concurrency_id IN (SELECT concurrency_id FROM rows_to_delete) RETURNING concurrency_id",
"describe": {
"columns": [
{
@@ -16,5 +16,5 @@
false
]
},
- "hash": "9f4811fe735d401b62f4b7bf3db2b5cd13eb3364a8a8546007dec7ab528b1f9d"
+ "hash": "c6ef0acdf20bd71dd26de981fb49f178ba8a1b8c1e01e0fec1dfd6a54ea7a894"
}
diff --git a/backend/.sqlx/query-cbc8e3e22862a76e1a8386cca247694378b2be15f3a40e7e6690f31157bdb5af.json b/backend/.sqlx/query-cbc8e3e22862a76e1a8386cca247694378b2be15f3a40e7e6690f31157bdb5af.json
new file mode 100644
index 0000000000..4c4921e2a0
--- /dev/null
+++ b/backend/.sqlx/query-cbc8e3e22862a76e1a8386cca247694378b2be15f3a40e7e6690f31157bdb5af.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT key FROM debounce_key",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "key",
+ "type_info": "Varchar"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "cbc8e3e22862a76e1a8386cca247694378b2be15f3a40e7e6690f31157bdb5af"
+}
diff --git a/backend/.sqlx/query-cfe06702916362aaf5122bb95593eff389e0d44b7a58b69fd5c79629599902fc.json b/backend/.sqlx/query-cfe06702916362aaf5122bb95593eff389e0d44b7a58b69fd5c79629599902fc.json
new file mode 100644
index 0000000000..e9cedd467d
--- /dev/null
+++ b/backend/.sqlx/query-cfe06702916362aaf5122bb95593eff389e0d44b7a58b69fd5c79629599902fc.json
@@ -0,0 +1,22 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "DELETE FROM debounce_stale_data WHERE job_id = $1 RETURNING to_relock",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "to_relock",
+ "type_info": "TextArray"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Uuid"
+ ]
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "cfe06702916362aaf5122bb95593eff389e0d44b7a58b69fd5c79629599902fc"
+}
diff --git a/backend/.sqlx/query-ca15fe5d43f0e94f50408efe5c9e359770b759e8661687b4503c4b692ecd245e.json b/backend/.sqlx/query-d5661c7557cf3a8dee7cf799cd364d21d38edb827d2c08b0ca7d72311b78d574.json
similarity index 50%
rename from backend/.sqlx/query-ca15fe5d43f0e94f50408efe5c9e359770b759e8661687b4503c4b692ecd245e.json
rename to backend/.sqlx/query-d5661c7557cf3a8dee7cf799cd364d21d38edb827d2c08b0ca7d72311b78d574.json
index 6812bfdfc7..907b140fdd 100644
--- a/backend/.sqlx/query-ca15fe5d43f0e94f50408efe5c9e359770b759e8661687b4503c4b692ecd245e.json
+++ b/backend/.sqlx/query-d5661c7557cf3a8dee7cf799cd364d21d38edb827d2c08b0ca7d72311b78d574.json
@@ -1,11 +1,11 @@
{
"db_name": "PostgreSQL",
- "query": "SELECT versions[array_upper(versions, 1)] FROM app WHERE path = $1 AND workspace_id = $2",
+ "query": "SELECT hash FROM script WHERE path = $1 AND workspace_id = $2 AND deleted = false ORDER BY created_at DESC LIMIT 1",
"describe": {
"columns": [
{
"ordinal": 0,
- "name": "versions",
+ "name": "hash",
"type_info": "Int8"
}
],
@@ -16,8 +16,8 @@
]
},
"nullable": [
- null
+ false
]
},
- "hash": "ca15fe5d43f0e94f50408efe5c9e359770b759e8661687b4503c4b692ecd245e"
+ "hash": "d5661c7557cf3a8dee7cf799cd364d21d38edb827d2c08b0ca7d72311b78d574"
}
diff --git a/backend/.sqlx/query-d6a060b255f02a2a776a6a3ac4024d3b06170d3a02a06a4c209e49e5ef175916.json b/backend/.sqlx/query-d6a060b255f02a2a776a6a3ac4024d3b06170d3a02a06a4c209e49e5ef175916.json
new file mode 100644
index 0000000000..b69e04115f
--- /dev/null
+++ b/backend/.sqlx/query-d6a060b255f02a2a776a6a3ac4024d3b06170d3a02a06a4c209e49e5ef175916.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT lock FROM script WHERE path = 'f/dre_script/script'",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "lock",
+ "type_info": "Text"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "d6a060b255f02a2a776a6a3ac4024d3b06170d3a02a06a4c209e49e5ef175916"
+}
diff --git a/backend/.sqlx/query-db7a756d541dbedbdeb23ab8f914d0ab538043fff345a0438a40708c4066771b.json b/backend/.sqlx/query-db7a756d541dbedbdeb23ab8f914d0ab538043fff345a0438a40708c4066771b.json
new file mode 100644
index 0000000000..99c0aa49ad
--- /dev/null
+++ b/backend/.sqlx/query-db7a756d541dbedbdeb23ab8f914d0ab538043fff345a0438a40708c4066771b.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT versions[2] FROM flow WHERE path = 'f/dre/flow'",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "versions",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "db7a756d541dbedbdeb23ab8f914d0ab538043fff345a0438a40708c4066771b"
+}
diff --git a/backend/.sqlx/query-e49b72cf5a05b47f76ebcdc5306928e1be4696120c5d78c1649d5e430a5dae4c.json b/backend/.sqlx/query-e49b72cf5a05b47f76ebcdc5306928e1be4696120c5d78c1649d5e430a5dae4c.json
new file mode 100644
index 0000000000..cd4130b542
--- /dev/null
+++ b/backend/.sqlx/query-e49b72cf5a05b47f76ebcdc5306928e1be4696120c5d78c1649d5e430a5dae4c.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT jsonb_array_elements(value->'modules')->'value'->>'lock' AS lock FROM flow",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "lock",
+ "type_info": "Text"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "e49b72cf5a05b47f76ebcdc5306928e1be4696120c5d78c1649d5e430a5dae4c"
+}
diff --git a/backend/.sqlx/query-e7bc612ccdbb2532a321ed83e26a0854975ad4c3f6fe24557b87a197485dff39.json b/backend/.sqlx/query-e7bc612ccdbb2532a321ed83e26a0854975ad4c3f6fe24557b87a197485dff39.json
new file mode 100644
index 0000000000..8e8a5c98e9
--- /dev/null
+++ b/backend/.sqlx/query-e7bc612ccdbb2532a321ed83e26a0854975ad4c3f6fe24557b87a197485dff39.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) FROM v2_job_queue",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "e7bc612ccdbb2532a321ed83e26a0854975ad4c3f6fe24557b87a197485dff39"
+}
diff --git a/backend/.sqlx/query-eaeeb4708a6d9bf6be3265dcaaaf97f2301599a39668fcd20e1642fd828e3eec.json b/backend/.sqlx/query-eaeeb4708a6d9bf6be3265dcaaaf97f2301599a39668fcd20e1642fd828e3eec.json
new file mode 100644
index 0000000000..e866bd4001
--- /dev/null
+++ b/backend/.sqlx/query-eaeeb4708a6d9bf6be3265dcaaaf97f2301599a39668fcd20e1642fd828e3eec.json
@@ -0,0 +1,23 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "\nSELECT\n j1.completed_at < j2.started_at\nFROM\n v2_job_completed j1,\n v2_job_completed j2\nWHERE\n j1.id = $1 \n AND j2.id = $2",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "?column?",
+ "type_info": "Bool"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Uuid",
+ "Uuid"
+ ]
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "eaeeb4708a6d9bf6be3265dcaaaf97f2301599a39668fcd20e1642fd828e3eec"
+}
diff --git a/backend/.sqlx/query-ee96e97f1a8bd2ac592665ad3e02680159db11c8aeef82056ec30b498f8129da.json b/backend/.sqlx/query-ee96e97f1a8bd2ac592665ad3e02680159db11c8aeef82056ec30b498f8129da.json
new file mode 100644
index 0000000000..0e1f9620e0
--- /dev/null
+++ b/backend/.sqlx/query-ee96e97f1a8bd2ac592665ad3e02680159db11c8aeef82056ec30b498f8129da.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) from v2_job_queue",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "ee96e97f1a8bd2ac592665ad3e02680159db11c8aeef82056ec30b498f8129da"
+}
diff --git a/backend/.sqlx/query-eff9926fa211497ddbc53866444768ab79733e33bf8f9110a4f4d75a3c07da67.json b/backend/.sqlx/query-eff9926fa211497ddbc53866444768ab79733e33bf8f9110a4f4d75a3c07da67.json
new file mode 100644
index 0000000000..6a48df6e2b
--- /dev/null
+++ b/backend/.sqlx/query-eff9926fa211497ddbc53866444768ab79733e33bf8f9110a4f4d75a3c07da67.json
@@ -0,0 +1,20 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT COUNT(*) FROM debounce_key",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "count",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "eff9926fa211497ddbc53866444768ab79733e33bf8f9110a4f4d75a3c07da67"
+}
diff --git a/backend/.sqlx/query-f0efa383f2025158de160577ad839ae72faf0c8fe097e6ad6d309aee9a8aede2.json b/backend/.sqlx/query-f0efa383f2025158de160577ad839ae72faf0c8fe097e6ad6d309aee9a8aede2.json
new file mode 100644
index 0000000000..32616f298f
--- /dev/null
+++ b/backend/.sqlx/query-f0efa383f2025158de160577ad839ae72faf0c8fe097e6ad6d309aee9a8aede2.json
@@ -0,0 +1,24 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "INSERT INTO flow_version\n (workspace_id, path, value, schema, created_by)\n\n SELECT workspace_id, path, value, schema, created_by\n FROM flow_version WHERE path = $1 AND workspace_id = $2 AND id = $3\n\n RETURNING id\n ",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Int8"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text",
+ "Text",
+ "Int8"
+ ]
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "f0efa383f2025158de160577ad839ae72faf0c8fe097e6ad6d309aee9a8aede2"
+}
diff --git a/backend/.sqlx/query-fe1539db7384c8edc6d8ec672495fe3964efb8551ad6d74f141fc457034cc5b9.json b/backend/.sqlx/query-fe1539db7384c8edc6d8ec672495fe3964efb8551ad6d74f141fc457034cc5b9.json
new file mode 100644
index 0000000000..7b7e250ef7
--- /dev/null
+++ b/backend/.sqlx/query-fe1539db7384c8edc6d8ec672495fe3964efb8551ad6d74f141fc457034cc5b9.json
@@ -0,0 +1,22 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT job_id FROM debounce_key WHERE key = $1 FOR UPDATE",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "job_id",
+ "type_info": "Uuid"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Text"
+ ]
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "fe1539db7384c8edc6d8ec672495fe3964efb8551ad6d74f141fc457034cc5b9"
+}
diff --git a/backend/Cargo.toml b/backend/Cargo.toml
index 855287e63c..58ecd617dd 100644
--- a/backend/Cargo.toml
+++ b/backend/Cargo.toml
@@ -86,6 +86,7 @@ oauth2 = ["windmill-api/oauth2"]
zip = ["windmill-api/zip"]
static_frontend = ["windmill-api/static_frontend"]
scoped_cache = ["windmill-common/scoped_cache"]
+test_job_debouncing = []
# Languages
python = ["windmill-worker/python", "windmill-api/python"]
rust = ["windmill-worker/rust"]
diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt
index 85cd80d864..bf77fc7312 100644
--- a/backend/ee-repo-ref.txt
+++ b/backend/ee-repo-ref.txt
@@ -1 +1 @@
-656c0418f9959154738e3583ade5e2a80c4a4daa
\ No newline at end of file
+365a3c9dd7a3fddc3280bbda86ba93ef149d1b64
diff --git a/backend/migrations/20250925142554_job_debouncing.down.sql b/backend/migrations/20250925142554_job_debouncing.down.sql
new file mode 100644
index 0000000000..fd1c155f43
--- /dev/null
+++ b/backend/migrations/20250925142554_job_debouncing.down.sql
@@ -0,0 +1,3 @@
+DROP TABLE IF EXISTS debounce_key;
+DROP TABLE IF EXISTS debounce_stale_data;
+DROP TABLE IF EXISTS debounce_obj_latest_version;
diff --git a/backend/migrations/20250925142554_job_debouncing.up.sql b/backend/migrations/20250925142554_job_debouncing.up.sql
new file mode 100644
index 0000000000..0c946aaaf5
--- /dev/null
+++ b/backend/migrations/20250925142554_job_debouncing.up.sql
@@ -0,0 +1,19 @@
+CREATE TABLE debounce_key (
+ key VARCHAR(255) NOT NULL,
+ job_id uuid NOT NULL,
+ PRIMARY KEY (key)
+);
+
+CREATE TABLE debounce_stale_data (
+ job_id uuid NOT NULL,
+ to_relock TEXT[],
+ PRIMARY KEY (job_id)
+);
+
+-- TODO: Prune on move/deletion
+-- But normally this will persist across runs.
+-- CREATE TABLE unlocked_script_latest_version (
+-- key VARCHAR(255) NOT NULL,
+-- version BIGINT NOT NULL,
+-- PRIMARY KEY (key)
+-- );
diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs
index e4d3c380af..3e6ddec903 100644
--- a/backend/src/monitor.rs
+++ b/backend/src/monitor.rs
@@ -1588,6 +1588,7 @@ pub async fn monitor_db(
}
}
};
+
// run every hour (60 minutes / 30 seconds = 120)
let cleanup_worker_group_stats_f = async {
if server_mode && iteration.is_some() && iteration.as_ref().unwrap().should_run(120) {
@@ -2415,6 +2416,7 @@ async fn cleanup_concurrency_counters_empty_keys(db: &DB) -> error::Result<()> {
WITH rows_to_delete AS (
SELECT concurrency_id
FROM concurrency_counter
+
WHERE job_uuids = '{}'::jsonb
FOR UPDATE SKIP LOCKED
)
diff --git a/backend/tests/common/mod.rs b/backend/tests/common/mod.rs
index 026eee12f7..73db6d1f62 100644
--- a/backend/tests/common/mod.rs
+++ b/backend/tests/common/mod.rs
@@ -20,6 +20,17 @@ use windmill_common::{
};
use windmill_queue::PushIsolationLevel;
+pub async fn init_client(db: Pool) -> (windmill_api_client::Client, u16, ApiServer) {
+ initialize_tracing().await;
+ let server = ApiServer::start(db).await.unwrap();
+ let port = server.addr.port();
+ let client = windmill_api_client::create_client(
+ &format!("http://localhost:{port}"),
+ "SECRET_TOKEN".to_string(),
+ );
+ (client, port, server)
+}
+
/// it's important this is unique between tests as there is one prometheus registry and
/// run_worker shouldn't register the same metric with the same worker name more than once.
///
@@ -131,7 +142,7 @@ impl RunJob {
let tx = PushIsolationLevel::IsolatedRoot(db.clone());
let (uuid, tx) = windmill_queue::push(
- &db,
+ db,
tx,
"test-workspace",
payload,
@@ -157,6 +168,7 @@ impl RunJob {
None,
false,
None,
+ None,
)
.await
.expect("push has to succeed");
@@ -170,8 +182,8 @@ impl RunJob {
let uuid = self.push(db).await;
let listener = listen_for_completed_jobs(db).await;
in_test_worker(db, listener.find(&uuid), port).await;
- let r = completed_job(uuid, db).await;
- r
+
+ completed_job(uuid, db).await
}
/// push the job, spawn a worker, wait until the job is in completed_job
@@ -185,8 +197,8 @@ impl RunJob {
let listener = listen_for_completed_jobs(db).await;
test(uuid).await;
in_test_worker(db, listener.find(&uuid), port).await;
- let r = completed_job(uuid, db).await;
- r
+
+ completed_job(uuid, db).await
}
}
@@ -251,10 +263,10 @@ pub fn spawn_test_worker(
let base_internal_url = format!("http://localhost:{}", port);
{
let mut wc = WORKER_CONFIG.write().await;
- (*wc).worker_tags = windmill_common::worker::DEFAULT_TAGS.clone();
- (*wc).priority_tags_sorted = vec![windmill_common::worker::PriorityTags {
+ wc.worker_tags = windmill_common::worker::DEFAULT_TAGS.clone();
+ wc.priority_tags_sorted = vec![windmill_common::worker::PriorityTags {
priority: 0,
- tags: (*wc).worker_tags.clone(),
+ tags: wc.worker_tags.clone(),
}];
windmill_common::worker::store_suspended_pull_query(&wc).await;
windmill_common::worker::store_pull_query(&wc).await;
@@ -349,7 +361,7 @@ fn find_module_in_vec(modules: Vec, id: &str) -> Option () {
+pub async fn set_jwt_secret() {
let secret = "mytestsecret".to_string();
let mut l = JWT_SECRET.write().await;
*l = secret;
@@ -475,10 +487,10 @@ pub async fn assert_lockfile(
.await
.unwrap();
- let mut completed = listen_for_completed_jobs(&db).await;
+ let mut completed = listen_for_completed_jobs(db).await;
let db2 = db.clone();
in_test_worker(
- &db,
+ db,
async move {
completed.next().await; // deployed script
@@ -571,10 +583,10 @@ pub async fn run_deployed_relative_imports(
.await
.unwrap();
- let mut completed = listen_for_completed_jobs(&db).await;
+ let mut completed = listen_for_completed_jobs(db).await;
let db2 = db.clone();
in_test_worker(
- &db,
+ db,
async move {
completed.next().await; // deployed script
@@ -631,10 +643,10 @@ pub async fn run_preview_relative_imports(
let server = ApiServer::start(db.clone()).await?;
let port = server.addr.port();
- let mut completed = listen_for_completed_jobs(&db).await;
+ let mut completed = listen_for_completed_jobs(db).await;
let db2 = db.clone();
in_test_worker(
- &db,
+ db,
async move {
let job = RunJob::from(JobPayload::Code(RawCode {
hash: None,
@@ -671,3 +683,21 @@ pub async fn run_preview_relative_imports(
Ok(())
}
+
+/// IMPORTANT!:
+/// Do not run parallel in tests!
+///
+/// No tests can run this at the same time, will result into conflicts!!!
+pub async fn rebuild_dmap(client: &windmill_api_client::Client) -> bool {
+ client
+ .client()
+ .post(format!(
+ "{}/w/test-workspace/workspaces/rebuild_dependency_map",
+ client.baseurl()
+ ))
+ .send()
+ .await
+ .unwrap()
+ .status()
+ .is_success()
+}
diff --git a/backend/tests/fixtures/djob_debouncing.sql b/backend/tests/fixtures/djob_debouncing.sql
new file mode 100644
index 0000000000..067d46c398
--- /dev/null
+++ b/backend/tests/fixtures/djob_debouncing.sql
@@ -0,0 +1,242 @@
+-- FLOWS --
+INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
+'test-workspace',
+'test-user',
+'#requirements:
+#bottle==0.13.2
+def main():
+ pass
+',
+'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
+'',
+'',
+'f/dre/leaf_left', 333400, 'python3', '');
+-- Padded Hex: 0000000000051658
+
+INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
+'test-workspace',
+'test-user',
+'#requirements:
+#tiny==0.1.3
+def main():
+ pass
+',
+'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
+'',
+'',
+'f/dre/leaf_right', 333403, 'python3', '');
+-- Padded Hex: 000000000005165B
+
+
+INSERT INTO public.flow(workspace_id, summary, description, path, versions, schema, value, edited_by) VALUES (
+'test-workspace',
+'',
+'',
+'f/dre/flow',
+'{1443253234253454}',
+'{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "properties": {},
+ "required": [],
+ "type": "object"
+}',
+$tag$
+{
+ "modules": [
+ {
+ "id": "a",
+ "value": {
+ "lock": "# py: 3.11\n",
+ "type": "rawscript",
+ "assets": [],
+ "content": "import f.dre.leaf_left\n\ndef main():\n pass",
+ "language": "python3",
+ "input_transforms": {}
+ },
+ "summary": "leaf Left"
+ },
+ {
+ "id": "b",
+ "value": {
+ "lock": "# py: 3.11\n",
+ "type": "rawscript",
+ "assets": [],
+ "content": "import f.dre.leaf_right\nimport f.dre.leaf_left\n\ndef main():\n pass",
+ "language": "python3",
+ "input_transforms": {}
+ },
+ "summary": "leaf Left and Right"
+ },
+ {
+ "id": "c",
+ "value": {
+ "lock": "# py: 3.11\n",
+ "type": "rawscript",
+ "assets": [],
+ "content": "import f.dre.leaf_right\n\ndef main():\n pass",
+ "language": "python3",
+ "input_transforms": {}
+ },
+ "summary": "leaf RIght"
+ }
+ ]
+}$tag$,
+'system'
+);
+
+INSERT INTO public.flow_version(id, workspace_id, path, schema, value, created_by) VALUES (
+1443253234253454,
+'test-workspace',
+'f/dre/flow',
+'{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "properties": {},
+ "required": [],
+ "type": "object"
+}',
+$tag$
+{
+ "modules": [
+ {
+ "id": "a",
+ "value": {
+ "lock": "# py: 3.11\n",
+ "type": "rawscript",
+ "assets": [],
+ "content": "import f.dre.leaf_left\n\ndef main():\n pass",
+ "language": "python3",
+ "input_transforms": {}
+ },
+ "summary": "leaf Left"
+ },
+ {
+ "id": "b",
+ "value": {
+ "lock": "# py: 3.11\n",
+ "type": "rawscript",
+ "assets": [],
+ "content": "import f.dre.leaf_right\nimport f.dre.leaf_left\n\ndef main():\n pass",
+ "language": "python3",
+ "input_transforms": {}
+ },
+ "summary": "leaf Left and Right"
+ },
+ {
+ "id": "c",
+ "value": {
+ "lock": "# py: 3.11\n",
+ "type": "rawscript",
+ "assets": [],
+ "content": "import f.dre.leaf_right\n\ndef main():\n pass",
+ "language": "python3",
+ "input_transforms": {}
+ },
+ "summary": "leaf RIght"
+ }
+ ]
+}$tag$,
+'system'
+);
+
+-- APPS --
+INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
+'test-workspace',
+'test-user',
+'#requirements:
+#bottle==0.13.2
+def main():
+ pass
+',
+'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
+'',
+'',
+'f/dre_app/leaf_left', 433400, 'python3', '');
+-- Padded Hex: 0000000000069CF8
+
+INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
+'test-workspace',
+'test-user',
+'#requirements:
+#tiny==0.1.3
+def main():
+ pass
+',
+'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
+'',
+'',
+'f/dre_app/leaf_right', 433403, 'python3', '');
+-- Padded Hex: 0000000000069CFB
+
+INSERT INTO public.app(id, workspace_id, path, versions, policy) VALUES (
+2,
+'test-workspace',
+'f/dre_app/app',
+'{0}',
+'{}'
+);
+
+INSERT INTO public.app_version(id, app_id, value, created_by) VALUES (
+0,
+2,
+$tag${"grid":[{"3":{"fixed":true,"x":0,"y":0,"fullHeight":false,"w":6,"h":2},"12":{"fixed":true,"x":0,"y":0,"fullHeight":false,"w":12,"h":2},"data":{"type":"containercomponent","configuration":{},"customCss":{"container":{"class":"!p-0","style":""}},"numberOfSubgrids":1,"id":"topbar"},"id":"topbar"},{"3":{"fixed":false,"x":0,"y":2,"fullHeight":false,"w":1,"h":1},"12":{"fixed":false,"x":0,"y":2,"fullHeight":false,"w":2,"h":1},"data":{"type":"buttoncomponent","configuration":{"label":{"type":"static","value":"A"},"color":{"type":"static","value":"blue"},"size":{"type":"static","value":"xs"},"fillContainer":{"type":"static","value":false},"disabled":{"type":"static","value":false},"beforeIcon":{"type":"static"},"afterIcon":{"type":"static"},"tooltip":{"type":"static","value":""},"triggerOnAppLoad":{"type":"static","value":false},"runInBackground":{"type":"static","value":false},"onSuccess":{"type":"oneOf","selected":"none","configuration":{"none":{},"gotoUrl":{"url":{"type":"static","value":""},"newTab":{"type":"static","value":true}},"setTab":{"setTab":{"type":"static","value":[]}},"sendToast":{"message":{"type":"static","value":""}},"openModal":{"modalId":{"type":"static","value":""}},"closeModal":{"modalId":{"type":"static","value":""}},"open":{"id":{"type":"static","value":""}},"close":{"id":{"type":"static","value":""}},"clearFiles":{"id":{"type":"static","value":""}}}},"onError":{"type":"oneOf","selected":"errorOverlay","configuration":{"errorOverlay":{},"gotoUrl":{"url":{"type":"static","value":""},"newTab":{"type":"static","value":true}},"setTab":{"setTab":{"type":"static","value":[]}},"sendErrorToast":{"message":{"type":"static","value":"An error occurred"},"appendError":{"type":"static","value":true}},"open":{"id":{"type":"static","value":""}},"close":{"id":{"type":"static","value":""}}}},"confirmationModal":{"type":"oneOf","selected":"none","configuration":{"none":{},"confirmationModal":{"title":{"type":"static","value":"Title"},"description":{"type":"static","value":"Are you sure?"},"confirmationText":{"type":"static","value":"Confirm"}}}}},"componentInput":{"type":"runnable","fieldType":"any","fields":{},"runnable":{"type":"runnableByName","name":"Inline Script","inlineScript":{"content":"import f.dre_app.leaf_left\n\ndef main():\n pass\n","language":"python3","schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"},"path":"f/dre_app/app/Inline_Script"}},"autoRefresh":false,"recomputeOnInputChanged":false},"customCss":{"button":{"style":"","class":""},"container":{"style":"","class":""}},"recomputeIds":[],"horizontalAlignment":"center","verticalAlignment":"center","id":"a"},"id":"a"},{"3":{"fixed":false,"x":1,"y":2,"fullHeight":false,"w":1,"h":1},"12":{"fixed":false,"x":2,"y":2,"fullHeight":false,"w":2,"h":1},"data":{"type":"buttoncomponent","configuration":{"label":{"type":"static","value":"B"},"color":{"type":"static","value":"blue"},"size":{"type":"static","value":"xs"},"fillContainer":{"type":"static","value":false},"disabled":{"type":"static","value":false},"beforeIcon":{"type":"static"},"afterIcon":{"type":"static"},"tooltip":{"type":"static","value":""},"triggerOnAppLoad":{"type":"static","value":false},"runInBackground":{"type":"static","value":false},"onSuccess":{"type":"oneOf","selected":"none","configuration":{"none":{},"gotoUrl":{"url":{"type":"static","value":""},"newTab":{"type":"static","value":true}},"setTab":{"setTab":{"type":"static","value":[]}},"sendToast":{"message":{"type":"static","value":""}},"openModal":{"modalId":{"type":"static","value":""}},"closeModal":{"modalId":{"type":"static","value":""}},"open":{"id":{"type":"static","value":""}},"close":{"id":{"type":"static","value":""}},"clearFiles":{"id":{"type":"static","value":""}}}},"onError":{"type":"oneOf","selected":"errorOverlay","configuration":{"errorOverlay":{},"gotoUrl":{"url":{"type":"static","value":""},"newTab":{"type":"static","value":true}},"setTab":{"setTab":{"type":"static","value":[]}},"sendErrorToast":{"message":{"type":"static","value":"An error occurred"},"appendError":{"type":"static","value":true}},"open":{"id":{"type":"static","value":""}},"close":{"id":{"type":"static","value":""}}}},"confirmationModal":{"type":"oneOf","selected":"none","configuration":{"none":{},"confirmationModal":{"title":{"type":"static","value":"Title"},"description":{"type":"static","value":"Are you sure?"},"confirmationText":{"type":"static","value":"Confirm"}}}}},"componentInput":{"type":"runnable","fieldType":"any","fields":{},"runnable":{"type":"runnableByName","name":"Inline Script","inlineScript":{"content":"import f.dre_app.leaf_left\nimport f.dre_app.leaf_right\n\ndef main():\n pass\n","language":"python3","schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"},"path":"f/dre_app/app/Inline_Script"}},"autoRefresh":false,"recomputeOnInputChanged":false},"customCss":{"button":{"style":"","class":""},"container":{"style":"","class":""}},"recomputeIds":[],"horizontalAlignment":"center","verticalAlignment":"center","id":"b"},"id":"b"},{"3":{"fixed":false,"x":2,"y":2,"fullHeight":false,"w":1,"h":1},"12":{"fixed":false,"x":4,"y":2,"fullHeight":false,"w":2,"h":1},"data":{"type":"buttoncomponent","configuration":{"label":{"type":"static","value":"C"},"color":{"type":"static","value":"blue"},"size":{"type":"static","value":"xs"},"fillContainer":{"type":"static","value":false},"disabled":{"type":"static","value":false},"beforeIcon":{"type":"static"},"afterIcon":{"type":"static"},"tooltip":{"type":"static","value":""},"triggerOnAppLoad":{"type":"static","value":false},"runInBackground":{"type":"static","value":false},"onSuccess":{"type":"oneOf","selected":"none","configuration":{"none":{},"gotoUrl":{"url":{"type":"static","value":""},"newTab":{"type":"static","value":true}},"setTab":{"setTab":{"type":"static","value":[]}},"sendToast":{"message":{"type":"static","value":""}},"openModal":{"modalId":{"type":"static","value":""}},"closeModal":{"modalId":{"type":"static","value":""}},"open":{"id":{"type":"static","value":""}},"close":{"id":{"type":"static","value":""}},"clearFiles":{"id":{"type":"static","value":""}}}},"onError":{"type":"oneOf","selected":"errorOverlay","configuration":{"errorOverlay":{},"gotoUrl":{"url":{"type":"static","value":""},"newTab":{"type":"static","value":true}},"setTab":{"setTab":{"type":"static","value":[]}},"sendErrorToast":{"message":{"type":"static","value":"An error occurred"},"appendError":{"type":"static","value":true}},"open":{"id":{"type":"static","value":""}},"close":{"id":{"type":"static","value":""}}}},"confirmationModal":{"type":"oneOf","selected":"none","configuration":{"none":{},"confirmationModal":{"title":{"type":"static","value":"Title"},"description":{"type":"static","value":"Are you sure?"},"confirmationText":{"type":"static","value":"Confirm"}}}}},"componentInput":{"type":"runnable","fieldType":"any","fields":{},"runnable":{"type":"runnableByName","name":"Inline Script","inlineScript":{"content":"import f.dre_app.leaf_right\n\ndef main():\n pass\n","language":"python3","schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"},"path":"f/dre_app/app/Inline_Script"}},"autoRefresh":false,"recomputeOnInputChanged":false},"customCss":{"button":{"style":"","class":""},"container":{"style":"","class":""}},"recomputeIds":[],"horizontalAlignment":"center","verticalAlignment":"center","id":"c"},"id":"c"}],"fullscreen":false,"unusedInlineScripts":[],"hiddenInlineScripts":[],"theme":{"type":"path","path":"f/app_themes/theme_0"},"subgrids":{"topbar-0":[{"3":{"fixed":false,"x":0,"y":0,"fullHeight":false,"w":6,"h":1},"12":{"fixed":false,"x":0,"y":0,"fullHeight":false,"w":6,"h":1},"data":{"type":"textcomponent","configuration":{"style":{"type":"static","value":"Body"},"copyButton":{"type":"static","value":false},"tooltip":{"type":"evalv2","value":"","fieldType":"text","expr":"`Author: ${ctx.author}`","connections":[{"componentId":"ctx","id":"author"}]},"disableNoText":{"type":"static","value":true,"fieldType":"boolean"}},"componentInput":{"type":"templatev2","fieldType":"template","eval":"${ctx.summary}","connections":[{"id":"summary","componentId":"ctx"}]},"customCss":{"text":{"class":"text-xl font-semibold whitespace-nowrap truncate","style":""},"container":{"class":"","style":""}},"horizontalAlignment":"left","verticalAlignment":"center","id":"title"},"id":"title"},{"3":{"fixed":false,"x":0,"y":1,"fullHeight":false,"w":3,"h":1},"12":{"fixed":false,"x":6,"y":0,"fullHeight":false,"w":6,"h":1},"data":{"type":"recomputeallcomponent","configuration":{"defaultRefreshInterval":{"type":"static","value":"0"}},"customCss":{"container":{"style":"","class":""}},"menuItems":[],"horizontalAlignment":"right","verticalAlignment":"center","id":"recomputeall"},"id":"recomputeall"}]},"hideLegacyTopBar":true,"mobileViewOnSmallerScreens":false}$tag$,
+'system'
+);
+
+-- SCRIPTS --
+INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
+'test-workspace',
+'test-user',
+'#requirements:
+#bottle==0.13.2
+def main():
+ pass
+',
+'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
+'',
+'',
+'f/dre_script/leaf_left', 533400, 'python3', '');
+-- Padded Hex: 0000000000082398
+
+INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
+'test-workspace',
+'test-user',
+'#requirements:
+#tiny==0.1.3
+def main():
+ pass
+',
+'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
+'',
+'',
+'f/dre_script/leaf_right', 533403, 'python3', '');
+-- Padded Hex: 000000000008239B
+
+INSERT INTO public.script(workspace_id, created_by, content, schema, summary, description, path, hash, language, lock) VALUES (
+'test-workspace',
+'test-user',
+'
+import f.dre_script.leaf_left
+import f.dre_script.leaf_right
+
+def main():
+ pass
+',
+'{"$schema":"https://json-schema.org/draft/2020-12/schema","properties":{},"required":[],"type":"object"}',
+'',
+'',
+'f/dre_script/script', 533404, 'python3', '');
+-- Padded Hex: 000000000008239C
+
+-- Create dependency map
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre/leaf_left', 'flow', 'f/dre/flow', 'a');
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre/leaf_left', 'flow', 'f/dre/flow', 'b');
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre/leaf_right', 'flow', 'f/dre/flow', 'b');
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre/leaf_right', 'flow', 'f/dre/flow', 'c');
+
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre_app/leaf_left', 'app', 'f/dre_app/app', 'a');
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre_app/leaf_left', 'app', 'f/dre_app/app', 'b');
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre_app/leaf_right', 'app', 'f/dre_app/app', 'b');
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre_app/leaf_right', 'app', 'f/dre_app/app', 'c');
+
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre_script/leaf_left', 'script', 'f/dre_script/script', '');
+INSERT INTO dependency_map (workspace_id, imported_path, importer_kind, importer_path, importer_node_id) VALUES ('test-workspace', 'f/dre_script/leaf_right', 'script', 'f/dre_script/script', '');
diff --git a/backend/tests/job_payload.rs b/backend/tests/job_payload.rs
index d01064d4bc..ace3e61ef0 100644
--- a/backend/tests/job_payload.rs
+++ b/backend/tests/job_payload.rs
@@ -10,6 +10,7 @@ mod job_payload {
use windmill_common::jobs::JobPayload;
use windmill_common::flows::{FlowValue, FlowModule, FlowModuleValue};
use windmill_common::flow_status::RestartedFrom;
+
use windmill_common::worker::{
MIN_VERSION_IS_AT_LEAST_1_427, MIN_VERSION_IS_AT_LEAST_1_432, MIN_VERSION_IS_AT_LEAST_1_440,
};
diff --git a/backend/tests/relative_imports.rs b/backend/tests/relative_imports.rs
index 91af80918b..b088241f5a 100644
--- a/backend/tests/relative_imports.rs
+++ b/backend/tests/relative_imports.rs
@@ -1,48 +1,57 @@
// TODO: move all related logic here (if anything left anywhere in codebase)
mod common;
+use windmill_api_client::types::NewScript;
+
+fn quick_ns(
+ content: &str,
+ language: windmill_api_client::types::ScriptLang,
+ path: &str,
+ lock: Option,
+ parent_hash: Option,
+) -> NewScript {
+ NewScript {
+ content: content.into(),
+ language,
+ lock,
+ parent_hash,
+ path: path.into(),
+ concurrent_limit: None,
+ concurrency_time_window_s: None,
+ cache_ttl: None,
+ dedicated_worker: None,
+ description: "".to_string(),
+ draft_only: None,
+ envs: vec![],
+ is_template: None,
+ kind: None,
+ summary: "".to_string(),
+ tag: None,
+ schema: std::collections::HashMap::new(),
+ ws_error_handler_muted: Some(false),
+ priority: None,
+ delete_after_use: None,
+ timeout: None,
+ restart_unless_cancelled: None,
+ deployment_message: None,
+ concurrency_key: None,
+ visible_to_runner_only: None,
+ no_main_func: None,
+ codebase: None,
+ has_preprocessor: None,
+ on_behalf_of_email: None,
+ assets: vec![],
+ }
+}
+
mod dependency_map {
+ use super::quick_ns;
use sqlx::{Pool, Postgres};
use tokio_stream::StreamExt;
- use windmill_api_client::types::NewScript;
- use crate::common::{in_test_worker, listen_for_completed_jobs, ApiServer};
-
- pub async fn initialize_tracing() {
- use std::sync::Once;
-
- static ONCE: Once = Once::new();
- ONCE.call_once(|| {
- let _ = windmill_common::tracing_init::initialize_tracing(
- "test",
- &windmill_common::utils::Mode::Standalone,
- "test",
- );
- });
- }
-
- async fn rebuild_dmap(client: &windmill_api_client::Client) -> bool {
- client
- .client()
- .post(format!(
- "{}/w/test-workspace/workspaces/rebuild_dependency_map",
- client.baseurl()
- ))
- .send()
- .await
- .unwrap()
- .status()
- .is_success()
- }
+ use crate::common::{in_test_worker, init_client, listen_for_completed_jobs, ApiServer};
async fn init(db: Pool) -> (windmill_api_client::Client, u16, ApiServer) {
- initialize_tracing().await;
- let server = ApiServer::start(db).await.unwrap();
- let port = server.addr.port();
- let client = windmill_api_client::create_client(
- &format!("http://localhost:{port}"),
- "SECRET_TOKEN".to_string(),
- );
- (client, port, server)
+ init_client(db).await
}
async fn _clear_dmap(db: &Pool) {
@@ -108,47 +117,6 @@ mod dependency_map {
);
}
- fn quick_ns(
- content: &str,
- language: windmill_api_client::types::ScriptLang,
- path: &str,
- lock: Option,
- parent_hash: Option,
- ) -> NewScript {
- NewScript {
- content: content.into(),
- language,
- lock,
- parent_hash,
- path: path.into(),
- concurrent_limit: None,
- concurrency_time_window_s: None,
- cache_ttl: None,
- dedicated_worker: None,
- description: "".to_string(),
- draft_only: None,
- envs: vec![],
- is_template: None,
- kind: None,
- summary: "".to_string(),
- tag: None,
- schema: std::collections::HashMap::new(),
- ws_error_handler_muted: Some(false),
- priority: None,
- delete_after_use: None,
- timeout: None,
- restart_unless_cancelled: None,
- deployment_message: None,
- concurrency_key: None,
- visible_to_runner_only: None,
- no_main_func: None,
- codebase: None,
- has_preprocessor: None,
- on_behalf_of_email: None,
- assets: vec![],
- }
- }
-
lazy_static::lazy_static! {
pub static ref CORRECT_DMAP: Vec<(&'static str, &'static str, &'static str, &'static str)> = vec![
("f/rel/branch", "script", "f/rel/leaf_1", ""),
@@ -170,13 +138,16 @@ mod dependency_map {
("f/rel/root_app", "app", "f/rel/branch", "youcanpressme")];
}
+ // TODO:
+ // Test that checks that we can run rebuild_dmap multiple times in tests.
+
#[cfg(feature = "python")]
#[sqlx::test(fixtures("base", "dependency_map"))]
async fn relative_imports_test_rebuild_correctness(db: Pool) -> anyhow::Result<()> {
let (client, _port, _s) = init(db.clone()).await;
assert_dmap(&db, None, CORRECT_DMAP.clone()).await;
// rebuild map
- assert!(rebuild_dmap(&client).await);
+ assert!(super::common::rebuild_dmap(&client).await);
assert_dmap(&db, None, CORRECT_DMAP.clone()).await;
Ok(())
}
@@ -189,7 +160,7 @@ mod dependency_map {
// Spawn first rebuild
let handle = {
let client = client.clone();
- tokio::spawn(async move { rebuild_dmap(&client).await })
+ tokio::spawn(async move { super::common::rebuild_dmap(&client).await })
};
// Immidiately spawn another
@@ -235,7 +206,7 @@ def main():
",
windmill_api_client::types::ScriptLang::Python3,
"f/rel/root_script",
- Some(format!("# from requirements.txt")),
+ Some("# from requirements.txt".to_string()),
Some("000000000005165B".into()),
),
)
@@ -287,14 +258,14 @@ def main():
windmill_api_client::types::ScriptLang::Python3,
"f/rel/root_script",
// We still want to pass lock to it.
- Some(format!("# py311")),
+ Some("# py311".to_string()),
Some("000000000005165B".into()),
),
)
.await
.unwrap();
assert_dmap(&db, None, CORRECT_DMAP.clone()).await;
- tokio::time::sleep(std::time::Duration::from_secs(13)).await;
+ // tokio::time::sleep(std::time::Duration::from_secs(13)).await;
assert_dmap(&db, None, CORRECT_DMAP.clone()).await;
Ok(())
}
@@ -547,3 +518,1847 @@ def main():
Ok(())
}
}
+
+#[cfg(feature = "test_job_debouncing")]
+mod job_debouncing {
+ async fn trigger_djob_for(
+ client: &windmill_api_client::Client,
+ path: &str,
+ parent_hash: &str,
+ content: Option,
+ ) {
+ use super::quick_ns;
+ use windmill_api_client::types::ScriptLang;
+ client
+ .create_script(
+ "test-workspace",
+ &quick_ns(
+ &content.unwrap_or(
+ "
+def main():
+ pass
+ "
+ .into(),
+ ),
+ ScriptLang::Python3,
+ path,
+ None,
+ Some(parent_hash.into()),
+ ),
+ )
+ .await
+ .unwrap();
+ }
+ // TODO: test workspaces specific things,
+
+ /// # Double referenced even
+ /// It follows this topology:
+ ///
+ /// ┌─FLOW──────────┐
+ /// │┌───┐┌───┐┌───┐│
+ /// ││ A ││ B ││ C ││
+ /// │└─▲─┘▲───▲└─▲─┘│
+ /// └──┼──┼───┼──┼──┘
+ /// ┌┴──┴┐ ┌┴──┴┐
+ /// │L_LF│ │R_LF│
+ /// └────┘ └────┘
+ ///
+ /// p.s: "LF" stands for "Leaf", "L" - "Left", "R" - "Right"
+ mod flows {
+ use crate::common::{in_test_worker, init_client, listen_for_completed_jobs};
+ use crate::job_debouncing::trigger_djob_for;
+ use std::time::Duration;
+ use tokio::time::sleep;
+ use tokio_stream::StreamExt;
+
+ /// 1. LLF and RLF create two djobs for flow at the same and fall into single debounce
+ #[cfg(feature = "python")]
+ #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ async fn test_1(db: sqlx::Pool) -> anyhow::Result<()> {
+ // This tests if debouncing and consolidation works.
+ // Also makes sures that dependency job does not create new flow version
+
+ let (client, port, _s) = init_client(db.clone()).await;
+ let mut completed = listen_for_completed_jobs(&db).await;
+
+ // Verify locks are empty
+ {
+ assert_eq!(
+ sqlx::query_scalar!("SELECT jsonb_array_elements(value->'modules')->'value'->>'lock' AS lock FROM flow")
+ .fetch_all(&db)
+ .await
+ .unwrap(),
+ vec![
+ Some("# py: 3.11\n".into()),
+ Some("# py: 3.11\n".into()),
+ Some("# py: 3.11\n".into())
+ ]
+ );
+ }
+
+ // Trigger both at the same time.
+ {
+ trigger_djob_for(
+ &client,
+ "f/dre/leaf_left",
+ "0000000000051658",
+ Some("#requirements:\n#bottle==0.13.2\ndef main():\npass".into()),
+ )
+ .await;
+
+ trigger_djob_for(
+ &client,
+ "f/dre/leaf_right",
+ "000000000005165B",
+ Some("#requirements:\n#tiny==0.1.3\ndef main():\npass".into()),
+ )
+ .await;
+ }
+
+ in_test_worker(
+ &db,
+ async {
+ assert_eq!(
+ &sqlx::query_scalar!(
+ "SELECT runnable_path FROM v2_job WHERE id = $1",
+ completed.next().await.unwrap()
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "f/dre/leaf_left"
+ );
+
+ assert_eq!(
+ &sqlx::query_scalar!(
+ "SELECT runnable_path FROM v2_job WHERE id = $1",
+ completed.next().await.unwrap()
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "f/dre/leaf_right"
+ );
+
+ // Let jobs propagate
+ sleep(Duration::from_secs(2)).await;
+
+ // Verify there is only one queued job that is scheduled for atleast 3s ahead.
+ {
+ let q = sqlx::query_scalar!(
+ "SELECT (scheduled_for - created_at) FROM v2_job_queue"
+ )
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ assert_eq!(1, q.len());
+ assert!(dbg!(q[0].unwrap().microseconds) > 1_000_000 /* 1 second */);
+ }
+
+ // Verify debounce_stale_data and debounce_key
+ {
+ let q = sqlx::query!(
+ "SELECT
+ dsd.to_relock,
+ dk.key
+ FROM debounce_key dk
+ JOIN debounce_stale_data dsd ON dk.job_id = dsd.job_id"
+ )
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ // Should be single entry
+ assert!(q.len() == 1);
+
+ // This verifies that all nodes_to_relock are consolidated correctly
+ // AND there is no doublicats
+ assert_eq!(
+ q[0].to_relock.clone().unwrap(),
+ vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]
+ );
+
+ // Should be workspace specific and these specific tests cover only dependency job debouncing
+ assert_eq!(
+ q[0].key.clone(),
+ "test-workspace:f/dre/flow:dependency".to_owned(),
+ );
+ }
+
+ assert_eq!(
+ &sqlx::query_scalar!(
+ "SELECT runnable_path FROM v2_job WHERE id = $1",
+ completed.next().await.unwrap()
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "f/dre/flow"
+ );
+ },
+ port,
+ )
+ .await;
+
+ // Verify latest flow.version property
+ {
+ // Latest flow version should not be initial one
+ assert_eq!(
+ 1, // Automatically assigned
+ dbg!(sqlx::query_scalar!(
+ "SELECT versions[2] FROM flow WHERE path = 'f/dre/flow'"
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap())
+ );
+
+ // Only second element should be our initial version
+ assert_eq!(
+ 1443253234253454, // < Predefined in fixture
+ dbg!(sqlx::query_scalar!(
+ "SELECT versions[1] FROM flow WHERE path = 'f/dre/flow'"
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap())
+ );
+ }
+
+ // Verify that there is only two versions of flow in global flow_version
+ {
+ assert_eq!(
+ 2,
+ sqlx::query_scalar!(
+ "SELECT COUNT(*) FROM flow_version WHERE path = 'f/dre/flow'"
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ }
+
+ // Verify locks
+ {
+ assert_eq!(
+ sqlx::query_scalar!("SELECT jsonb_array_elements(value->'modules')->'value'->>'lock' AS lock FROM flow")
+ .fetch_all(&db)
+ .await
+ .unwrap(),
+ vec![
+ Some("# py: 3.11\nbottle==0.13.2".into()),
+ Some("# py: 3.11\nbottle==0.13.2\ntiny==0.1.3".into()),
+ Some("# py: 3.11\ntiny==0.1.3".into())
+ ]
+ );
+ }
+
+ // TODO:
+ // tracing_assertions::assert_has_events!([info("This is supposed to be called")]);
+ // 2025-10-06T14:31:10.832469Z WARN windmill-worker/src/worker.rs:1593: pull took more than 0.1s (0.222477345) this is a sign that the database is undersized for this load. empty: true, err: true worker=wk-default-nixos-EzDEL hostname=nixos
+
+ // Verify cleanup
+ {
+ assert_eq!(
+ 0,
+ sqlx::query_scalar!("SELECT COUNT(*) from debounce_key")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ assert_eq!(
+ 0,
+ sqlx::query_scalar!("SELECT COUNT(*) from debounce_stale_data")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ }
+
+ Ok(())
+ }
+
+ #[cfg(feature = "python")]
+ #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ async fn test_left(db: sqlx::Pool) -> anyhow::Result<()> {
+ use crate::common::RunJob;
+
+ // TODO: We don't care about timer. If there is no timer, it will be set automatically for djobs??
+ let (_client, port, _s) = init_client(db.clone()).await;
+ let mut completed = listen_for_completed_jobs(&db).await;
+
+ // Trigger both at the same time.
+ {
+ let mut args = std::collections::HashMap::new();
+ args.insert(
+ "dbg_djob_sleep".to_owned(),
+ // Execution should take this seconds
+ windmill_common::worker::to_raw_value(&20),
+ );
+
+ args.insert(
+ "triggered_by_relative_import".to_owned(),
+ // Execution should take this seconds
+ windmill_common::worker::to_raw_value(&()),
+ );
+
+ let (_flow_id, new_tx) = windmill_queue::push(
+ &db,
+ windmill_queue::PushIsolationLevel::IsolatedRoot(db.clone()),
+ "test-workspace",
+ windmill_common::jobs::JobPayload::FlowDependencies {
+ path: "f/dre/flow".to_owned(),
+ dedicated_worker: None,
+ version: 1443253234253454,
+ },
+ windmill_queue::PushArgs { args: &args, extra: None },
+ "admin",
+ "admin@windmill.dev",
+ "admin".to_owned(),
+ Some("trigger.dependents.to.recompute.dependencies"),
+ // Debounce period
+ Some(chrono::Utc::now() + chrono::Duration::seconds(5)),
+ None,
+ None,
+ None,
+ None,
+ None,
+ false,
+ false,
+ None,
+ true,
+ Some("dependency".into()),
+ None,
+ None,
+ None,
+ None,
+ false,
+ None,
+ None,
+ )
+ .await
+ .unwrap();
+ new_tx.commit().await.unwrap();
+
+ // let handle = {
+ // // let mut completed = listen_for_completed_jobs(&db).await;
+ // let db2 = db.clone();
+ // // let uuid = flow_id.clone();
+ // tokio::spawn(async move {
+ // in_test_worker(
+ // &db2,
+ // tokio::time::sleep(tokio::time::Duration::from_secs(60)),
+ // // completed.find(&uuid),
+ // port,
+ // )
+ // .await;
+ // })
+ // };
+
+ let db2 = db.clone();
+ in_test_worker(
+ &db2,
+ async {
+ // This job should execute and then try to start another job that will get debounced.
+ RunJob::from(windmill_common::jobs::JobPayload::Dependencies {
+ path: "f/dre/leaf_right".to_owned(),
+ hash: 333403.into(),
+ language: windmill_common::scripts::ScriptLang::Python3,
+ dedicated_worker: None,
+ })
+ // .arg("dbg_djob_sleep", serde_json::json!(10))
+ .run_until_complete(&db, port)
+ .await;
+
+ RunJob::from(windmill_common::jobs::JobPayload::Dependencies {
+ path: "f/dre/leaf_left".to_owned(),
+ hash: 333400.into(),
+ language: windmill_common::scripts::ScriptLang::Python3,
+ dedicated_worker: None,
+ })
+ // So set it to this long
+ .arg("dbg_djob_sleep", serde_json::json!(10))
+ .run_until_complete(&db, port)
+ .await;
+
+ completed.next().await; // leaf_right
+ completed.next().await; // leaf_left
+ completed.next().await; // importer
+ completed.next().await; // importer
+ },
+ port,
+ )
+ .await;
+ }
+
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job_queue")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 0
+ );
+
+ let r = sqlx::query_scalar!("SELECT runnable_id FROM v2_job ORDER BY created_at DESC")
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ assert_eq!(r.len(), 4);
+ assert!(r.contains(&Some(1)));
+ assert!(r.contains(&Some(333400)));
+ assert!(r.contains(&Some(333403)));
+ assert!(r.contains(&Some(1443253234253454)));
+
+ Ok(())
+ }
+
+ #[cfg(feature = "python")]
+ #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ async fn test_2(db: sqlx::Pool) -> anyhow::Result<()> {
+ let (_client, port, _s) = init_client(db.clone()).await;
+ let mut completed = listen_for_completed_jobs(&db).await;
+
+ // Function to create a dependency job
+ let create_dependency_job =
+ |delay,
+ nodes_to_relock,
+ db: sqlx::Pool,
+ version,
+ debounce_job_id_o| async move {
+ let mut args = std::collections::HashMap::new();
+ args.insert(
+ "dbg_sleep_between_pull_and_debounce_key_removal".to_owned(),
+ windmill_common::worker::to_raw_value(&delay),
+ );
+
+ args.insert(
+ "nodes_to_relock".to_owned(),
+ windmill_common::worker::to_raw_value(&nodes_to_relock),
+ );
+
+ args.insert(
+ "triggered_by_relative_import".to_string(),
+ windmill_common::worker::to_raw_value(&()),
+ );
+
+ args.insert(
+ "dbg_create_job_for_unexistant_flow_version".to_string(),
+ windmill_common::worker::to_raw_value(&()),
+ );
+
+ let (job_uuid, new_tx) = windmill_queue::push(
+ &db,
+ windmill_queue::PushIsolationLevel::IsolatedRoot(db.clone()),
+ "test-workspace",
+ windmill_common::jobs::JobPayload::FlowDependencies {
+ path: "f/dre/flow".to_owned(),
+ dedicated_worker: None,
+ version,
+ },
+ windmill_queue::PushArgs { args: &args, extra: None },
+ "admin",
+ "admin@windmill.dev",
+ "admin".to_owned(),
+ Some("trigger.dependents.to.recompute.dependencies"),
+ Some(chrono::Utc::now()), // Schedule immediately
+ None,
+ None,
+ None,
+ None,
+ None,
+ false,
+ false,
+ None,
+ true,
+ Some("dependency".into()),
+ None,
+ None,
+ None,
+ None,
+ false,
+ None,
+ debounce_job_id_o,
+ )
+ .await
+ .unwrap();
+
+ new_tx.commit().await.unwrap();
+ job_uuid
+ };
+
+ // Push the first dependency job
+ let job1 =
+ create_dependency_job(2, vec!["a", "b"], db.clone(), 1443253234253454, None).await;
+
+ let db2 = db.clone();
+ in_test_worker(
+ &db2,
+ async {
+ windmill_common::worker::update_min_version(
+ &windmill_common::worker::Connection::Sql(db2.clone()),
+ )
+ .await;
+ // Small delay to ensure the job is marked as running
+ tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
+
+ // Now is the time when the job is pulled, but debounce_key is not yet cleared.
+ {
+ assert!(sqlx::query_scalar!(
+ "SELECT running FROM v2_job_queue WHERE id = $1",
+ job1
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap());
+
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM debounce_key")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 1
+ );
+ }
+
+ // Block all tests using this variable until we are done
+ // let mut min_v = windmill_common::worker::MIN_VERSION_IS_AT_LEAST_1_440
+ // .write()
+ // .await;
+
+ // // Save initial min_v value;
+ // let initi_min_v = *min_v;
+
+ // // Make it true for this test.
+ // *min_v = true;
+
+ // Now push a second dependency job while the first is being processed
+ // This should trigger the race condition handling code
+ let job2 =
+ create_dependency_job(0, vec!["b", "c"], db.clone(), 1, Some(job1)).await;
+
+ // Set it back to initial
+ // *min_v = initi_min_v;
+
+ // Unblock all other tests
+ // drop(min_v);
+
+ // Process the first job completion, and the second job should also get debounced by this one
+ completed.next().await;
+
+ // Verify that both jobs were created and processed
+ assert_eq!(job1, job2, "Second job should be debounced");
+ },
+ port,
+ )
+ .await;
+
+ assert_eq!(
+ vec![1443253234253454, 1],
+ sqlx::query_scalar!("SELECT versions FROM flow WHERE path = 'f/dre/flow'")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ );
+
+ // Verify cleanup - all debounce entries should be cleaned up
+ assert_eq!(
+ 0,
+ sqlx::query_scalar!("SELECT COUNT(*) from debounce_key")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "All debounce_key entries should be cleaned up after job completion"
+ );
+
+ assert_eq!(
+ 0,
+ sqlx::query_scalar!("SELECT COUNT(*) from debounce_stale_data")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "All debounce_stale_data entries should be cleaned up after job completion"
+ );
+
+ // Verify locks
+ {
+ assert_eq!(
+ sqlx::query_scalar!("SELECT jsonb_array_elements(value->'modules')->'value'->>'lock' AS lock FROM flow")
+ .fetch_all(&db)
+ .await
+ .unwrap(),
+ vec![
+ Some("# py: 3.11\nbottle==0.13.2".into()),
+ Some("# py: 3.11\nbottle==0.13.2\ntiny==0.1.3".into()),
+ Some("# py: 3.11\ntiny==0.1.3".into())
+ ]
+ );
+ }
+
+ Ok(())
+ }
+ /// 2. Same as second test, however first flow djob will take longer than second debounce.
+ /// NOTE: This test should be ran in debug mode with `private` features enabled. In release it will not work properly.
+ #[cfg(all(feature = "python", feature = "private"))]
+ #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ // #[windmill::all_min_versions]
+ async fn test_3(db: sqlx::Pool) -> anyhow::Result<()> {
+ // This tests checks if concurrency limit works correcly and there is no race conditions.
+
+ use windmill_common::worker::Connection;
+ let (_client, port, _s) = init_client(db.clone()).await;
+ let mut completed = listen_for_completed_jobs(&db).await;
+
+ // At this point we should have two
+ let mut job_ids = vec![];
+ let push_job = |delay, version, db, nodes_to_relock, debounce_job_id_o| async move {
+ let mut args = std::collections::HashMap::new();
+ args.insert(
+ "dbg_djob_sleep".to_owned(),
+ // First one will create delay for 5 seconds
+ // The second will have no delay at all.
+ windmill_common::worker::to_raw_value(&delay),
+ );
+
+ args.insert(
+ "nodes_to_relock".to_owned(),
+ windmill_common::worker::to_raw_value(&nodes_to_relock),
+ );
+
+ args.insert(
+ "triggered_by_relative_import".to_string(),
+ windmill_common::worker::to_raw_value(&()),
+ );
+
+ let (job_uuid, new_tx) = windmill_queue::push(
+ &db,
+ windmill_queue::PushIsolationLevel::IsolatedRoot(db.clone()),
+ "test-workspace",
+ windmill_common::jobs::JobPayload::FlowDependencies {
+ path: "f/dre/flow".to_owned(),
+ dedicated_worker: None,
+ // In newest versions we pass the current version to the djob
+ // version: 1443253234253454,
+ version,
+ },
+ windmill_queue::PushArgs { args: &args, extra: None },
+ "admin",
+ "admin@windmill.dev",
+ "admin".to_owned(),
+ Some("trigger.dependents.to.recompute.dependencies"),
+ // Schedule for now.
+ Some(chrono::Utc::now()),
+ None,
+ None,
+ None,
+ None,
+ None,
+ false,
+ false,
+ None,
+ true,
+ Some("dependency".into()),
+ None,
+ None,
+ None,
+ None,
+ false,
+ None,
+ debounce_job_id_o,
+ )
+ .await
+ .unwrap();
+
+ new_tx.commit().await.unwrap();
+
+ job_uuid
+ };
+
+ // Push first
+ job_ids.push(push_job(5, 1443253234253454, db.clone(), ["a", "b"], None).await);
+
+ // Verify debounce_stale_data and debounce_key
+ {
+ let q = sqlx::query!("SELECT COUNT(*) FROM debounce_key")
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ // Should be single entry
+ assert_eq!(q.len(), 1);
+ }
+
+ // Start the first one in the background
+ let handle = {
+ let mut completed = listen_for_completed_jobs(&db).await;
+ let db2 = db.clone();
+ tokio::spawn(async move {
+ in_test_worker(
+ &db2,
+ // sleep(Duration::from_secs(7)),
+ completed.next(), // Only wait for the single job. We are going to spawn another worker for second one.
+ port,
+ )
+ .await;
+ })
+ };
+
+ // Wait for the job to be created and started
+ // This way next job is not going to be consumed by the first one.
+ sleep(Duration::from_secs(2)).await;
+
+ // Push second
+ job_ids.push(push_job(0, 1, db.clone(), ["b", "c"], None).await);
+
+ // Wait for the second one to finish in separate worker.
+ // in_test_worker(&db, completed.next(), port).await;
+ in_test_worker(
+ &db,
+ async {
+ // First job will be pulled
+ completed.next().await;
+ // However since we have concurrency limit enabled it will get rescheduled by creation of new djob.
+ // So we have to wait for that one as well.
+ completed.next().await;
+ },
+ port,
+ )
+ .await;
+
+ // Wait for the first one
+ handle.await.unwrap();
+
+ // Verify locks
+ {
+ assert_eq!(
+ sqlx::query_scalar!("SELECT jsonb_array_elements(value->'modules')->'value'->>'lock' AS lock FROM flow")
+ .fetch_all(&db)
+ .await
+ .unwrap(),
+ vec![
+ Some("# py: 3.11\nbottle==0.13.2".into()),
+ Some("# py: 3.11\nbottle==0.13.2\ntiny==0.1.3".into()),
+ Some("# py: 3.11\ntiny==0.1.3".into())
+ ]
+ );
+ }
+ // Verify that we have expected outcome
+ {
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job",)
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 2
+ );
+
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job_completed",)
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 2
+ );
+ // Check that two jobs were executed sequentially
+ assert!(sqlx::query_scalar!(
+ "
+SELECT
+ j1.completed_at < j2.started_at
+FROM
+ v2_job_completed j1,
+ v2_job_completed j2
+WHERE
+ j1.id = $1
+ AND j2.id = $2",
+ job_ids[0],
+ job_ids[1],
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap());
+ }
+ Ok(())
+ }
+
+ // TODO:
+ // test that update or create flow that should bypass debouncing
+ }
+
+ /// ## Testing for Apps
+ /// For apps we are going to do similar tests that we did for flows
+ mod apps {
+ use crate::common::{in_test_worker, init_client, listen_for_completed_jobs};
+ use crate::job_debouncing::trigger_djob_for;
+ use std::time::Duration;
+ use tokio::time::sleep;
+ use tokio_stream::StreamExt;
+
+ /// 1. LLF and RLF create two djobs for flow at the same and fall into single debounce
+ #[cfg(feature = "python")]
+ #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ async fn test_1(db: sqlx::Pool) -> anyhow::Result<()> {
+ // This tests if debouncing and consolidation works.
+ // Also makes sures that dependency job does not create new flow version
+
+ let (client, port, _s) = init_client(db.clone()).await;
+ let mut completed = listen_for_completed_jobs(&db).await;
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 0
+ );
+
+ // Trigger both at the same time.
+ // It will create two immediate dependency jobs
+ {
+ trigger_djob_for(
+ &client,
+ "f/dre_app/leaf_left",
+ "0000000000069CF8",
+ Some("#requirements:\n#bottle==0.13.2\ndef main():\npass".into()),
+ )
+ .await;
+
+ trigger_djob_for(
+ &client,
+ "f/dre_app/leaf_right",
+ "0000000000069CFB",
+ Some("#requirements:\n#tiny==0.1.3\ndef main():\npass".into()),
+ )
+ .await;
+ }
+
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 2
+ );
+
+ // Spawn single worker.
+ in_test_worker(
+ &db,
+ async {
+ assert_eq!(
+ &sqlx::query_scalar!(
+ "SELECT runnable_path FROM v2_job WHERE id = $1",
+ completed.next().await.unwrap()
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "f/dre_app/leaf_left"
+ );
+
+ assert_eq!(
+ &sqlx::query_scalar!(
+ "SELECT runnable_path FROM v2_job WHERE id = $1",
+ completed.next().await.unwrap()
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "f/dre_app/leaf_right"
+ );
+
+ // Verify there is only one queued job that is scheduled for atleast 3s ahead.
+ {
+ let q = sqlx::query_scalar!(
+ "SELECT (scheduled_for - created_at) FROM v2_job_queue"
+ )
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ assert_eq!(1, q.len());
+ assert!(dbg!(q[0].unwrap().microseconds) > 2_000_000);
+ }
+
+ // Verify debounce_stale_data and debounce_key
+ {
+ let q = sqlx::query!(
+ "SELECT
+ dsd.to_relock,
+ dk.key
+ FROM debounce_key dk
+ JOIN debounce_stale_data dsd ON dk.job_id = dsd.job_id"
+ )
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ // Should be single entry
+ assert!(q.len() == 1);
+
+ // This verifies that all nodes_to_relock are consolidated correctly
+ // AND there is no doublicats
+ assert_eq!(
+ q[0].to_relock.clone().unwrap(),
+ vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]
+ );
+
+ // Should be workspace specific and these specific tests cover only dependency job debouncing
+ assert_eq!(
+ q[0].key.clone(),
+ "test-workspace:f/dre_app/app:dependency".to_owned(),
+ );
+ }
+
+ assert_eq!(
+ &sqlx::query_scalar!(
+ "SELECT runnable_path FROM v2_job WHERE id = $1",
+ completed.next().await.unwrap()
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "f/dre_app/app"
+ );
+ },
+ port,
+ )
+ .await;
+
+ // Verify App states
+ {
+ let q = dbg!(sqlx::query_scalar!(
+ "SELECT versions FROM app WHERE path = 'f/dre_app/app'"
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap());
+
+ assert_eq!(2, q.len());
+
+ // There is also supposed to be this amount of app_versions
+ assert_eq!(
+ 2,
+ sqlx::query_scalar!("SELECT COUNT(*) FROM app_version WHERE app_id = '2'")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ }
+
+ // Verify cleanup
+ {
+ assert_eq!(
+ 0,
+ sqlx::query_scalar!("SELECT COUNT(*) from debounce_key")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ assert_eq!(
+ 0,
+ sqlx::query_scalar!("SELECT COUNT(*) from debounce_stale_data")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ }
+
+ Ok(())
+ }
+
+ #[cfg(feature = "python")]
+ #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ async fn test_left(db: sqlx::Pool) -> anyhow::Result<()> {
+ use crate::common::RunJob;
+
+ // TODO: We don't care about timer. If there is no timer, it will be set automatically for djobs??
+ let (_client, port, _s) = init_client(db.clone()).await;
+ let mut completed = listen_for_completed_jobs(&db).await;
+
+ let mut args = std::collections::HashMap::new();
+ args.insert(
+ "dbg_djob_sleep".to_owned(),
+ // Execution should take this seconds
+ windmill_common::worker::to_raw_value(&20),
+ );
+ args.insert(
+ "triggered_by_relative_import".to_owned(),
+ // Execution should take this seconds
+ windmill_common::worker::to_raw_value(&()),
+ );
+
+ let (_flow_id, new_tx) = windmill_queue::push(
+ &db,
+ windmill_queue::PushIsolationLevel::IsolatedRoot(db.clone()),
+ "test-workspace",
+ windmill_common::jobs::JobPayload::AppDependencies {
+ path: "f/dre_app/app".to_owned(),
+ version: 0,
+ },
+ windmill_queue::PushArgs { args: &args, extra: None },
+ "admin",
+ "admin@windmill.dev",
+ "admin".to_owned(),
+ Some("trigger.dependents.to.recompute.dependencies"),
+ // Debounce period
+ Some(chrono::Utc::now() + chrono::Duration::seconds(5)),
+ None,
+ None,
+ None,
+ None,
+ None,
+ false,
+ false,
+ None,
+ true,
+ Some("dependency".into()),
+ None,
+ None,
+ None,
+ None,
+ false,
+ None,
+ None,
+ )
+ .await
+ .unwrap();
+ new_tx.commit().await.unwrap();
+
+ // let mut handle = {
+ // let mut completed = listen_for_completed_jobs(&db).await;
+ // let db2 = db.clone();
+ // let uuid = flow_id.clone();
+ // tokio::spawn(async move {
+ // in_test_worker(
+ // &db2,
+ // // tokio::time::sleep(tokio::time::Duration::from_secs(60)),
+ // async move {
+ // completed.find(&uuid).await;
+ // },
+ // port,
+ // )
+ // .await;
+ // })
+ // };
+
+ let db2 = db.clone();
+ in_test_worker(
+ &db2,
+ async {
+ // This job should execute and then try to start another job that will get debounced.
+ RunJob::from(windmill_common::jobs::JobPayload::Dependencies {
+ path: "f/dre_app/leaf_right".to_owned(),
+ hash: 433403.into(),
+ language: windmill_common::scripts::ScriptLang::Python3,
+ dedicated_worker: None,
+ })
+ // .arg("dbg_djob_sleep", serde_json::json!(10))
+ .run_until_complete(&db, port)
+ .await;
+
+ RunJob::from(windmill_common::jobs::JobPayload::Dependencies {
+ path: "f/dre_app/leaf_left".to_owned(),
+ hash: 433400.into(),
+ language: windmill_common::scripts::ScriptLang::Python3,
+ dedicated_worker: None,
+ })
+ // So set it to this long
+ .arg("dbg_djob_sleep", serde_json::json!(10))
+ .run_until_complete(&db, port)
+ .await;
+
+ completed.next().await; // leaf_right
+ completed.next().await; // leaf_left
+ completed.next().await; // importer
+ completed.next().await; // importer
+ },
+ port,
+ )
+ .await;
+
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job_queue")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 0
+ );
+
+ let r = sqlx::query_scalar!("SELECT runnable_id FROM v2_job ORDER BY created_at DESC")
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ assert_eq!(r.len(), 4);
+ assert!(r.contains(&Some(9)));
+ assert!(r.contains(&Some(433400)));
+ assert!(r.contains(&Some(433403)));
+ assert!(r.contains(&Some(0)));
+
+ // handle.await.unwrap();
+
+ Ok(())
+ }
+ /// 2. Same as second test, however first app djob will take longer than second debounce.
+ /// NOTE: This test should be ran in debug mode. In release it will not work properly.
+ #[cfg(all(feature = "python", feature = "private"))]
+ #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ async fn test_3(db: sqlx::Pool) -> anyhow::Result<()> {
+ // This tests checks if concurrency limit works correcly and there is no race conditions.
+ let (_client, port, _s) = init_client(db.clone()).await;
+ let mut completed = listen_for_completed_jobs(&db).await;
+
+ // At this point we should have two
+ let mut job_ids = vec![];
+ let push_job = |delay, db| async move {
+ let mut args = std::collections::HashMap::new();
+ args.insert(
+ "dbg_djob_sleep".to_owned(),
+ // First one will create delay for 5 seconds
+ // The second will have no delay at all.
+ windmill_common::worker::to_raw_value(&delay),
+ );
+
+ args.insert(
+ "triggered_by_relative_import".to_string(),
+ windmill_common::worker::to_raw_value(&()),
+ );
+
+ let (job_uuid, new_tx) = windmill_queue::push(
+ &db,
+ windmill_queue::PushIsolationLevel::IsolatedRoot(db.clone()),
+ "test-workspace",
+ windmill_common::jobs::JobPayload::AppDependencies {
+ path: "f/dre_app/app".to_owned(),
+ // In newest versions we pass the current version to the djob
+ version: 0,
+ },
+ windmill_queue::PushArgs { args: &args, extra: None },
+ "admin",
+ "admin@windmill.dev",
+ "admin".to_owned(),
+ Some("trigger.dependents.to.recompute.dependencies"),
+ // Schedule for now.
+ Some(chrono::Utc::now()),
+ None,
+ None,
+ None,
+ None,
+ None,
+ false,
+ false,
+ None,
+ true,
+ Some("dependency".into()),
+ None,
+ None,
+ None,
+ None,
+ false,
+ None,
+ None,
+ )
+ .await
+ .unwrap();
+
+ new_tx.commit().await.unwrap();
+
+ job_uuid
+ };
+
+ // TODO: Verify concurrency key.
+ // Push first
+ job_ids.push(push_job(5, db.clone()).await);
+
+ // Verify debounce_stale_data and debounce_key
+ {
+ let q = sqlx::query!("SELECT COUNT(*) FROM debounce_key")
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ // Should be single entry
+ assert_eq!(q.len(), 1);
+ }
+
+ // Start the first one in the background
+ let handle = {
+ let mut completed = listen_for_completed_jobs(&db).await;
+ let db2 = db.clone();
+ tokio::spawn(async move {
+ in_test_worker(
+ &db2,
+ // sleep(Duration::from_secs(7)),
+ completed.next(), // Only wait for the single job. We are going to spawn another worker for second one.
+ port,
+ )
+ .await;
+ })
+ };
+
+ // Wait for the job to be created and started
+ // This way next job is not going to be consumed by the first one.
+ sleep(Duration::from_secs(2)).await;
+
+ // Push second
+ job_ids.push(push_job(0, db.clone()).await);
+
+ // Wait for the second one to finish in separate worker.
+ // in_test_worker(&db, completed.next(), port).await;
+ in_test_worker(
+ &db,
+ async {
+ // First job will be pulled
+ completed.next().await;
+ // However since we have concurrency limit enabled it will get rescheduled by creation of new djob.
+ // So we have to wait for that one as well.
+ completed.next().await;
+ },
+ port,
+ )
+ .await;
+
+ // Wait for the first one
+ handle.await.unwrap();
+
+ // Verify that we have expected outcome
+ {
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job",)
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 2
+ );
+
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job_completed",)
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 2
+ );
+ // Check that two jobs were executed sequentially
+ assert!(sqlx::query_scalar!(
+ "
+SELECT
+ j1.completed_at < j2.started_at
+FROM
+ v2_job_completed j1,
+ v2_job_completed j2
+WHERE
+ j1.id = $1
+ AND j2.id = $2",
+ job_ids[0],
+ job_ids[1],
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap());
+ }
+ Ok(())
+ }
+ }
+
+ // TODO: Test debounce reassignment works
+
+ /// ## Testing for Scripts
+ mod scripts {
+ use crate::common::{in_test_worker, init_client, listen_for_completed_jobs};
+ use crate::job_debouncing::trigger_djob_for;
+ use std::time::Duration;
+ use tokio::time::sleep;
+ use tokio_stream::StreamExt;
+
+ /// 1. LLF and RLF create two djobs for flow at the same and fall into single debounce
+ #[cfg(feature = "python")]
+ #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ // TODO: Same test_but script fails.
+ async fn test_1(db: sqlx::Pool) -> anyhow::Result<()> {
+ // This tests if debouncing and consolidation works.
+ // Also makes sures that dependency job does not create new flow version
+ let (client, port, _s) = init_client(db.clone()).await;
+ let mut completed = listen_for_completed_jobs(&db).await;
+
+ // Verify lock is empty
+ {
+ assert_eq!(
+ sqlx::query_scalar!(
+ "SELECT lock FROM script WHERE path = 'f/dre_script/script'"
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap(),
+ Some("".into())
+ );
+ }
+
+ // Trigger both at the same time.
+ {
+ trigger_djob_for(
+ &client,
+ "f/dre_script/leaf_left",
+ "0000000000082398",
+ Some("#requirements:\n#bottle==0.13.2\ndef main():\npass".into()),
+ )
+ .await;
+ trigger_djob_for(
+ &client,
+ "f/dre_script/leaf_right",
+ "000000000008239B",
+ Some("#requirements:\n#tiny==0.1.3\ndef main():\npass".into()),
+ )
+ .await;
+ }
+
+ sleep(Duration::from_secs(1)).await;
+
+ in_test_worker(
+ &db,
+ async {
+ assert_eq!(
+ &sqlx::query_scalar!(
+ "SELECT runnable_path FROM v2_job WHERE id = $1",
+ completed.next().await.unwrap()
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "f/dre_script/leaf_left"
+ );
+
+ assert_eq!(
+ &sqlx::query_scalar!(
+ "SELECT runnable_path FROM v2_job WHERE id = $1",
+ completed.next().await.unwrap()
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "f/dre_script/leaf_right"
+ );
+
+ // handle.await.unwrap();
+
+ // Let jobs propagate
+
+ tokio::select!(
+ _ = async {
+ while sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job_queue WHERE running = false")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ == 0
+ {
+ sleep(Duration::from_secs(1)).await;
+ }
+ } => {},
+ _ = sleep(Duration::from_secs(60)) => { panic!("Timeout") }
+ );
+ // Verify there is only one queued job that is scheduled for atleast 3s ahead.
+ {
+ for r in
+ sqlx::query_scalar!("SELECT id FROM v2_job_queue WHERE running = false")
+ .fetch_all(&db)
+ .await
+ .unwrap()
+ {
+ dbg!(
+ sqlx::query!("SELECT runnable_path FROM v2_job WHERE id = $1", r)
+ .fetch_all(&db)
+ .await
+ .unwrap()
+ );
+ }
+ for r in sqlx::query_scalar!("SELECT id FROM v2_job_completed")
+ .fetch_all(&db)
+ .await
+ .unwrap()
+ {
+ dbg!(
+ sqlx::query!("SELECT runnable_path FROM v2_job WHERE id = $1", r)
+ .fetch_all(&db)
+ .await
+ .unwrap()
+ );
+ }
+
+ dbg!(sqlx::query!("SELECT runnable_path FROM v2_job")
+ .fetch_all(&db)
+ .await
+ .unwrap());
+
+ let q = sqlx::query_scalar!(
+ "SELECT (scheduled_for - created_at) FROM v2_job_queue WHERE running = false"
+ )
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ assert_eq!(1, q.len());
+ assert!(dbg!(q[0].unwrap().microseconds) > 1_000_000 /* 1 second */);
+ }
+
+ // Verify debounce_stale_data and debounce_key
+ {
+ let q = sqlx::query_scalar!("SELECT key FROM debounce_key")
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ assert_eq!(q.len(), 1);
+
+ assert_eq!(
+ q[0].clone(),
+ "test-workspace:f/dre_script/script:dependency".to_owned(),
+ );
+
+ // Stale data is empty for scripts
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM debounce_stale_data")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 0
+ );
+ }
+
+ // Wait until debounce delay is complete
+ // sleep(Duration::from_secs(6)).await;
+
+ assert_eq!(
+ &sqlx::query_scalar!(
+ "SELECT runnable_path FROM v2_job WHERE id = $1",
+ completed.next().await.unwrap()
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ "f/dre_script/script"
+ );
+ },
+ port,
+ )
+ .await;
+
+ // completed.next().await.unwrap();
+
+ // Verify
+ {
+ assert_eq!(
+ 0,
+ sqlx::query_scalar!("SELECT COUNT(*) from v2_job_queue")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ assert_eq!(
+ vec![533404],
+ dbg!(sqlx::query_scalar!(
+ "SELECT hash FROM script WHERE path = 'f/dre_script/script' AND archived = true"
+ )
+ .fetch_all(&db)
+ .await
+ .unwrap())
+ );
+
+ assert_ne!(
+ 533404,
+ sqlx::query_scalar!(
+ "SELECT hash FROM script WHERE path = 'f/dre_script/script' AND archived = false"
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ );
+
+ assert_eq!(
+ vec![533404],
+ sqlx::query_scalar!(
+ "SELECT parent_hashes FROM script WHERE path = 'f/dre_script/script' AND archived = false"
+ )
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ }
+
+ // Verify cleanup
+ {
+ assert_eq!(
+ 0,
+ sqlx::query_scalar!("SELECT COUNT(*) from debounce_key")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ assert_eq!(
+ 0,
+ sqlx::query_scalar!("SELECT COUNT(*) from debounce_stale_data")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap()
+ );
+ }
+
+ // handle.await.unwrap();
+ Ok(())
+ }
+
+ #[cfg(feature = "python")]
+ #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ async fn test_left(db: sqlx::Pool) -> anyhow::Result<()> {
+ use crate::common::RunJob;
+
+ // TODO: We don't care about timer. If there is no timer, it will be set automatically for djobs??
+ let (_client, port, _s) = init_client(db.clone()).await;
+ let mut completed = listen_for_completed_jobs(&db).await;
+
+ // Trigger both at the same time.
+ {
+ let mut args = std::collections::HashMap::new();
+ args.insert(
+ "dbg_djob_sleep".to_owned(),
+ // Execution should take this seconds
+ windmill_common::worker::to_raw_value(&20),
+ );
+
+ args.insert(
+ "triggered_by_relative_import".to_owned(),
+ // Execution should take this seconds
+ windmill_common::worker::to_raw_value(&()),
+ );
+
+ let (_flow_id, new_tx) = windmill_queue::push(
+ &db,
+ windmill_queue::PushIsolationLevel::IsolatedRoot(db.clone()),
+ "test-workspace",
+ windmill_common::jobs::JobPayload::Dependencies {
+ path: "f/dre_script/script".to_owned(),
+ dedicated_worker: None,
+ language: windmill_common::scripts::ScriptLang::Python3,
+ hash: 533404.into(),
+ },
+ windmill_queue::PushArgs { args: &args, extra: None },
+ "admin",
+ "admin@windmill.dev",
+ "admin".to_owned(),
+ Some("trigger.dependents.to.recompute.dependencies"),
+ // Debounce period
+ Some(chrono::Utc::now() + chrono::Duration::seconds(5)),
+ None,
+ None,
+ None,
+ None,
+ None,
+ false,
+ false,
+ None,
+ true,
+ Some("dependency".into()),
+ None,
+ None,
+ None,
+ None,
+ false,
+ None,
+ None,
+ )
+ .await
+ .unwrap();
+ new_tx.commit().await.unwrap();
+
+ let db2 = db.clone();
+ in_test_worker(
+ &db2,
+ async {
+ // This job should execute and then try to start another job that will get debounced.
+ RunJob::from(windmill_common::jobs::JobPayload::Dependencies {
+ path: "f/dre_script/leaf_right".to_owned(),
+ hash: 533403.into(),
+ language: windmill_common::scripts::ScriptLang::Python3,
+ dedicated_worker: None,
+ })
+ .run_until_complete(&db, port)
+ .await;
+
+ // This one is supposed to be started after flow djob has debounced and started but haven't finished yet.
+ RunJob::from(windmill_common::jobs::JobPayload::Dependencies {
+ path: "f/dre_script/leaf_left".to_owned(),
+ hash: 533400.into(),
+ language: windmill_common::scripts::ScriptLang::Python3,
+ dedicated_worker: None,
+ })
+ // So set it to this long
+ .arg("dbg_djob_sleep", serde_json::json!(10))
+ .run_until_complete(&db, port)
+ .await;
+
+ completed.next().await; // leaf_right
+ completed.next().await; // leaf_left
+ completed.next().await; // importer
+ completed.next().await; // importer
+ },
+ port,
+ )
+ .await;
+ }
+
+ assert_eq!(
+ sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job_queue")
+ .fetch_one(&db)
+ .await
+ .unwrap()
+ .unwrap(),
+ 0
+ );
+
+ let r = sqlx::query_scalar!("SELECT runnable_id FROM v2_job ORDER BY created_at DESC")
+ .fetch_all(&db)
+ .await
+ .unwrap();
+
+ assert_eq!(r.len(), 4);
+ assert!(r.contains(&Some(-221349019907577876)));
+ assert!(r.contains(&Some(533400)));
+ assert!(r.contains(&Some(533403)));
+ assert!(r.contains(&Some(533404)));
+
+ Ok(())
+ }
+
+ // // TODO: we don't need scripts to have concurrency limit
+ // /// 3. Same as second test, however first app djob will take longer than second debounce.
+ // /// NOTE: This test should be ran in debug mode. In release it will not work properly.
+ // #[cfg(all(feature = "python", feature = "private"))]
+ // #[sqlx::test(fixtures("base", "djob_debouncing"))]
+ // async fn test_3(db: sqlx::Pool) -> anyhow::Result<()> {
+ // // This tests checks if concurrency limit works correcly and there is no race conditions.
+ // let (client, port, _s) = init_client(db.clone()).await;
+ // let mut completed = listen_for_completed_jobs(&db).await;
+
+ // // At this point we should have two
+ // let mut job_ids = vec![];
+ // let push_job = |delay, db| async move {
+ // let mut args = std::collections::HashMap::new();
+ // args.insert(
+ // "dbg_djob_sleep".to_owned(),
+ // // First one will create delay for 5 seconds
+ // // The second will have no delay at all.
+ // windmill_common::worker::to_raw_value(&delay),
+ // );
+
+ // args.insert(
+ // "triggered_by_relative_import".to_string(),
+ // windmill_common::worker::to_raw_value(&()),
+ // );
+
+ // let (job_uuid, new_tx) = windmill_queue::push(
+ // &db,
+ // windmill_queue::PushIsolationLevel::IsolatedRoot(db.clone()),
+ // "test-workspace",
+ // windmill_common::jobs::JobPayload::Dependencies {
+ // path: "f/dre_script/script".to_owned(),
+ // language: windmill_common::scripts::ScriptLang::Python3,
+ // dedicated_worker: None,
+ // hash: windmill_common::scripts::ScriptHash(533404),
+ // },
+ // windmill_queue::PushArgs { args: &args, extra: None },
+ // "admin",
+ // "admin@windmill.dev",
+ // "admin".to_owned(),
+ // Some("trigger.dependents.to.recompute.dependencies"),
+ // // Schedule for now.
+ // Some(chrono::Utc::now()),
+ // None,
+ // None,
+ // None,
+ // None,
+ // None,
+ // false,
+ // false,
+ // None,
+ // true,
+ // Some("dependency".into()),
+ // None,
+ // None,
+ // None,
+ // None,
+ // false,
+ // None,
+ // None,
+ // )
+ // .await
+ // .unwrap();
+
+ // new_tx.commit().await.unwrap();
+
+ // job_uuid
+ // };
+
+ // // Push first
+ // job_ids.push(push_job(5, db.clone()).await);
+ // sleep(Duration::from_millis(300)).await;
+
+ // // Verify debounce_stale_data and debounce_key
+ // {
+ // let q = sqlx::query_scalar!("SELECT key FROM debounce_key")
+ // .fetch_all(&db)
+ // .await
+ // .unwrap();
+
+ // assert_eq!(q.len(), 1);
+
+ // assert_eq!(
+ // q[0].clone(),
+ // "test-workspace:f/dre_script/script:dependency".to_owned(),
+ // );
+
+ // // Stale data is empty for scripts
+ // assert_eq!(
+ // sqlx::query_scalar!("SELECT COUNT(*) FROM debounce_stale_data")
+ // .fetch_one(&db)
+ // .await
+ // .unwrap()
+ // .unwrap(),
+ // 0
+ // );
+ // }
+
+ // // Start the first one in the background
+ // let handle = {
+ // let mut completed = listen_for_completed_jobs(&db).await;
+ // let db2 = db.clone();
+ // tokio::spawn(async move {
+ // in_test_worker(
+ // &db2,
+ // // sleep(Duration::from_secs(7)),
+ // completed.next(), // Only wait for the single job. We are going to spawn another worker for second one.
+ // port,
+ // )
+ // .await;
+ // })
+ // };
+
+ // // Wait for the job to be created and started
+ // // This way next job is not going to be consumed by the first one.
+ // sleep(Duration::from_secs(1)).await;
+
+ // // Push second
+ // job_ids.push(push_job(0, db.clone()).await);
+
+ // // Wait for the second one to finish in separate worker.
+ // in_test_worker(
+ // &db,
+ // async {
+ // // First job will be pulled
+ // completed.next().await;
+ // // However since we have concurrency limit enabled it will get rescheduled by creation of new djob.
+ // // So we have to wait for that one as well.
+ // completed.next().await;
+ // },
+ // port,
+ // )
+ // .await;
+
+ // // Wait for the first one
+ // handle.await.unwrap();
+
+ // // Verify that we have expected outcome
+ // {
+ // assert_eq!(
+ // sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job_queue",)
+ // .fetch_one(&db)
+ // .await
+ // .unwrap()
+ // .unwrap(),
+ // 0
+ // );
+ // // Verify lock
+ // {
+ // assert_eq!(
+ // sqlx::query_scalar!(
+ // "SELECT lock FROM script WHERE path = 'f/dre_script/script'"
+ // )
+ // .fetch_one(&db)
+ // .await
+ // .unwrap(),
+ // Some("# py: 3.11\nbottle==0.13.2\ntiny==0.1.3".into())
+ // );
+ // }
+ // assert_eq!(
+ // sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job",)
+ // .fetch_one(&db)
+ // .await
+ // .unwrap()
+ // .unwrap(),
+ // 2
+ // );
+
+ // assert_eq!(
+ // sqlx::query_scalar!("SELECT COUNT(*) FROM v2_job_completed",)
+ // .fetch_one(&db)
+ // .await
+ // .unwrap()
+ // .unwrap(),
+ // 2
+ // );
+ // // Check that two jobs were executed sequentially
+ // assert!(sqlx::query_scalar!(
+ // "
+ // SELECT
+ // j1.completed_at < j2.started_at
+ // FROM
+ // v2_job_completed j1,
+ // v2_job_completed j2
+ // WHERE
+ // j1.id = $1
+ // AND j2.id = $2",
+ // job_ids[0],
+ // job_ids[1],
+ // )
+ // .fetch_one(&db)
+ // .await
+ // .unwrap()
+ // .unwrap());
+ // }
+ // Ok(())
+ // }
+ }
+ // TODO: Test git sync
+}
diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs
index 362df943a0..72d753c48c 100644
--- a/backend/tests/worker.rs
+++ b/backend/tests/worker.rs
@@ -20,7 +20,6 @@ use windmill_common::flows::InputTransform;
#[cfg(any(feature = "python", feature = "deno_core"))]
use windmill_common::flow_status::RestartedFrom;
-
use windmill_common::{
flows::FlowValue,
jobs::{JobPayload, RawCode},
@@ -31,6 +30,8 @@ use common::*;
#[cfg(feature = "enterprise")]
use futures::StreamExt;
+use windmill_common::flows::FlowModule;
+use windmill_common::flows::FlowModuleValue;
// async fn _print_job(id: Uuid, db: &Pool) -> Result<(), anyhow::Error> {
// tracing::info!(
@@ -332,9 +333,6 @@ async fn test_identity(db: Pool) -> anyhow::Result<()> {
Ok(())
}
-use windmill_common::flows::FlowModule;
-use windmill_common::flows::FlowModuleValue;
-
#[cfg(feature = "deno_core")]
#[sqlx::test(fixtures("base"))]
async fn test_deno_flow_same_worker(db: Pool) -> anyhow::Result<()> {
@@ -2083,14 +2081,14 @@ async fn test_flow_lock_all(db: Pool) -> anyhow::Result<()> {
language: windmill_api_client::types::RawScriptLanguage::Bash,
lock: Some(ref lock),
..
- }) if lock == "")
+ }) if lock.is_empty())
|| matches!(
m.value,
windmill_api_client::types::FlowModuleValue::RawScript(RawScript{
language: windmill_api_client::types::RawScriptLanguage::Go | windmill_api_client::types::RawScriptLanguage::Python3 | windmill_api_client::types::RawScriptLanguage::Deno,
lock: Some(ref lock),
..
- }) if lock.len() > 0),
+ }) if !lock.is_empty()),
"{:?}", m.value
);
});
@@ -2749,7 +2747,7 @@ async fn test_result_format(db: Pool) -> anyhow::Result<()> {
assert_eq!(job_result.get(), correct_result);
let response = windmill_api::jobs::run_wait_result(
- &db.into(),
+ &db,
Uuid::parse_str(ordered_result_job_id).unwrap(),
"test-workspace".to_string(),
None,
@@ -2760,8 +2758,7 @@ async fn test_result_format(db: Pool) -> anyhow::Result<()> {
let result: Box = serde_json::from_slice(
&axum::body::to_bytes(response.into_body(), usize::MAX)
.await
- .unwrap()
- .to_vec(),
+ .unwrap(),
)
.unwrap();
assert_eq!(result.get(), correct_result);
@@ -2805,7 +2802,7 @@ async fn test_job_labels(db: Pool) -> anyhow::Result<()> {
restarted_from: None,
})
.arg("world", json!("you"))
- .run_until_complete_with(&db, port, |id| async move {
+ .run_until_complete_with(db, port, |id| async move {
sqlx::query!(
"UPDATE v2_job SET labels = $2 WHERE id = $1 AND $2::TEXT[] IS NOT NULL",
id,
@@ -2871,7 +2868,7 @@ async fn test_workflow_as_code(db: Pool) -> anyhow::Result<()> {
// workflow as code require at least 2 workers:
let db = &db;
in_test_worker(
- &db,
+ db,
async move {
let job = RunJob::from(JobPayload::Code(RawCode {
language: ScriptLang::Python3,
@@ -2879,7 +2876,7 @@ async fn test_workflow_as_code(db: Pool) -> anyhow::Result<()> {
..RawCode::default()
}))
.arg("n", json!(3))
- .run_until_complete(&db, port)
+ .run_until_complete(db, port)
.await;
assert_eq!(job.json_result().unwrap(), json!(["OK", 3]));
diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs
index 15a89d762f..1cd0750919 100644
--- a/backend/windmill-api/src/apps.rs
+++ b/backend/windmill-api/src/apps.rs
@@ -60,7 +60,7 @@ use windmill_common::{
users::username_to_permissioned_as,
utils::{
http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, require_admin,
- Pagination, RunnableKind, StripPath,
+ Pagination, RunnableKind, StripPath, WarnAfterExt,
},
variables::{build_crypt, build_crypt_with_key_suffix, encrypt},
worker::{to_raw_value, CLOUD_HOSTED},
@@ -1240,6 +1240,7 @@ async fn create_app_internal<'a>(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
@@ -1523,6 +1524,14 @@ async fn update_app_internal<'a>(
path.to_owned()
};
let v_id = if let Some(nvalue) = &ns.value {
+ // Row lock debounce key for path. We need this to make all updates of runnables sequential and predictable.
+ tokio::time::timeout(
+ core::time::Duration::from_secs(60),
+ windmill_common::jobs::lock_debounce_key(&w_id, &npath, &mut tx),
+ )
+ .warn_after_seconds(10)
+ .await??;
+
let app_id = sqlx::query_scalar!(
"SELECT id FROM app WHERE path = $1 AND workspace_id = $2",
npath,
@@ -1620,6 +1629,7 @@ async fn update_app_internal<'a>(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
tracing::info!("Pushed app dependency job {}", dependency_job_uuid);
@@ -1938,6 +1948,7 @@ async fn execute_component(
None,
false,
end_user_email,
+ None,
)
.await?;
tx.commit().await?;
diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs
index c983fb8d1f..4d1e7aecc1 100644
--- a/backend/windmill-api/src/flows.rs
+++ b/backend/windmill-api/src/flows.rs
@@ -32,7 +32,7 @@ use sql_builder::prelude::*;
use sqlx::{FromRow, Postgres, Transaction};
use windmill_audit::audit_oss::audit_log;
use windmill_audit::ActionKind;
-use windmill_common::utils::query_elems_from_hub;
+use windmill_common::utils::{query_elems_from_hub, WarnAfterExt};
use windmill_common::worker::{to_raw_value, CLOUD_HOSTED};
use windmill_common::HUB_BASE_URL;
use windmill_common::{
@@ -546,6 +546,7 @@ async fn create_flow(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
@@ -884,6 +885,15 @@ async fn update_flow(
.await?;
}
+ // Row lock debounce key for path. We need this to make all updates of runnables sequential and predictable.
+ tokio::time::timeout(
+ core::time::Duration::from_secs(60),
+ windmill_common::jobs::lock_debounce_key(&w_id, &nf.path, &mut tx),
+ )
+ .warn_after_seconds(10)
+ .await??;
+
+ // This will lock anyone who is trying to iterate on flow_versions with given path and parameters.
let version = sqlx::query_scalar!(
"INSERT INTO flow_version (workspace_id, path, value, schema, created_by) VALUES ($1, $2, $3, $4::text::json, $5) RETURNING id",
w_id,
@@ -900,6 +910,7 @@ async fn update_flow(
))
})?;
+ // TODO: This should happen only after we are done with dependency job.
sqlx::query!(
"UPDATE flow SET versions = array_append(versions, $1) WHERE path = $2 AND workspace_id = $3",
version, nf.path, w_id
@@ -1014,8 +1025,10 @@ async fn update_flow(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
+
sqlx::query!(
"UPDATE flow SET dependency_job = $1 WHERE path = $2 AND workspace_id = $3",
dependency_job_uuid,
diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs
index 47c3260fc1..e54a61df9f 100644
--- a/backend/windmill-api/src/jobs.rs
+++ b/backend/windmill-api/src/jobs.rs
@@ -4101,6 +4101,7 @@ pub async fn run_flow_by_path_inner(
push_authed.as_ref(),
false,
None,
+ None,
)
.await?;
@@ -4217,6 +4218,7 @@ pub async fn restart_flow(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -4320,6 +4322,7 @@ pub async fn run_script_by_path_inner(
push_authed.as_ref(),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -4473,6 +4476,7 @@ pub async fn run_workflow_as_code(
push_authed.as_ref(),
false,
None,
+ None,
)
.await?;
@@ -5017,6 +5021,7 @@ pub async fn run_wait_result_job_by_path_get(
push_authed.as_ref(),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -5170,6 +5175,7 @@ pub async fn run_wait_result_script_by_path_internal(
push_authed.as_ref(),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -5287,6 +5293,7 @@ pub async fn run_wait_result_script_by_hash(
push_authed.as_ref(),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -5599,6 +5606,7 @@ pub async fn run_wait_result_flow_by_path_internal(
push_authed.as_ref(),
false,
None,
+ None,
)
.await?;
@@ -5690,6 +5698,7 @@ async fn run_preview_script(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -5807,6 +5816,7 @@ async fn run_bundle_preview_script(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
job_id = Some(uuid);
@@ -5945,6 +5955,7 @@ async fn run_dependencies_job(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -6013,6 +6024,7 @@ async fn run_flow_dependencies_job(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -6357,6 +6369,7 @@ async fn run_preview_flow_job(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -6531,6 +6544,7 @@ async fn run_dynamic_select(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -6659,6 +6673,7 @@ pub async fn run_job_by_hash_inner(
push_authed.as_ref(),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs
index 56107f055e..394e5e62d2 100644
--- a/backend/windmill-api/src/scripts.rs
+++ b/backend/windmill-api/src/scripts.rs
@@ -769,6 +769,14 @@ async fn create_script_internal<'c>(
}
};
+ // Row lock debounce key for path. We need this to make all updates of runnables sequential and predictable.
+ tokio::time::timeout(
+ core::time::Duration::from_secs(60),
+ windmill_common::jobs::lock_debounce_key(&w_id, &ns.path, &mut tx),
+ )
+ .warn_after_seconds(10)
+ .await??;
+
sqlx::query!(
"INSERT INTO script (workspace_id, hash, path, parent_hashes, summary, description, \
content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, \
@@ -817,6 +825,7 @@ async fn create_script_internal<'c>(
)
.execute(&mut *tx)
.await?;
+
let p_path_opt = parent_hashes_and_perms.as_ref().map(|x| x.p_path.clone());
if let Some(ref p_path) = p_path_opt {
sqlx::query!(
@@ -1000,6 +1009,7 @@ async fn create_script_internal<'c>(
Some(&authed.clone().into()),
false,
None,
+ None,
)
.await?;
Ok((hash, new_tx, None))
diff --git a/backend/windmill-api/src/triggers/trigger_helpers.rs b/backend/windmill-api/src/triggers/trigger_helpers.rs
index 5da92541bb..993f458343 100644
--- a/backend/windmill-api/src/triggers/trigger_helpers.rs
+++ b/backend/windmill-api/src/triggers/trigger_helpers.rs
@@ -861,6 +861,7 @@ async fn trigger_script_with_retry_and_error_handler(
push_authed.as_ref(),
false,
None,
+ None,
)
.await?;
tx.commit().await?;
diff --git a/backend/windmill-common/src/error.rs b/backend/windmill-common/src/error.rs
index 1c41bdc9b5..d9644fbba9 100644
--- a/backend/windmill-common/src/error.rs
+++ b/backend/windmill-common/src/error.rs
@@ -188,6 +188,12 @@ impl From for Error {
}
}
+impl From for Error {
+ fn from(value: tokio::time::error::Elapsed) -> Self {
+ Self::InternalErr(value.to_string())
+ }
+}
+
impl Error {
/// https://docs.rs/anyhow/1/anyhow/struct.Error.html#display-representations
pub fn alt(&self) -> String {
diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs
index 4608fe72b3..edb9f1f0cf 100644
--- a/backend/windmill-common/src/jobs.rs
+++ b/backend/windmill-common/src/jobs.rs
@@ -327,6 +327,7 @@ pub enum JobPayload {
path: String,
apply_preprocessor: bool,
},
+
ScriptHash {
hash: ScriptHash,
path: String,
@@ -339,51 +340,71 @@ pub enum JobPayload {
priority: Option,
apply_preprocessor: bool,
},
+
FlowScript {
id: FlowNodeId, // flow_node(id).
language: ScriptLang,
+ /// Override default concurrency key
custom_concurrency_key: Option,
+ /// How many jobs can run at the same time
concurrent_limit: Option,
+ /// In seconds
concurrency_time_window_s: Option,
cache_ttl: Option,
dedicated_worker: Option,
path: String,
},
+
FlowNode {
id: FlowNodeId, // flow_node(id).
path: String, // flow node inner path (e.g. `outer/branchall-42`).
},
+
AppScript {
id: AppScriptId, // app_script(id).
path: Option,
language: ScriptLang,
cache_ttl: Option,
},
+
Code(RawCode),
+
+ /// Script Dependency Job
Dependencies {
path: String,
hash: ScriptHash,
language: ScriptLang,
dedicated_worker: Option,
},
+
+ /// Flow Dependency Job
FlowDependencies {
path: String,
dedicated_worker: Option,
version: i64,
},
+
+ /// App Dependency Job
AppDependencies {
path: String,
version: i64,
},
+
+ /// Flow Dependency Job, exposed with API. Requirements can be partially or fully predefined
RawFlowDependencies {
path: String,
flow_value: FlowValue,
},
+
+ /// Dependency Job, exposed with API. Requirements can be predefined
RawScriptDependencies {
script_path: String,
+ /// Will reflect raw requirements content (e.g. requirements.txt)
content: String,
language: ScriptLang,
},
+
+ /// Flow Job
Flow {
path: String,
dedicated_worker: Option,
@@ -400,6 +421,8 @@ pub enum JobPayload {
path: Option,
restarted_from: Option,
},
+
+ /// Flow consisting of single script
SingleStepFlow {
path: String,
hash: Option,
@@ -545,7 +568,7 @@ pub async fn script_path_to_payload<'e>(
custom_concurrency_key: concurrency_key,
concurrent_limit,
concurrency_time_window_s,
- cache_ttl: cache_ttl,
+ cache_ttl,
language,
dedicated_worker,
priority,
@@ -775,3 +798,26 @@ pub async fn check_tag_available_for_workspace_internal(
return Ok(());
}
+
+pub async fn lock_debounce_key<'c>(
+ w_id: &str,
+ runnable_path: &str,
+ tx: &mut sqlx::Transaction<'c, sqlx::Postgres>,
+) -> error::Result> {
+ let key = format!("{w_id}:{runnable_path}:dependency");
+
+ tracing::debug!(
+ workspace_id = %w_id,
+ runnable_path = %runnable_path,
+ debounce_key = %key,
+ "Locking debounce_key for dependency job scheduling"
+ );
+
+ sqlx::query_scalar!(
+ "SELECT job_id FROM debounce_key WHERE key = $1 FOR UPDATE",
+ &key
+ )
+ .fetch_optional(&mut **tx)
+ .await
+ .map_err(error::Error::from)
+}
diff --git a/backend/windmill-common/src/scripts.rs b/backend/windmill-common/src/scripts.rs
index 32ed0c3c2d..9c1af245d8 100644
--- a/backend/windmill-common/src/scripts.rs
+++ b/backend/windmill-common/src/scripts.rs
@@ -9,6 +9,7 @@
use std::{
fmt::{self, Display},
hash::{Hash, Hasher},
+ ops::Deref,
str::FromStr,
};
@@ -131,6 +132,13 @@ impl FromStr for ScriptLang {
#[sqlx(transparent)]
pub struct ScriptHash(pub i64);
+impl Deref for ScriptHash {
+ type Target = i64;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
impl Into for ScriptHash {
fn into(self) -> u64 {
self.0 as u64
@@ -217,7 +225,10 @@ const PREVIEW_IS_ESM_CODEBASE_HASH: i64 = -44;
const PREVIEW_IS_TAR_ESM_CODEBASE_HASH: i64 = -45;
pub fn is_special_codebase_hash(hash: i64) -> bool {
- hash == PREVIEW_IS_CODEBASE_HASH || hash == PREVIEW_IS_TAR_CODEBASE_HASH || hash == PREVIEW_IS_ESM_CODEBASE_HASH || hash == PREVIEW_IS_TAR_ESM_CODEBASE_HASH
+ hash == PREVIEW_IS_CODEBASE_HASH
+ || hash == PREVIEW_IS_TAR_CODEBASE_HASH
+ || hash == PREVIEW_IS_ESM_CODEBASE_HASH
+ || hash == PREVIEW_IS_TAR_ESM_CODEBASE_HASH
}
pub fn codebase_to_hash(is_tar: bool, is_esm: bool) -> i64 {
@@ -236,7 +247,6 @@ pub fn codebase_to_hash(is_tar: bool, is_esm: bool) -> i64 {
}
}
-
pub fn hash_to_codebase_id(job_id: &str, hash: i64) -> Option {
match hash {
PREVIEW_IS_CODEBASE_HASH => Some(job_id.to_string()),
@@ -247,7 +257,6 @@ pub fn hash_to_codebase_id(job_id: &str, hash: i64) -> Option {
}
}
-
pub struct CodebaseInfo {
pub is_tar: bool,
pub is_esm: bool,
@@ -714,3 +723,91 @@ pub fn hash_script(ns: &NewScript) -> i64 {
ns.hash(&mut dh);
dh.finish() as i64
}
+
+pub async fn clone_script<'c>(
+ base_hash: ScriptHash,
+ w_id: &str,
+ deployment_message: Option,
+ tx: &mut sqlx::Transaction<'c, sqlx::Postgres>,
+) -> crate::error::Result {
+ let s =
+ sqlx::query_as::<_, Script>("SELECT * FROM script WHERE hash = $1 AND workspace_id = $2")
+ .bind(base_hash.0)
+ .bind(w_id)
+ .fetch_one(&mut **tx)
+ .await?;
+
+ let ns = NewScript {
+ path: s.path.clone(),
+ parent_hash: Some(base_hash),
+ summary: s.summary,
+ description: s.description,
+ content: s.content,
+ schema: s.schema,
+ is_template: Some(s.is_template),
+ // TODO: Make it either None everywhere (particularly when raw reqs are calculated)
+ // Or handle this case and conditionally make Some (only with raw reqs)
+ lock: None,
+ language: s.language,
+ kind: Some(s.kind),
+ tag: s.tag,
+ draft_only: s.draft_only,
+ envs: s.envs,
+ concurrent_limit: s.concurrent_limit,
+ concurrency_time_window_s: s.concurrency_time_window_s,
+ cache_ttl: s.cache_ttl,
+ dedicated_worker: s.dedicated_worker,
+ ws_error_handler_muted: s.ws_error_handler_muted,
+ priority: s.priority,
+ timeout: s.timeout,
+ delete_after_use: s.delete_after_use,
+ restart_unless_cancelled: s.restart_unless_cancelled,
+ deployment_message,
+ concurrency_key: s.concurrency_key,
+ visible_to_runner_only: s.visible_to_runner_only,
+ no_main_func: s.no_main_func,
+ codebase: s.codebase,
+ has_preprocessor: s.has_preprocessor,
+ on_behalf_of_email: s.on_behalf_of_email,
+ assets: s.assets,
+ };
+
+ let new_hash = hash_script(&ns);
+
+ tracing::debug!(
+ "cloning script at path {} from '{}' to '{}'",
+ s.path,
+ *base_hash,
+ new_hash
+ );
+
+ sqlx::query!("
+ INSERT INTO script
+ (workspace_id, hash, path, parent_hashes, summary, description, content, \
+ created_by, schema, is_template, extra_perms, lock, language, kind, tag, \
+ draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
+ dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \
+ delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, \
+ codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets)
+
+ SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, \
+ content, created_by, schema, is_template, extra_perms, NULL, language, kind, tag, \
+ draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
+ dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \
+ delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, \
+ codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets
+
+ FROM script WHERE hash = $2 AND workspace_id = $3;
+ ", new_hash, base_hash.0, w_id).execute(&mut **tx).await?;
+
+ // Archive base.
+ sqlx::query!(
+ "UPDATE script SET archived = true WHERE hash = $1 AND workspace_id = $2",
+ *base_hash,
+ w_id
+ )
+ .execute(&mut **tx)
+ .await?;
+
+ Ok(new_hash)
+}
diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs
index 5cec1ac6f2..8dd2f4d3f8 100644
--- a/backend/windmill-queue/src/jobs.rs
+++ b/backend/windmill-queue/src/jobs.rs
@@ -111,6 +111,9 @@ lazy_static::lazy_static! {
.ok()
.and_then(|x| x.parse().ok())
.unwrap_or(false);
+
+ // TODO: Remove
+ static ref WMDEBUG_NO_DJOB_DEBOUNCING: bool = std::env::var("WMDEBUG_NO_DJOB_DEBOUNCING").is_ok();
}
#[cfg(feature = "cloud")]
@@ -450,6 +453,7 @@ pub async fn push_init_job<'c>(
None,
false,
None,
+ None,
)
.await?;
inner_tx.commit().await?;
@@ -505,6 +509,7 @@ pub async fn push_periodic_bash_job<'c>(
None,
false,
None,
+ None,
)
.await?;
inner_tx.commit().await?;
@@ -1401,6 +1406,7 @@ async fn restart_job_if_perpetual_inner(
None,
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -1889,6 +1895,7 @@ pub async fn push_error_handler<'a, 'c, T: Serialize + Send + Sync>(
None,
false,
None,
+ None,
)
.await?;
tx.commit().await?;
@@ -2180,6 +2187,12 @@ impl std::ops::Deref for PulledJob {
}
}
+impl std::ops::DerefMut for PulledJob {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.job
+ }
+}
+
lazy_static::lazy_static! {
pub static ref DISABLE_CONCURRENCY_LIMIT: bool = std::env::var("DISABLE_CONCURRENCY_LIMIT").is_ok_and(|s| s == "true");
}
@@ -2270,10 +2283,13 @@ impl PulledJobResult {
}
}
+/// Pull the job from queue
pub async fn pull(
db: &Pool,
+ // Whether or not try to pull from suspended jobs first
suspend_first: bool,
worker_name: &str,
+ // Execute queries supplied by caller instead of generic one
query_o: Option<&(String, String)>,
#[cfg(feature = "benchmark")] bench: &mut BenchmarkIter,
) -> windmill_common::error::Result {
@@ -2292,6 +2308,7 @@ pub async fn pull(
missing_concurrency_key: false,
});
}
+
if let Some((query_suspended, query_no_suspend)) = query_o {
let njob = {
let job = if query_suspended.is_empty() {
@@ -2313,9 +2330,13 @@ pub async fn pull(
(job, false)
};
- #[cfg(all(feature = "enterprise", feature = "private"))]
let pulled_job_result = match job {
- Some(job) if job.concurrent_limit.is_some() => {
+ #[cfg(feature = "private")]
+ Some(job)
+ if job.concurrent_limit.is_some()
+ // Concurrency limit is available for either enterprise job or dependency job
+ && (cfg!(feature = "enterprise") || (job.is_dependency() && !*WMDEBUG_NO_DJOB_DEBOUNCING)) =>
+ {
let job = crate::jobs_ee::apply_concurrency_limit(
db,
pull_loop_count,
@@ -2332,10 +2353,6 @@ pub async fn pull(
_ => PulledJobResult { job, suspended, missing_concurrency_key: false },
};
- #[cfg(not(all(feature = "enterprise", feature = "private")))]
- let pulled_job_result =
- PulledJobResult { job, suspended, missing_concurrency_key: false };
-
Ok::<_, Error>(pulled_job_result)
}?;
@@ -2364,6 +2381,7 @@ pub async fn pull(
}
return Ok(njob);
};
+
let (job, suspended) = pull_single_job_and_mark_as_running_no_concurrency_limit(
db,
suspend_first,
@@ -2372,7 +2390,6 @@ pub async fn pull(
bench,
)
.await?;
-
let Some(job) = job else {
return Ok(PulledJobResult { job: None, suspended, missing_concurrency_key: false });
};
@@ -2380,12 +2397,14 @@ pub async fn pull(
let has_concurent_limit = job.concurrent_limit.is_some();
#[cfg(not(feature = "enterprise"))]
- if has_concurent_limit {
+ if has_concurent_limit && !job.is_dependency() {
tracing::error!("Concurrent limits are an EE feature only, ignoring constraints")
}
#[cfg(not(feature = "enterprise"))]
- let has_concurent_limit = false;
+ let has_concurent_limit = false
+ || (job.is_dependency() && cfg!(feature = "private") && !*WMDEBUG_NO_DJOB_DEBOUNCING);
+ // if we don't have private flag, we don't have concurrency limit
// concurrency check. If more than X jobs for this path are already running, we re-queue and pull another job from the queue
let pulled_job = job;
@@ -2404,12 +2423,16 @@ pub async fn pull(
});
}
- #[cfg(all(feature = "enterprise", feature = "private"))]
- if let Some(pulled_job) =
- crate::jobs_ee::apply_concurrency_limit(db, pull_loop_count, suspended, pulled_job)
- .await?
+ #[cfg(feature = "private")]
+ if cfg!(feature = "enterprise")
+ || (pulled_job.is_dependency() && !*WMDEBUG_NO_DJOB_DEBOUNCING)
{
- return Ok(pulled_job);
+ if let Some(pulled_job) =
+ crate::jobs_ee::apply_concurrency_limit(db, pull_loop_count, suspended, pulled_job)
+ .await?
+ {
+ return Ok(pulled_job);
+ }
}
}
}
@@ -2444,6 +2467,7 @@ async fn pull_single_job_and_mark_as_running_no_concurrency_limit<'c>(
} else {
None
};
+
if r.is_none() {
// #[cfg(feature = "benchmark")]
// let instant = Instant::now();
@@ -2531,6 +2555,69 @@ pub async fn concurrency_key(
})
}
+pub async fn custom_debounce_key(
+ db: &Pool,
+ job_id: &Uuid,
+) -> Result, sqlx::Error> {
+ let fut = async || {
+ sqlx::query_scalar!("SELECT key FROM debounce_key WHERE job_id = $1", job_id)
+ .fetch_optional(db) // this should no longer be fetch optional
+ .await
+ };
+ fut.retry(
+ ConstantBuilder::default()
+ .with_delay(std::time::Duration::from_secs(3))
+ .with_max_times(5)
+ .build(),
+ )
+ .notify(|err, dur| {
+ tracing::error!(
+ "Could not get debounce key for job {job_id}, retrying in {dur:#?}, err: {err:#?}"
+ );
+ })
+ .await
+}
+
+/// Helper function to extract nodes/components to relock from job arguments
+/// Returns the list of nodes to relock if present in either nodes_to_relock (flows) or components_to_relock (apps)
+fn extract_to_relock_from_args(args: &HashMap>) -> Option> {
+ args.get("nodes_to_relock") // For flows
+ .or(args.get("components_to_relock")) // For apps
+ .and_then(|rv| {
+ serde_json::from_str::>(&rv.to_string())
+ .map_err(|e| tracing::warn!("Failed to deserialize relock data: {}", e))
+ .ok()
+ })
+}
+
+/// Helper function to accumulate nodes/components to relock for a debounced job
+/// This merges new items with existing ones, removing duplicates
+async fn accumulate_debounce_stale_data(
+ tx: &mut Transaction<'_, Postgres>,
+ job_id: &Uuid,
+ to_relock: &[String],
+) -> Result<(), Error> {
+ sqlx::query!(
+ "
+ INSERT INTO debounce_stale_data (job_id, to_relock)
+ VALUES ($1, $2)
+ ON CONFLICT (job_id)
+ DO UPDATE SET to_relock = (
+ SELECT array_agg(DISTINCT x)
+ FROM unnest(
+ -- Combine existing array with new values, removing duplicates
+ array_cat(debounce_stale_data.to_relock, EXCLUDED.to_relock)
+ ) AS x
+ )
+ ",
+ job_id,
+ to_relock
+ )
+ .execute(&mut **tx)
+ .await?;
+ Ok(())
+}
+
pub fn interpolate_args(x: String, args: &PushArgs, workspace_id: &str) -> String {
// Save this value to avoid parsing twice
let workspaced = x.as_str().replace("$workspace", workspace_id).to_string();
@@ -3183,6 +3270,8 @@ pub async fn push<'c, 'd>(
authed: Option<&Authed>,
running: bool, // whether the job is already running: only set this to true if you don't want the job to be picked up by a worker from the queue. It will also set started_at to now.
end_user_email: Option,
+ // If we know there is already a debounce job, we can use this for debouncing.
+ debounce_job_id_o: Option,
) -> Result<(Uuid, Transaction<'c, Postgres>), Error> {
#[cfg(feature = "cloud")]
if *CLOUD_HOSTED {
@@ -3383,8 +3472,8 @@ pub async fn push<'c, 'd>(
raw_flow,
flow_status,
language,
- custom_concurrency_key,
- concurrent_limit,
+ mut custom_concurrency_key,
+ mut concurrent_limit,
concurrency_time_window_s,
cache_ttl,
dedicated_worker,
@@ -3555,7 +3644,7 @@ pub async fn push<'c, 'd>(
),
JobPayload::Dependencies { hash, language, path, dedicated_worker } => (
Some(hash.0),
- Some(path),
+ Some(path.clone()),
None,
JobKind::Dependencies,
None,
@@ -3568,6 +3657,8 @@ pub async fn push<'c, 'd>(
dedicated_worker,
None,
),
+
+ // CLI usage, is not modifying db, no need for debouncing.
JobPayload::RawScriptDependencies { script_path, content, language } => (
None,
Some(script_path),
@@ -3583,6 +3674,8 @@ pub async fn push<'c, 'd>(
None,
None,
),
+
+ // CLI usage, is not modifying db, no need for debouncing.
JobPayload::RawFlowDependencies { path, flow_value } => (
None,
Some(path),
@@ -3599,9 +3692,17 @@ pub async fn push<'c, 'd>(
None,
),
JobPayload::FlowDependencies { path, dedicated_worker, version } => {
+ #[cfg(test)]
+ let skip_compat = args
+ .args
+ .contains_key("dbg_create_job_for_unexistant_flow_version");
+
+ #[cfg(not(test))]
+ let skip_compat = false;
+
// Keep inserting `value` if not all workers are updated.
// Starting at `v1.440`, the value is fetched on pull from the version id.
- let value_o = if !*MIN_VERSION_IS_AT_LEAST_1_440.read().await {
+ let value_o = if !*MIN_VERSION_IS_AT_LEAST_1_440.read().await && !skip_compat {
let mut ntx = tx.into_tx().await?;
// The version has been inserted only within the transaction.
let data = cache::flow::fetch_version(&mut *ntx, version).await?;
@@ -3613,7 +3714,7 @@ pub async fn push<'c, 'd>(
};
(
Some(version),
- Some(path),
+ Some(path.clone()),
None,
JobKind::FlowDependencies,
value_o,
@@ -3629,7 +3730,7 @@ pub async fn push<'c, 'd>(
}
JobPayload::AppDependencies { path, version } => (
Some(version),
- Some(path),
+ Some(path.clone()),
None,
JobKind::AppDependencies,
None,
@@ -4088,6 +4189,19 @@ pub async fn push<'c, 'd>(
),
};
+ // Enforce concurrency limit on all dependency jobs.
+ // TODO: We can ignore this for scripts djobs. The main reason we need all djobs to be sequential is because we have
+ // nodes_to_relock and we need all locks whose corresponding steps aren't in nodes_to_relock be already present.
+ //
+ // This is not the case for scripts, so we can potentially have multiple djobs for scripts at the same time.
+ if let (Some(path), true) = (
+ &script_path,
+ cfg!(feature = "private") && job_kind.is_dependency() && !*WMDEBUG_NO_DJOB_DEBOUNCING,
+ ) {
+ custom_concurrency_key = Some(format!("dependency:{workspace_id}/{path}"));
+ concurrent_limit = Some(1);
+ }
+
let final_priority: Option;
#[cfg(not(feature = "enterprise"))]
{
@@ -4218,6 +4332,184 @@ pub async fn push<'c, 'd>(
Ulid::new().into()
};
+ // Dependency job debouncing: When multiple dependency jobs are scheduled for the same script/flow/app,
+ // we want to deduplicate them to avoid redundant work. The debouncing mechanism works by:
+ // 1. Creating a unique debounce key for each dependency target (dependency:workspace/type/path)
+ // 2. Reusing existing jobs when possible, or creating new ones when the existing job is already running
+ // 3. Accumulating the nodes/components that need relocking across all debounced requests
+ match (
+ scheduled_for_o.is_some(),
+ job_kind.is_dependency(),
+ script_path.clone(),
+ *WMDEBUG_NO_DJOB_DEBOUNCING,
+ // We only do debouncing for jobs triggered by relative imports
+ // We do not want this be the case for normal djobs, since they will always be sequential.
+ args.args.contains_key("triggered_by_relative_import"),
+ ) {
+ // === DEPENDENCY JOB DEBOUNCING ===
+ //
+ // Debouncing consolidates multiple dependency job requests into a single execution,
+ // reducing redundant work when many scripts/flows/apps are updated simultaneously.
+ //
+ // Prerequisites for debouncing (all must be true):
+ // 1. Job is scheduled in the future (debounce_delay is not None) - provides consolidation window
+ // 2. Job is a dependency job
+ // 3. Object path is provided (script/flow/app path)
+ // 4. Fallback mode is disabled (normal operation)
+ // 5. Job was created by relative imports (triggered by dependency chain)
+ //
+ // How it works:
+ //
+ // PHASE 1 - PUSH (in jobs.rs::push):
+ // When a dependency job is scheduled with delay, check debounce_key table
+ // - If key exists: Merge request into existing job, accumulate nodes/components
+ // - If key doesn't exist: Create new entry and store initial nodes/components
+ //
+ // PHASE 2 - ACCUMULATION:
+ // During the debounce window (typically 5-15 seconds), multiple requests merge
+ // - Each request adds nodes/components to debounce_stale_data table
+ // - SQL DISTINCT automatically removes duplicates during merge
+ //
+ // PHASE 3 - PULL (in jobs.rs::pull):
+ // When the delayed job finally executes:
+ // - Lock debounce_key FOR UPDATE to prevent races
+ // - Retrieve all accumulated nodes/components from debounce_stale_data
+ // - Process all collected dependencies in single execution
+ // - Clean up both debounce_key and debounce_stale_data entries
+ (true, true, Some(obj_path), false, true) => {
+ // Generate unique debounce key: "workspace_id:object_path:dependency"
+ // This ensures each workspace+path combination has independent debounce window
+ let debounce_key = format!("{workspace_id}:{obj_path}:dependency");
+
+ tracing::debug!(
+ workspace_id = %workspace_id,
+ object_path = %obj_path,
+ debounce_key = %debounce_key,
+ "Checking for existing debounced dependency job"
+ );
+
+ // Check if there's already a pending job registered for this debounce key
+ // The debounce_job_id_o is passed in by the caller after locking the key FOR UPDATE
+ // IMPORTANT: This is assumed that the caller will lock debounce_key row in this transaction.
+ // We do this to block puller from further actions until we are done with consolidation and stuff that we do here in push.
+ if let Some(debounce_job_id) = debounce_job_id_o {
+ tracing::debug!(
+ existing_job_id = %debounce_job_id,
+ new_job_id = %job_id,
+ "Found existing debounced job, merging this request"
+ );
+
+ // NOTE: Race condition handling:
+ // In rare cases, the debounce_key entry may still exist even though the job
+ // has been pulled and is running. This can happen because:
+ // - Job pull marks job as running first
+ // - Then debounce_key cleanup happens (without transaction for performance)
+ // - Between these steps, new requests might see the old debounce_key
+ //
+ // This is acceptable because the puller will be blocked and cannot proceed until this transaction finishes.
+ // This will give us some space to add consolidated data (if such) and debounce the request.
+ // Once tx is commited, the puller will be unblocked and continue execution.
+ // Accumulate the nodes/components that need relocking from this request
+
+ // This ensures all dependency updates are handled even if jobs are debounced
+ if let Some(to_relock) = extract_to_relock_from_args(&args.args) {
+ tracing::debug!(
+ job_id = %debounce_job_id,
+ node_count = to_relock.len(),
+ nodes = ?to_relock,
+ "Accumulating nodes/components to existing debounced job"
+ );
+
+ accumulate_debounce_stale_data(&mut tx, &debounce_job_id, &to_relock)
+ .await
+ .map_err(|e| {
+ tracing::error!(
+ error = %e,
+ job_id = %debounce_job_id,
+ debounce_key = %debounce_key,
+ "Failed to accumulate stale data for debounced job"
+ );
+ e
+ })?;
+ } else {
+ tracing::trace!(
+ job_id = %debounce_job_id,
+ "No nodes to relock in this request, skipping accumulation"
+ );
+ }
+
+ // Return the existing job ID, effectively debouncing this request
+ // The new job_id we generated won't be used
+ tracing::debug!(
+ returned_job_id = %debounce_job_id,
+ skipped_job_id = %job_id,
+ "Debounced: returning existing job ID instead of creating new job"
+ );
+
+ // We will skip some of the work downstream and just debounce the job.
+ return Ok((debounce_job_id, tx));
+ } else {
+ // No existing debounce entry - this is the first request in the debounce window
+ tracing::debug!(
+ job_id = %job_id,
+ debounce_key = %debounce_key,
+ "Creating new debounce entry (first request in window)"
+ );
+
+ sqlx::query!(
+ "INSERT INTO debounce_key (key, job_id) VALUES ($1, $2)",
+ &debounce_key,
+ job_id,
+ )
+ .execute(&mut *tx)
+ .await
+ .map_err(|e| {
+ tracing::error!(
+ error = %e,
+ debounce_key = %debounce_key,
+ job_id = %job_id,
+ "Failed to insert debounce_key entry"
+ );
+ Error::InternalErr(format!("Failed to create debounce entry: {}", e))
+ })?;
+
+ // Store initial nodes/components to relock if provided
+ if let Some(to_relock) = extract_to_relock_from_args(&args.args) {
+ tracing::debug!(
+ job_id = %job_id,
+ node_count = to_relock.len(),
+ nodes = ?to_relock,
+ "Storing initial nodes/components for new debounced job"
+ );
+
+ accumulate_debounce_stale_data(&mut tx, &job_id, &to_relock)
+ .await
+ .map_err(|e| {
+ tracing::error!(
+ error = %e,
+ job_id = %job_id,
+ "Failed to store initial stale data for debounced job"
+ );
+ e
+ })?;
+ } else {
+ tracing::trace!(
+ job_id = %job_id,
+ "No initial nodes to relock, debounce entry created without stale data"
+ );
+ }
+ }
+ }
+ _ => {
+ // Debouncing not applicable - proceed with normal job creation
+ tracing::trace!(
+ job_id = %job_id,
+ job_kind = ?job_kind,
+ "Debouncing conditions not met, proceeding with normal job creation"
+ );
+ }
+ };
+
if concurrent_limit.is_some() {
insert_concurrency_key(
workspace_id,
@@ -4230,7 +4522,6 @@ pub async fn push<'c, 'd>(
)
.await?;
}
-
let stringified_args = if *JOB_ARGS_AUDIT_LOGS {
Some(serde_json::to_string(&args).map_err(|e| {
Error::internal_err(format!(
@@ -4520,6 +4811,39 @@ pub async fn insert_concurrency_key<'d, 'c>(
Ok(())
}
+// pub async fn insert_debounce_key<'d, 'c>(
+// workspace_id: &str,
+// args: &PushArgs<'d>,
+// script_path: &Option,
+// job_kind: JobKind,
+// custom_concurrency_key: Option,
+// tx: &mut Transaction<'c, Postgres>,
+// job_id: Uuid,
+// ) -> Result<(), Error> {
+// let concurrency_key = custom_concurrency_key
+// .map(|x| interpolate_args(x, args, workspace_id))
+// .unwrap_or(fullpath_with_workspace(
+// workspace_id,
+// script_path.as_ref(),
+// &job_kind,
+// ));
+// sqlx::query!(
+// "WITH inserted_concurrency_counter AS (
+// INSERT INTO concurrency_counter (concurrency_id, job_uuids)
+// VALUES ($1, '{}'::jsonb)
+// ON CONFLICT DO NOTHING
+// )
+// INSERT INTO concurrency_key(key, job_id) VALUES ($1, $2)",
+// concurrency_key,
+// job_id,
+// )
+// .execute(&mut **tx)
+// .warn_after_seconds(3)
+// .await
+// .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:#}")))?;
+// Ok(())
+// }
+
pub fn canceled_job_to_result(job: &MiniPulledJob) -> serde_json::Value {
let reason = job
.canceled_reason
@@ -4809,3 +5133,232 @@ pub async fn get_same_worker_job(
))
})
}
+
+pub async fn preprocess_dependency_job(job: &mut PulledJob, db: &DB) -> error::Result<()> {
+ let kind = job.kind;
+ // Handle dependency job debouncing cleanup when a job is pulled for execution
+ if kind.is_dependency() && !*WMDEBUG_NO_DJOB_DEBOUNCING {
+ // Only used for testing in tests/relative_imports.rs
+ // Give us some space to work with.
+ #[cfg(debug_assertions)]
+ if let Some(duration) = job
+ .args
+ .as_ref()
+ .map(|x| {
+ x.get("dbg_sleep_between_pull_and_debounce_key_removal")
+ .map(|v| serde_json::from_str::(v.get()).ok())
+ .flatten()
+ })
+ .flatten()
+ {
+ tracing::debug!("going to sleep",);
+ sleep(std::time::Duration::from_secs(duration as u64)).await;
+ }
+
+ tracing::debug!(
+ "Processing debounce cleanup for dependency job {} at path {:?}",
+ &job.id,
+ &job.runnable_path
+ );
+
+ let key = format!("{}:{}:dependency", &job.workspace_id, job.runnable_path());
+ let mut tx = db.begin().await?;
+
+ // === DEBOUNCE CLEANUP ===
+ //
+ // Clean up the debounce_key entry for this job (if it exists).
+ //
+ // IMPORTANT: We delete by key (not job_id) to avoid race conditions:
+ // If pusher has locked this row then this call will be blocked until all txs are commited.
+ //
+ // The idea is that the worker_lockfiles::trigger_dependents_to_recompute_locks will fetch the latest version of the obj.
+ // This object needs to be created before the djob is executed and it happens right here.
+ //
+ // This way the next pusher can fetch the latest version of object and base their djob payload on newest version.
+ // The concurrency limit on djobs will make sure that by the time next djob is started executing the base version it is referencing
+ // has already calculated all locks. This way even next djob will always use the fully finalized version of object.
+ //
+ //
+ //
+ // Note: We don't use a transaction here for performance (it's called during job pull).
+ // This means there's a tiny window where the job is running but key isn't deleted yet,
+ // which is acceptable because new requests will just accumulate data to this job.
+ tracing::debug!(
+ job_id = %job.id,
+ "Cleaning up debounce_key entry for completed/pulled job"
+ );
+
+ // This will either:
+ // 1. Block until pusher pushed. Which gives us:
+ // - If there was any stale data in pusher, then we will read it here (couple of lines below)
+ // 2. Block pusher until we are done here. This gives us:
+ // - We will clone objects and retrieve the latest version. So when we are done the pusher can read latest version.
+ sqlx::query!("DELETE FROM debounce_key WHERE key = $1", &key)
+ .execute(&mut *tx)
+ .await
+ .map_err(|e| {
+ tracing::error!(
+ error = %e,
+ job_id = %job.id,
+ "Failed to delete debounce_key"
+ );
+ e
+ })?;
+
+ if job
+ .args
+ .as_ref()
+ .map(|x| x.get("triggered_by_relative_import").is_some())
+ .unwrap_or_default()
+ {
+ let Some(base_hash) = job.runnable_id else {
+ return Err(Error::InternalErr(
+ "Missing runnable_id for dependency job triggered by relative import"
+ .to_string(),
+ ));
+ };
+
+ tracing::debug!(
+ job_id = %job.id,
+ base_hash = %base_hash,
+ job_kind = ?kind,
+ "Creating new version for dependency job triggered by relative import"
+ );
+
+ let new_id = match kind {
+ JobKind::Dependencies => {
+ let deployment_message = job
+ .args
+ .clone()
+ .map(|hashmap| {
+ hashmap
+ .get("deployment_message")
+ .map(|map_value| {
+ serde_json::from_str::(map_value.get()).ok()
+ })
+ .flatten()
+ })
+ .flatten();
+
+ // This way we tell downstream which script we should archive when the resolution is finished.
+ // (not used at the moment)
+ job.args
+ .as_mut()
+ .map(|args| args.insert("base_hash".to_owned(), to_raw_value(&*base_hash)));
+
+ let new_hash = windmill_common::scripts::clone_script(
+ base_hash,
+ &job.workspace_id,
+ deployment_message,
+ &mut tx,
+ )
+ .await?;
+
+ new_hash
+ }
+ JobKind::FlowDependencies => {
+ sqlx::query_scalar!(
+ "INSERT INTO flow_version
+ (workspace_id, path, value, schema, created_by)
+
+ SELECT workspace_id, path, value, schema, created_by
+ FROM flow_version WHERE path = $1 AND workspace_id = $2 AND id = $3
+
+ RETURNING id
+ ",
+ job.runnable_path(),
+ job.workspace_id,
+ *base_hash,
+ )
+ .fetch_one(&mut *tx)
+ .await?
+ }
+ JobKind::AppDependencies => {
+ sqlx::query_scalar!(
+ "INSERT INTO app_version
+ (app_id, value, created_by, raw_app)
+ SELECT app_id, value, created_by, raw_app
+ FROM app_version WHERE id = $1
+ RETURNING id",
+ *base_hash
+ )
+ .fetch_one(&mut *tx)
+ .await?
+ }
+ _ => {
+ return Err(Error::InternalErr(format!(
+ "Matched unexpected JobKind ({:?}). This is a bug!",
+ kind
+ )))
+ }
+ };
+
+ job.runnable_id.replace(new_id.into());
+ }
+
+ // === RETRIEVE ACCUMULATED DEBOUNCE DATA ===
+ //
+ // For flows and apps, retrieve all nodes/components that were accumulated
+ // during the debounce window. This data comes from requests that were merged
+ // into this job instead of creating their own jobs.
+ //
+ // Scripts don't need this because they don't have nodes/components to relock.
+ if let Some(to_relock_field) = match &job.kind {
+ JobKind::FlowDependencies => Some("nodes_to_relock"),
+ JobKind::AppDependencies => Some("components_to_relock"),
+ _ => None, // Scripts don't use accumulated stale data
+ } {
+ tracing::debug!(
+ job_id = %job.id,
+ job_kind = ?job.kind,
+ field = %to_relock_field,
+ "Retrieving accumulated stale data from debounced requests"
+ );
+
+ if let Some(stale_data) = sqlx::query_scalar!(
+ "DELETE FROM debounce_stale_data WHERE job_id = $1 RETURNING to_relock",
+ &job.id
+ )
+ .fetch_optional(&mut *tx)
+ .await
+ .map_err(|e| {
+ tracing::error!(
+ error = %e,
+ job_id = %job.id,
+ "Failed to retrieve debounce_stale_data"
+ );
+ e
+ })?
+ .flatten()
+ {
+ tracing::debug!(
+ job_id = %job.id,
+ node_count = stale_data.len(),
+ nodes = ?stale_data,
+ "Retrieved accumulated nodes/components from {} debounced requests",
+ stale_data.len()
+ );
+
+ // Replace the job's relock list with the accumulated data
+ // This ensures all nodes from all debounced requests are processed
+ if let Some(args) = job.args.as_mut() {
+ args.insert(to_relock_field.to_owned(), to_raw_value(&stale_data));
+ tracing::debug!(
+ field = %to_relock_field,
+ "Updated job args with accumulated debounce data"
+ );
+ }
+ } else {
+ tracing::trace!(
+ job_id = %job.id,
+ "No accumulated stale data found (no debounced requests or already cleaned up)"
+ );
+ }
+ }
+
+ // This will unblock pusher.
+ tx.commit().await?;
+ }
+
+ Ok(())
+}
diff --git a/backend/windmill-queue/src/schedule.rs b/backend/windmill-queue/src/schedule.rs
index c35a94a2dc..1aa4a11a10 100644
--- a/backend/windmill-queue/src/schedule.rs
+++ b/backend/windmill-queue/src/schedule.rs
@@ -23,8 +23,8 @@ use windmill_common::jobs::check_tag_available_for_workspace_internal;
use windmill_common::jobs::JobPayload;
use windmill_common::schedule::schedule_to_user;
use windmill_common::scripts::ScriptHash;
-use windmill_common::worker::to_raw_value;
use windmill_common::utils::WarnAfterExt;
+use windmill_common::worker::to_raw_value;
use windmill_common::FlowVersionInfo;
use windmill_common::DB;
use windmill_common::{
@@ -39,13 +39,13 @@ async fn get_schedule_metadata<'c>(
tx: &mut sqlx::Transaction<'c, sqlx::Postgres>,
schedule: &Schedule,
) -> Result<(
- Option, // tag
- Option, // timeout
- Option, // on_behalf_of_email
- String, // created_by
- Option, // hash (for scripts)
- Option, // flow_version (for flows)
- Option, // retry
+ Option, // tag
+ Option, // timeout
+ Option, // on_behalf_of_email
+ String, // created_by
+ Option, // hash (for scripts)
+ Option, // flow_version (for flows)
+ Option, // retry
)> {
let parsed_retry = schedule
.retry
@@ -62,20 +62,24 @@ async fn get_schedule_metadata<'c>(
)
.await?;
- let FlowVersionInfo {
+ let FlowVersionInfo { tag, on_behalf_of_email, edited_by, .. } =
+ get_latest_flow_version_info_for_path_from_version(
+ &mut **tx,
+ version,
+ &schedule.workspace_id,
+ &schedule.script_path,
+ )
+ .await?;
+
+ Ok((
tag,
+ None,
on_behalf_of_email,
edited_by,
- ..
- } = get_latest_flow_version_info_for_path_from_version(
- &mut **tx,
- version,
- &schedule.workspace_id,
- &schedule.script_path,
- )
- .await?;
-
- Ok((tag, None, on_behalf_of_email, edited_by, None, Some(version), parsed_retry))
+ None,
+ Some(version),
+ parsed_retry,
+ ))
} else {
let (
hash,
@@ -98,7 +102,15 @@ async fn get_schedule_metadata<'c>(
)
.await?;
- Ok((tag, timeout, on_behalf_of_email, created_by, Some(hash), None, parsed_retry))
+ Ok((
+ tag,
+ timeout,
+ on_behalf_of_email,
+ created_by,
+ Some(hash),
+ None,
+ parsed_retry,
+ ))
}
}
@@ -208,7 +220,9 @@ pub async fn push_scheduled_job<'c>(
// If schedule handler is defined, wrap the scheduled job in a synthetic flow
// with the handler as the first step (with stop_after_if to skip if handler returns false)
- let (payload, tag, timeout, on_behalf_of_email, created_by) = if let Some(handler_path) = &schedule.dynamic_skip {
+ let (payload, tag, timeout, on_behalf_of_email, created_by) = if let Some(handler_path) =
+ &schedule.dynamic_skip
+ {
// Build skip handler args
let mut skip_handler_args = HashMap::>::new();
skip_handler_args.insert(
@@ -472,6 +486,7 @@ pub async fn push_scheduled_job<'c>(
push_authed,
false,
None,
+ None,
)
.warn_after_seconds_with_sql(1, "push in push_scheduled_job".to_string())
.await?;
diff --git a/backend/windmill-worker/src/ai_executor.rs b/backend/windmill-worker/src/ai_executor.rs
index 01a4b36b71..bbd40979fe 100644
--- a/backend/windmill-worker/src/ai_executor.rs
+++ b/backend/windmill-worker/src/ai_executor.rs
@@ -969,6 +969,7 @@ pub async fn run_agent(
job_perms.as_ref(),
true,
None,
+ None,
)
.await?;
diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs
index 893cb9ef15..39f1944d6d 100644
--- a/backend/windmill-worker/src/worker.rs
+++ b/backend/windmill-worker/src/worker.rs
@@ -11,6 +11,7 @@
use anyhow::anyhow;
use futures::TryFutureExt;
+use tokio::time::sleep;
use tokio::time::timeout;
use windmill_common::client::AuthedClient;
use windmill_common::scripts::hash_to_codebase_id;
@@ -57,6 +58,7 @@ use std::{
time::Duration,
};
use windmill_parser::MainArgSignature;
+use windmill_queue::preprocess_dependency_job;
use windmill_queue::PulledJobResultToJobErr;
use uuid::Uuid;
@@ -954,6 +956,7 @@ pub async fn run_worker(
);
}
+ dbg!("start");
let start_time = Instant::now();
let worker_dir = format!("{TMP_DIR}/{worker_name}");
@@ -994,6 +997,8 @@ pub async fn run_worker(
});
}
+ dbg!("python stuff is done");
+
if let Some(ref netrc) = *NETRC {
tracing::info!(worker = %worker_name, hostname = %hostname, "Writing netrc at {}/.netrc", HOME_ENV.as_str());
write_file(&HOME_ENV, ".netrc", netrc).expect("could not write netrc");
@@ -1001,6 +1006,8 @@ pub async fn run_worker(
create_directory_async(&worker_dir).await;
+ dbg!("worker dir created");
+
if !*DISABLE_NSJAIL {
let _ = write_file(
&worker_dir,
@@ -1383,6 +1390,7 @@ pub async fn run_worker(
let mut killpill_rx2 = killpill_rx.resubscribe();
+ dbg!("starting loop");
loop {
let last_processing_duration_secs = last_processing_duration.load(Ordering::SeqCst);
if last_processing_duration_secs > 5 {
@@ -1503,7 +1511,6 @@ pub async fn run_worker(
match &conn {
Connection::Sql(db) => {
let job = get_same_worker_job(db, &same_worker_job).await;
- // tracing::error!("r: {:?}", r);
if job.is_err() && !same_worker_job.recoverable {
tracing::error!(
worker = %worker_name, hostname = %hostname,
@@ -1559,6 +1566,7 @@ pub async fn run_worker(
Connection::Sql(db) => {
let pull_time = Instant::now();
let likelihood_of_suspend = last_30jobs_suspended as f64 / 30.0;
+
let suspend_first = suspend_first_success
|| rand::random::() < likelihood_of_suspend
|| last_suspend_first.elapsed().as_secs_f64() > 5.0;
@@ -1566,8 +1574,7 @@ pub async fn run_worker(
if suspend_first {
last_suspend_first = Instant::now();
}
-
- let job = match timeout(
+ let mut job = match timeout(
Duration::from_secs(10),
pull(
&db,
@@ -1589,6 +1596,31 @@ pub async fn run_worker(
}
};
+ // Essential debouncing job preprocessing.
+ if let Ok(windmill_queue::PulledJobResult {
+ job: Some(ref mut pulled_job),
+ ..
+ }) = &mut job
+ {
+ match timeout(
+ core::time::Duration::from_secs(10),
+ preprocess_dependency_job(pulled_job, &db),
+ )
+ .warn_after_seconds(2)
+ .await
+ {
+ Ok(Err(e)) => {
+ tracing::error!(worker = %worker_name, hostname = %hostname, "critical: debouncing job preprocessor failed: {e:?}");
+ job = Err(e.into());
+ }
+ Err(e) => {
+ tracing::error!(worker = %worker_name, hostname = %hostname, "critical: debouncing job preprocessor has timed out: {e:?}");
+ job = Err(e.into());
+ }
+ _ => {}
+ }
+ }
+
add_time!(bench, "job pulled from DB");
let duration_pull_s = pull_time.elapsed().as_secs_f64();
let err_pull = job.is_ok();
@@ -1658,6 +1690,7 @@ pub async fn run_worker(
Err(err) => Err(err),
}
}
+
Connection::Http(client) => crate::agent_workers::pull_job(&client, None, None)
.await
.map_err(|e| error::Error::InternalErr(e.to_string()))
@@ -2527,6 +2560,23 @@ pub async fn handle_queued_job(
logs.push_str("---\n");
}
+ // Only used for testing in tests/relative_imports.rs
+ // Give us some space to work with.
+ #[cfg(debug_assertions)]
+ if let Some(dbg_djob_sleep) = job
+ .args
+ .as_ref()
+ .map(|x| {
+ x.get("dbg_djob_sleep")
+ .map(|v| serde_json::from_str::(v.get()).ok())
+ .flatten()
+ })
+ .flatten()
+ {
+ tracing::debug!("Debug: {} going to sleep for {}", job.id, dbg_djob_sleep);
+ sleep(std::time::Duration::from_secs(dbg_djob_sleep as u64)).await;
+ }
+
tracing::debug!(
workspace_id = %job.workspace_id,
"handling job {}",
@@ -2564,7 +2614,7 @@ pub async fn handle_queued_job(
JobKind::FlowDependencies => match conn {
Connection::Sql(db) => {
handle_flow_dependency_job(
- &job,
+ (*job).clone(),
preview_data.as_ref(),
&mut mem_peak,
&mut canceled_by,
@@ -2586,7 +2636,7 @@ pub async fn handle_queued_job(
},
JobKind::AppDependencies => match conn {
Connection::Sql(db) => handle_app_dependency_job(
- &job,
+ (*job).clone(),
&mut mem_peak,
&mut canceled_by,
job_dir,
diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs
index ad5b40ee4a..7a75068fcf 100644
--- a/backend/windmill-worker/src/worker_flow.rs
+++ b/backend/windmill-worker/src/worker_flow.rs
@@ -3160,6 +3160,7 @@ async fn push_next_flow_job(
job_perms.as_ref(),
false,
None,
+ None,
)
.warn_after_seconds(2)
.await?;
diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs
index 59f0f87552..f4fbf5bb92 100644
--- a/backend/windmill-worker/src/worker_lockfiles.rs
+++ b/backend/windmill-worker/src/worker_lockfiles.rs
@@ -5,21 +5,23 @@ use std::path::{Component, Path, PathBuf};
#[cfg(feature = "python")]
use crate::ansible_executor::{get_git_repos_lock, AnsibleDependencyLocks};
-use crate::scoped_dependency_map::{ScopedDependencyMap, WMDEBUG_NO_DMAP_DISSOLVE};
+use crate::scoped_dependency_map::ScopedDependencyMap;
use async_recursion::async_recursion;
+use chrono::{Duration, Utc};
use itertools::Itertools;
use serde_json::value::RawValue;
use serde_json::{from_value, json, Value};
use sha2::Digest;
use sqlx::types::Json;
+use tokio::time::timeout;
use uuid::Uuid;
use windmill_common::assets::{clear_asset_usage, insert_asset_usage, AssetUsageKind};
use windmill_common::error::Error;
use windmill_common::error::Result;
use windmill_common::flows::{FlowModule, FlowModuleValue, FlowNodeId};
-use windmill_common::get_latest_deployed_hash_for_path;
use windmill_common::jobs::JobPayload;
-use windmill_common::scripts::{hash_script, NewScript, ScriptHash};
+use windmill_common::scripts::ScriptHash;
+use windmill_common::utils::WarnAfterExt;
#[cfg(feature = "python")]
use windmill_common::worker::PythonAnnotations;
use windmill_common::worker::{to_raw_value, to_raw_value_owned, write_file, Connection};
@@ -46,6 +48,9 @@ lazy_static::lazy_static! {
static ref WMDEBUG_NO_NEW_FLOW_VERSION_ON_DJ: bool = std::env::var("WMDEBUG_NO_NEW_FLOW_VERSION_ON_DJ").is_ok();
static ref WMDEBUG_NO_NEW_APP_VERSION_ON_DJ: bool = std::env::var("WMDEBUG_NO_NEW_APP_VERSION_ON_DJ").is_ok();
static ref WMDEBUG_NO_COMPONENTS_TO_RELOCK: bool = std::env::var("WMDEBUG_NO_COMPONENTS_TO_RELOCK").is_ok();
+ static ref DEPENDENCY_JOB_DEBOUNCE_DELAY: usize = std::env::var("DEPENDENCY_JOB_DEBOUNCE_DELAY").ok().and_then(|flag| flag.parse().ok()).unwrap_or(
+ if cfg!(test) { /* if test we want increased debouncing delay */ 15 } else { 5 }
+ );
}
use crate::common::OccupancyMetrics;
@@ -144,6 +149,12 @@ pub async fn handle_dependency_job(
token: &str,
occupancy_metrics: &mut OccupancyMetrics,
) -> error::Result> {
+ // Processing a dependency job - these jobs handle lockfile generation and dependency updates
+ // for scripts, flows, and apps when their dependencies or imported scripts change
+ tracing::debug!(
+ "Processing dependency job for path: {:?}",
+ job.runnable_path()
+ );
let script_path = job.runnable_path();
let raw_deps = job
.args
@@ -243,141 +254,25 @@ pub async fn handle_dependency_job(
let current_hash = job.runnable_id.unwrap_or(ScriptHash(0));
let w_id = &job.workspace_id;
+
let (deployment_message, parent_path) =
get_deployment_msg_and_parent_path_from_args(job.args.clone());
- let script_info = sqlx::query_as::<_, windmill_common::scripts::Script>(
- "SELECT * FROM script WHERE hash = $1 AND workspace_id = $2",
+ // We do not create new row for this update
+ // That means we can keep current hash and just update lock
+ sqlx::query!(
+ "UPDATE script SET lock = $1 WHERE hash = $2 AND workspace_id = $3",
+ &content,
+ ¤t_hash.0,
+ w_id
)
- .bind(¤t_hash.0)
- .bind(w_id)
- .fetch_one(db)
+ .execute(db)
.await?;
- // DependencyJob can be triggered only from 2 places:
- // 1. create_script function in windmill-api/src/scripts.rs
- // 2. trigger_dependents_to_recompute_dependencies (in this file)
- //
- // First will **always** produce script with null in `lock`
- // where Second will **always** do with lock being not null
- let deployed_hash = if script_info.lock.is_some() && !*WMDEBUG_NO_HASH_CHANGE_ON_DJ {
- let path = script_info.path.clone();
-
- let mut tx = db.begin().await?;
- // This entire section exists to solve following problem:
- //
- // 2 workers, one script that depend on another in python
- // run the original script on both workers
- // you update the dependenecy of a relative import,
- // run it again until you ran it on both, normally it should fail on one of those
- //
- // It happens because every worker has cached their own script versions.
- // However usual dependency job does not update hash of the script (and cache is keyed by the hash).
- // This logical branch will create new script which will update the hash and automatically invalidate cache.
- //
- // IMPORTANT: This will **only** be triggered by another DependencyJob. It will never be triggered by script (re)deployement
-
- let ns = NewScript {
- path: script_info.path,
- parent_hash: Some(current_hash),
- summary: script_info.summary,
- description: script_info.description,
- content: script_info.content,
- schema: script_info.schema,
- is_template: Some(script_info.is_template),
- // TODO: Make it either None everywhere (particularely when raw reqs are calculated)
- // Or handle this case and conditionally make Some (only with raw reqs)
- lock: None,
- language: script_info.language,
- kind: Some(script_info.kind),
- tag: script_info.tag,
- draft_only: script_info.draft_only,
- envs: script_info.envs,
- concurrent_limit: script_info.concurrent_limit,
- concurrency_time_window_s: script_info.concurrency_time_window_s,
- cache_ttl: script_info.cache_ttl,
- dedicated_worker: script_info.dedicated_worker,
- ws_error_handler_muted: script_info.ws_error_handler_muted,
- priority: script_info.priority,
- timeout: script_info.timeout,
- delete_after_use: script_info.delete_after_use,
- restart_unless_cancelled: script_info.restart_unless_cancelled,
- deployment_message: deployment_message.clone(),
- concurrency_key: script_info.concurrency_key,
- visible_to_runner_only: script_info.visible_to_runner_only,
- no_main_func: script_info.no_main_func,
- codebase: script_info.codebase,
- has_preprocessor: script_info.has_preprocessor,
- on_behalf_of_email: script_info.on_behalf_of_email,
- assets: script_info.assets,
- };
-
- let new_hash = hash_script(&ns);
-
- sqlx::query!("
- INSERT INTO script
- (workspace_id, hash, path, parent_hashes, summary, description, content, \
- created_by, schema, is_template, extra_perms, lock, language, kind, tag, \
- draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
- dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \
- delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, \
- codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets)
-
- SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, \
- content, created_by, schema, is_template, extra_perms, $4, language, kind, tag, \
- draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, \
- dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, \
- delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, \
- codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets
-
- FROM script WHERE hash = $2 AND workspace_id = $3;
- ",
- new_hash, current_hash.0, w_id, &content).execute(db).await?;
- tracing::info!(
- "Updated script at path {} with hash {} to new hash {}",
- path,
- current_hash.0,
- new_hash
- );
- // Archive current
- sqlx::query!(
- "UPDATE script SET archived = true WHERE hash = $1 AND workspace_id = $2",
- current_hash.0,
- w_id
- )
- .execute(&mut *tx)
- .await?;
- tracing::info!(
- "Archived script at path {} from dependency job {}",
- path,
- current_hash.0
- );
- tx.commit().await?;
-
- ScriptHash(new_hash)
- } else {
- // We do not create new row for this update
- // That means we can keep current hash and just update lock
- sqlx::query!(
- "UPDATE script SET lock = $1 WHERE hash = $2 AND workspace_id = $3",
- &content,
- ¤t_hash.0,
- w_id
- )
- .execute(db)
- .await?;
-
- // `lock` has been updated; invalidate the cache.
- // Since only worker that ran this Dependency Job has the cache
- // we do not need to think about invalidating cache for other workers.
- cache::script::invalidate(current_hash);
-
- if *WMDEBUG_NO_HASH_CHANGE_ON_DJ {
- tracing::warn!("WMDEBUG_NO_HASH_CHANGE_ON_DJ usually should not be used. Behavior might be unstable. Please contact Windmill Team for support.")
- }
-
- current_hash
- };
+ // `lock` has been updated; invalidate the cache.
+ // Since only worker that ran this Dependency Job has the cache
+ // we do not need to think about invalidating cache for other workers.
+ cache::script::invalidate(current_hash);
if let Err(e) = handle_deployment_metadata(
&job.permissioned_as_email,
@@ -385,7 +280,7 @@ pub async fn handle_dependency_job(
&db,
&w_id,
DeployedObject::Script {
- hash: deployed_hash,
+ hash: current_hash,
path: script_path.to_string(),
parent_path: parent_path.clone(),
},
@@ -520,20 +415,30 @@ pub async fn process_relative_imports(
// But currently we will do this extra db call for every script regardless of whether they have relative imports or not
// Script might have no relative imports but still be referenced by someone else.
- if let Err(e) = trigger_dependents_to_recompute_dependencies(
- w_id,
- script_path,
- deployment_message,
- parent_path,
- permissioned_as_email,
- created_by,
- permissioned_as,
- db,
- already_visited,
+ match timeout(
+ core::time::Duration::from_secs(60),
+ trigger_dependents_to_recompute_dependencies(
+ w_id,
+ script_path,
+ deployment_message,
+ parent_path,
+ permissioned_as_email,
+ created_by,
+ permissioned_as,
+ db,
+ already_visited,
+ ),
)
+ .warn_after_seconds(10)
.await
{
- tracing::error!(%e, "error triggering dependents to recompute dependencies");
+ Ok(Err(e)) => {
+ tracing::error!(%e, "error triggering dependents to recompute dependencies")
+ }
+ Err(e) => {
+ tracing::error!(%e, "triggering dependents to recompute dependencies has timed out")
+ }
+ _ => {}
}
}
@@ -551,6 +456,15 @@ pub async fn trigger_dependents_to_recompute_dependencies(
db: &sqlx::Pool,
mut already_visited: Vec