fix: make WM_FLOW_PATH available in flow step previews (#8042)

* fix: pass flow path in flow step preview for AI agent modules

JobLoader.runFlowPreview was missing the path parameter, causing
WM_FLOW_PATH to be unavailable when using the Run button on individual
flow steps. Test up to here worked correctly because it uses a
different code path (utils.svelte.ts) that already passed the path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: make WM_FLOW_PATH available for rawscript/script step previews

Inject the flow path as `_flow_path` in the job args when running a
script preview from the flow editor. The SQL pull queries now use
COALESCE to fall back to this arg when no parent runnable path exists,
making WM_FLOW_PATH available for individual step "Run" previews.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: rename _flow_path args key to _FLOW_PATH

Match existing convention used by _ENTRYPOINT_OVERRIDE.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-02-21 21:56:32 +00:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 18b3c1ae5c
commit a91c532eca
6 changed files with 35 additions and 14 deletions
+2
View File
@@ -19671,6 +19671,8 @@ components:
type: boolean
lock:
type: string
flow_path:
type: string
required:
- args
+17 -5
View File
@@ -42,8 +42,6 @@ use windmill_common::runnable_settings::{
};
#[cfg(feature = "inline_preview")]
use windmill_common::runtime_assets::{register_runtime_asset, InsertRuntimeAssetParams};
use windmill_types::s3::BundleFormat;
use windmill_object_store::upload_artifact_to_store;
use windmill_common::scripts::ScriptRunnableSettingsInline;
use windmill_common::triggers::TriggerMetadata;
use windmill_common::utils::{RunnableKind, WarnAfterExt};
@@ -54,8 +52,10 @@ use windmill_common::workspace_dependencies::{
use windmill_common::DYNAMIC_INPUT_CACHE;
#[cfg(all(feature = "enterprise", feature = "smtp"))]
use windmill_common::{email_oss::send_email_html, server::load_smtp_config};
use windmill_object_store::upload_artifact_to_store;
#[cfg(feature = "inline_preview")]
use windmill_parser::asset_parser::AssetKind;
use windmill_types::s3::BundleFormat;
#[cfg(feature = "inline_preview")]
use windmill_worker::get_worker_internal_server_inline_utils;
@@ -1386,7 +1386,8 @@ async fn get_logs_from_store(
log_file_index: &Option<Vec<String>>,
) -> Option<error::Result<Body>> {
use futures::StreamExt;
let stream = windmill_object_store::get_logs_from_store(log_offset, logs, log_file_index).await?;
let stream =
windmill_object_store::get_logs_from_store(log_offset, logs, log_file_index).await?;
let header = bytes::Bytes::from(
r#"to remove ansi colors, use: | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g'
"#
@@ -2849,6 +2850,7 @@ struct Preview {
dedicated_worker: Option<bool>,
lock: Option<String>,
format: Option<String>,
flow_path: Option<String>,
}
#[cfg(feature = "inline_preview")]
@@ -4509,6 +4511,14 @@ async fn run_preview_script(
check_tag_available_for_workspace(&db, &w_id, &tag, &authed).await?;
let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into());
let preview_args = preview.args.unwrap_or_default();
let flow_path_extra = preview.flow_path.map(|fp| {
let mut extra = HashMap::new();
extra.insert("_FLOW_PATH".to_string(), to_raw_value(&fp));
extra
});
let push_args = PushArgs { extra: flow_path_extra, args: &preview_args };
let (uuid, tx) = push(
&db,
tx,
@@ -4532,7 +4542,7 @@ async fn run_preview_script(
dedicated_worker: preview.dedicated_worker,
}),
},
PushArgs::from(&preview.args.unwrap_or_default()),
push_args,
authed.display_username(),
&authed.email,
username_to_permissioned_as(&authed.username),
@@ -5772,7 +5782,9 @@ async fn get_log_file(Path((_w_id, file_p)): Path<(String, String)>) -> error::R
#[cfg(all(feature = "enterprise", feature = "parquet"))]
if let Some(os) = windmill_object_store::get_object_store().await {
let file = os
.get(&windmill_object_store::object_store_reexports::Path::from(format!("logs/{file_p}")))
.get(&windmill_object_store::object_store_reexports::Path::from(
format!("logs/{file_p}"),
))
.await;
if let Ok(file) = file {
if let Ok(bytes) = file.bytes().await {
+1 -1
View File
@@ -427,7 +427,7 @@ fn format_pull_query(peek: String) -> String {
j.same_worker, j.pre_run_error, j.visible_to_owner,
j.tag, j.concurrent_limit, j.concurrency_time_window_s, j.flow_innermost_root_job, j.root_job,
j.timeout, j.flow_step_id, j.cache_ttl, q.cache_ignore_s3_path, q.runnable_settings_handle, j.priority, j.raw_code, j.raw_lock, j.raw_flow,
j.script_entrypoint_override, j.preprocessed, pj.runnable_path as parent_runnable_path,
j.script_entrypoint_override, j.preprocessed, COALESCE(pj.runnable_path, j.args->>'_FLOW_PATH') as parent_runnable_path,
COALESCE(p.email, j.permissioned_as_email) as permissioned_as_email, p.username as permissioned_as_username, p.is_admin as permissioned_as_is_admin,
p.is_operator as permissioned_as_is_operator, p.groups as permissioned_as_groups, p.folders as permissioned_as_folders, p.end_user_email as permissioned_as_end_user_email
FROM q, j
+1 -1
View File
@@ -6208,7 +6208,7 @@ pub async fn get_same_worker_job(
v2_job.raw_code,
v2_job.raw_lock,
v2_job.raw_flow,
pj.runnable_path as parent_runnable_path,
COALESCE(pj.runnable_path, v2_job.args->>'_FLOW_PATH') as parent_runnable_path,
p.email as permissioned_as_email, p.username as permissioned_as_username, p.is_admin as permissioned_as_is_admin,
p.is_operator as permissioned_as_is_operator, p.groups as permissioned_as_groups, p.folders as permissioned_as_folders, p.end_user_email as permissioned_as_end_user_email
FROM v2_job_queue
+8 -4
View File
@@ -226,7 +226,8 @@
export async function runFlowPreview(
args: Record<string, any>,
flow: OpenFlow & { tag?: string },
callbacks?: Callbacks
callbacks?: Callbacks,
path?: string
): Promise<string> {
return abstractRun(
() =>
@@ -235,7 +236,8 @@
requestBody: {
args,
value: flow.value,
tag: flow.tag
tag: flow.tag,
path
}
}),
callbacks
@@ -288,7 +290,8 @@
tag: string | undefined,
lock?: string,
hash?: string,
callbacks?: Callbacks
callbacks?: Callbacks,
flowPath?: string
): Promise<string> {
return abstractRun(
() =>
@@ -301,7 +304,8 @@
language: lang as Preview['language'],
tag,
lock,
script_hash: hash
script_hash: hash,
flow_path: flowPath
}
}),
callbacks
@@ -76,7 +76,8 @@
flowStore?.val?.tag ?? val.tag,
undefined,
undefined,
callbacks
callbacks,
$pathStore
)
} else if (val.type == 'script') {
const script = val.hash
@@ -90,7 +91,8 @@
flowStore?.val?.tag ?? (val.tag_override ? val.tag_override : script.tag),
script.lock,
val.hash ?? script.hash,
callbacks
callbacks,
$pathStore
)
} else if (val.type == 'flow') {
await jobLoader?.runFlowByPath(val.path, args, callbacks)
@@ -125,7 +127,8 @@
summary: '',
schema
},
callbacks
callbacks,
$pathStore
)
} else {
throw Error('Not supported module type')