mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
@@ -3769,6 +3769,7 @@ async fn batch_rerun_handle_job(
|
||||
batch_rerun_compute_js_expression(expr.clone(), job.clone()).await?,
|
||||
);
|
||||
}
|
||||
InputTransform::Ai => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -643,6 +643,7 @@ pub enum InputTransform {
|
||||
#[serde(default = "default_empty_string")]
|
||||
expr: String,
|
||||
},
|
||||
Ai,
|
||||
}
|
||||
|
||||
impl InputTransform {
|
||||
@@ -661,6 +662,7 @@ impl TryFrom<UntaggedInputTransform> for InputTransform {
|
||||
let input_transform = match value.type_.as_str() {
|
||||
"static" => InputTransform::new_static_value(value.value.unwrap_or_else(default_null)),
|
||||
"javascript" => InputTransform::new_javascript_expr(&value.expr.unwrap_or_default()),
|
||||
"ai" => InputTransform::Ai,
|
||||
other => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"got value: {other} for field `type`, expected value: `static` or `javascript`"
|
||||
@@ -816,8 +818,7 @@ pub struct McpToolValue {
|
||||
pub exclude_tools: Vec<String>,
|
||||
}
|
||||
|
||||
fn is_none_or_empty_vec<T>(expr: &Option<Vec<T>>) -> bool
|
||||
{
|
||||
fn is_none_or_empty_vec<T>(expr: &Option<Vec<T>>) -> bool {
|
||||
expr.is_none() || expr.as_ref().unwrap().is_empty()
|
||||
}
|
||||
|
||||
|
||||
@@ -322,11 +322,17 @@ async fn execute_windmill_tool(
|
||||
// Evaluate each input transform and merge with AI-provided args
|
||||
for (key, transform) in input_transforms.iter() {
|
||||
// We skip static empty / null values, those are the one the AI will fill in
|
||||
if let InputTransform::Static { value } = transform {
|
||||
let val = value.get().trim();
|
||||
if val.is_empty() || val == "null" {
|
||||
match transform {
|
||||
InputTransform::Static { value } => {
|
||||
let val = value.get().trim();
|
||||
if val.is_empty() || val == "null" {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
InputTransform::Ai => {
|
||||
continue;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
let result = evaluate_input_transform::<Box<RawValue>>(
|
||||
transform,
|
||||
|
||||
@@ -78,6 +78,7 @@ pub fn filter_schema_by_input_transforms(
|
||||
!val.is_empty() && val != "null"
|
||||
}
|
||||
InputTransform::Javascript { expr } => !expr.trim().is_empty(),
|
||||
InputTransform::Ai => false,
|
||||
};
|
||||
if is_completed {
|
||||
Some(key.clone())
|
||||
|
||||
@@ -1910,11 +1910,8 @@ pub async fn evaluate_input_transform<T>(
|
||||
by_id: Option<&IdContext>,
|
||||
) -> error::Result<T>
|
||||
where
|
||||
T: for<'de> serde::Deserialize<'de> + Send,
|
||||
T: for<'de> serde::Deserialize<'de> + Send + Default,
|
||||
{
|
||||
let mut context = HashMap::with_capacity(2);
|
||||
context.insert("result".to_string(), last_result.clone());
|
||||
context.insert("previous_result".to_string(), last_result.clone());
|
||||
match transform {
|
||||
InputTransform::Static { value } => serde_json::from_str(value.get()).map_err(|e| {
|
||||
Error::ExecutionErr(format!(
|
||||
@@ -1923,6 +1920,9 @@ where
|
||||
))
|
||||
}),
|
||||
InputTransform::Javascript { expr } => {
|
||||
let mut context = HashMap::with_capacity(2);
|
||||
context.insert("result".to_string(), last_result.clone());
|
||||
context.insert("previous_result".to_string(), last_result.clone());
|
||||
let result = eval_timeout(
|
||||
expr.to_string(),
|
||||
context,
|
||||
@@ -1947,6 +1947,7 @@ where
|
||||
))
|
||||
})
|
||||
}
|
||||
InputTransform::Ai => Ok(T::default()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2007,6 +2008,7 @@ async fn transform_input(
|
||||
})?;
|
||||
mapped.insert(key.to_string(), v);
|
||||
}
|
||||
InputTransform::Ai => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2441,6 +2443,9 @@ async fn push_next_flow_job(
|
||||
)));
|
||||
}
|
||||
}
|
||||
InputTransform::Ai => {
|
||||
user_groups_required = Vec::new();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
user_groups_required = Vec::new();
|
||||
@@ -4348,6 +4353,11 @@ async fn next_forloop_status(
|
||||
/* Iterator is an InputTransform, evaluate it into an array. */
|
||||
let itered_raw = match iterator {
|
||||
InputTransform::Static { value } => to_raw_value(value),
|
||||
InputTransform::Ai => {
|
||||
return Err(Error::ExecutionErr(format!(
|
||||
"AI input transform not supported for iterator"
|
||||
)))?
|
||||
}
|
||||
InputTransform::Javascript { expr } => {
|
||||
let mut context = HashMap::with_capacity(5);
|
||||
context.insert("result".to_string(), arc_last_job_result.clone());
|
||||
@@ -4429,6 +4439,11 @@ async fn next_forloop_status(
|
||||
)
|
||||
.await?
|
||||
}
|
||||
InputTransform::Ai => {
|
||||
return Err(Error::ExecutionErr(format!(
|
||||
"AI input transform not supported for iterator"
|
||||
)))?
|
||||
}
|
||||
};
|
||||
serde_json::from_str::<Vec<Box<RawValue>>>(itered_raw.get()).map_err(
|
||||
|not_array| {
|
||||
|
||||
@@ -159,7 +159,13 @@
|
||||
|
||||
function getPropertyType(arg: InputTransform | any): PropertyType {
|
||||
// For agent tools, if static with undefined/empty value, treat as 'ai', meaning the field will be filled by the AI agent dynamically.
|
||||
if (isAgentTool && arg?.type === 'static' && arg?.value === undefined) {
|
||||
if (
|
||||
isAgentTool &&
|
||||
((arg?.type === 'static' && arg?.value === undefined) || arg?.type === 'ai')
|
||||
) {
|
||||
if (arg?.type === 'static') {
|
||||
arg.type = 'ai'
|
||||
}
|
||||
return 'ai'
|
||||
}
|
||||
|
||||
@@ -563,7 +569,7 @@
|
||||
if (e.detail === 'ai') {
|
||||
// Switch to AI mode: static with no value
|
||||
if (arg) {
|
||||
arg.type = 'static'
|
||||
arg.type = 'ai'
|
||||
arg.value = undefined
|
||||
arg.expr = undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user