Compare commits
2 Commits
v1.682.0
...
claude/iss
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51e2dcd86a | ||
|
|
2375667408 |
@@ -139,6 +139,9 @@ pub struct FlowValue {
|
||||
pub concurrency_key: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub chat_input_enabled: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[serde(default)]
|
||||
pub env_variables: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
impl FlowValue {
|
||||
|
||||
@@ -3970,6 +3970,7 @@ pub async fn push<'c, 'd>(
|
||||
skip_expr: None,
|
||||
preprocessor_module: None,
|
||||
chat_input_enabled: None,
|
||||
env_variables: None,
|
||||
};
|
||||
// this is a new flow being pushed, flow_status is set to flow_value:
|
||||
let flow_status: FlowStatus = FlowStatus::new(&flow_value);
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
<PropPickerWrapper
|
||||
alwaysOn
|
||||
notSelectable
|
||||
env={flowStore.val.value.env_variables}
|
||||
pickableProperties={stepPropPicker.pickableProperties}
|
||||
on:select={({ detail }) => {
|
||||
editor?.insertAtCursor(detail)
|
||||
@@ -59,7 +60,8 @@
|
||||
bind:code={branch.expr}
|
||||
class="small-editor border "
|
||||
shouldBindKey={false}
|
||||
extraLib={stepPropPicker.extraLib}
|
||||
extraLib={stepPropPicker.extraLib +
|
||||
`\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`}
|
||||
/>
|
||||
</div>
|
||||
</PropPickerWrapper>
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { dfs } from '$lib/components/flows/dfs'
|
||||
import FlowCard from '../common/FlowCard.svelte'
|
||||
import { Alert, Badge } from '$lib/components/common'
|
||||
import type { FlowModule, FlowModuleValue, InputTransform, PathScript, RawScript } from '$lib/gen'
|
||||
import { Alert, Button } from '$lib/components/common'
|
||||
import { getContext, setContext } from 'svelte'
|
||||
import type { PropPickerWrapperContext } from '../propPicker/PropPickerWrapper.svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
import Toggle from '../../Toggle.svelte'
|
||||
import InputTransformSchemaForm from '$lib/components/InputTransformSchemaForm.svelte'
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import { Trash, Plus } from 'lucide-svelte'
|
||||
|
||||
interface Props {
|
||||
noEditor: boolean
|
||||
@@ -16,75 +13,51 @@
|
||||
|
||||
let { noEditor }: Props = $props()
|
||||
|
||||
let hideOptional = $state(false)
|
||||
const { flowStateStore, flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const { flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let scriptModules = $derived(
|
||||
dfs(flowStore.val.value.modules, (x) => x)
|
||||
.map((x) => [x.value, x] as [FlowModuleValue, FlowModule])
|
||||
.filter((x) => x[0].type == 'script' || x[0].type == 'rawscript' || x[0].type == 'flow') as [
|
||||
PathScript | RawScript,
|
||||
FlowModule
|
||||
][]
|
||||
)
|
||||
// Initialize env_variables if it doesn't exist
|
||||
if (!flowStore.val.value.env_variables) {
|
||||
flowStore.val.value.env_variables = {}
|
||||
}
|
||||
|
||||
let resources = $derived(
|
||||
Object.fromEntries(
|
||||
scriptModules
|
||||
.map(([v, m]) => [
|
||||
m.id,
|
||||
Object.entries(v.input_transforms)
|
||||
.map((x) => {
|
||||
let schema = flowStateStore.val[m.id]?.schema
|
||||
let val: { argName: string; type: string } | undefined = undefined
|
||||
let envVariables = $derived(flowStore.val.value.env_variables ?? {})
|
||||
let envEntries = $derived(Object.entries(envVariables))
|
||||
|
||||
const [k, inputTransform] = x
|
||||
const v = schema?.properties[k]
|
||||
|
||||
if (
|
||||
v?.format?.includes('resource') &&
|
||||
inputTransform.type === 'static' &&
|
||||
(inputTransform.value === '' ||
|
||||
inputTransform.value === undefined ||
|
||||
inputTransform.value === null)
|
||||
) {
|
||||
val = {
|
||||
argName: k,
|
||||
type: v.format.split('-')[1]
|
||||
}
|
||||
}
|
||||
return val
|
||||
})
|
||||
.filter(Boolean)
|
||||
])
|
||||
.filter((x) => x[1].length > 0)
|
||||
) as {
|
||||
[k: string]: {
|
||||
argName: string
|
||||
type: string
|
||||
}[]
|
||||
function addEnvVariable() {
|
||||
if (!flowStore.val.value.env_variables) {
|
||||
flowStore.val.value.env_variables = {}
|
||||
}
|
||||
)
|
||||
|
||||
let steps = $derived(
|
||||
scriptModules
|
||||
.map(
|
||||
([v, m]) =>
|
||||
[
|
||||
v.input_transforms,
|
||||
Object.entries(v.input_transforms)
|
||||
.filter((x) => {
|
||||
const shouldDisplay = hideOptional
|
||||
? flowStateStore.val[m.id]?.schema?.required?.includes(x[0])
|
||||
: true
|
||||
return x[1].type == 'static' && shouldDisplay
|
||||
})
|
||||
.map((x) => x[0]),
|
||||
m
|
||||
] as [Record<string, InputTransform>, string[], FlowModule]
|
||||
)
|
||||
.filter(([i, f, m]) => f.length > 0)
|
||||
)
|
||||
// Find a unique key name
|
||||
let counter = 1
|
||||
let newKey = `NEW_VAR`
|
||||
while (flowStore.val.value.env_variables[newKey]) {
|
||||
newKey = `NEW_VAR_${counter}`
|
||||
counter++
|
||||
}
|
||||
|
||||
flowStore.val.value.env_variables[newKey] = ''
|
||||
}
|
||||
|
||||
function removeEnvVariable(key: string) {
|
||||
if (flowStore.val.value.env_variables) {
|
||||
delete flowStore.val.value.env_variables[key]
|
||||
}
|
||||
}
|
||||
|
||||
function updateEnvVariableKey(oldKey: string, newKey: string) {
|
||||
if (flowStore.val.value.env_variables && oldKey !== newKey) {
|
||||
const value = flowStore.val.value.env_variables[oldKey]
|
||||
delete flowStore.val.value.env_variables[oldKey]
|
||||
flowStore.val.value.env_variables[newKey] = value
|
||||
}
|
||||
}
|
||||
|
||||
function updateEnvVariableValue(key: string, value: string) {
|
||||
if (flowStore.val.value.env_variables) {
|
||||
flowStore.val.value.env_variables[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
setContext<PropPickerWrapperContext>('PropPickerWrapper', {
|
||||
inputMatches: writable(undefined),
|
||||
@@ -95,63 +68,78 @@
|
||||
</script>
|
||||
|
||||
<div class="min-h-full">
|
||||
<FlowCard {noEditor} title="All Static Inputs">
|
||||
<FlowCard {noEditor} title="Environment Variables">
|
||||
{#snippet header()}
|
||||
<Toggle bind:checked={hideOptional} options={{ left: 'Hide optional inputs' }} />
|
||||
<Button
|
||||
size="xs"
|
||||
color="blue"
|
||||
variant="border"
|
||||
startIcon={{ icon: Plus }}
|
||||
onclick={addEnvVariable}
|
||||
>
|
||||
Add Variable
|
||||
</Button>
|
||||
{/snippet}
|
||||
<div class="min-h-full flex-1">
|
||||
<Alert type="info" title="Static Inputs" class="m-4"
|
||||
>This page centralizes the static inputs of every steps. It is aking to a file containing
|
||||
all constants. Modifying a value here modifies it in the step input directly. It is
|
||||
especially useful when forking a flow to get an overview of all the variables to parametrize
|
||||
that are not exposed directly as flow inputs.</Alert
|
||||
>
|
||||
{#if Object.keys(resources).length > 0}
|
||||
<Alert type="warning" title="Missing resources" class="m-4">
|
||||
The following resources are missing and the flow will not be fully runnable until they are
|
||||
set. Add your own resources:
|
||||
{#each Object.entries(resources) as [id, r]}
|
||||
{#each r as resource}
|
||||
<div class="mt-2">
|
||||
<Badge color="red">{id}</Badge> is missing a resource of type{' '}
|
||||
<Badge color="red">{resource?.type}</Badge> for the input{' '}
|
||||
<Badge color="red">{resource?.argName}</Badge>
|
||||
</div>
|
||||
{/each}
|
||||
{/each}
|
||||
</Alert>
|
||||
{/if}
|
||||
{#if steps.length == 0}
|
||||
<div class="mt-2"></div>
|
||||
{#if flowStore.val.value.modules.length == 0}
|
||||
<Alert type="warning" title="No steps" class="m-4">
|
||||
This flow has no steps. Add a step to see its static inputs.
|
||||
</Alert>
|
||||
{:else}
|
||||
<Alert type="warning" title="No static inputs" class="m-4">
|
||||
This flow has no steps with static inputs. Add a step with static inputs to see them
|
||||
here.
|
||||
</Alert>
|
||||
{/if}
|
||||
{/if}
|
||||
{#each steps as [_args, filter, m], index (m.id + index)}
|
||||
{#if filter.length > 0}
|
||||
<div class="relative h-full border-t p-4">
|
||||
<h2 class="sticky w-full top-0 z-10 inline-flex items-center py-2">
|
||||
<span class="mr-4">{m.summary || m.value['path'] || 'Inline script'}</span>
|
||||
<Badge large color="indigo">{m.id}</Badge>
|
||||
</h2>
|
||||
<Alert type="info" title="Environment Variables" class="m-4">
|
||||
Define environment variables that can be referenced throughout your flow using the
|
||||
<code class="text-xs">env</code> prefix (e.g., <code class="text-xs">env.FOO</code>).
|
||||
These variables are stored in the flow definition and can be easily updated when deploying
|
||||
or forking the flow.
|
||||
</Alert>
|
||||
|
||||
<InputTransformSchemaForm
|
||||
noDynamicToggle
|
||||
{filter}
|
||||
class="mt-2"
|
||||
schema={flowStateStore.val[m.id]?.schema ?? {}}
|
||||
bind:args={steps[index][0]}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if envEntries.length === 0}
|
||||
<Alert type="warning" title="No environment variables" class="m-4">
|
||||
This flow has no environment variables defined. Click "Add Variable" to create one.
|
||||
</Alert>
|
||||
{:else}
|
||||
<div class="p-4 space-y-3">
|
||||
{#each envEntries as [key, value], index (key + index)}
|
||||
<div class="flex items-center gap-2 p-3 border rounded bg-surface-secondary">
|
||||
<div class="flex-1 grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<label class="text-xs text-secondary mb-1 block">Variable Name</label>
|
||||
<input
|
||||
type="text"
|
||||
class="windmill-input"
|
||||
placeholder="VARIABLE_NAME"
|
||||
value={key}
|
||||
oninput={(e) => {
|
||||
const target = e.currentTarget as HTMLInputElement
|
||||
const newKey = target.value
|
||||
if (newKey && newKey !== key) {
|
||||
updateEnvVariableKey(key, newKey)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs text-secondary mb-1 block">Value</label>
|
||||
<input
|
||||
type="text"
|
||||
class="windmill-input"
|
||||
placeholder="value"
|
||||
value={value}
|
||||
oninput={(e) => {
|
||||
const target = e.currentTarget as HTMLInputElement
|
||||
updateEnvVariableValue(key, target.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="red"
|
||||
variant="border"
|
||||
startIcon={{ icon: Trash }}
|
||||
iconOnly
|
||||
onclick={() => removeEnvVariable(key)}
|
||||
aria-label="Remove variable"
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</FlowCard>
|
||||
</div>
|
||||
|
||||
@@ -288,6 +288,7 @@
|
||||
<PropPickerWrapper
|
||||
notSelectable
|
||||
flow_input={stepPropPicker.pickableProperties.flow_input}
|
||||
env={flowStore.val.value.env_variables}
|
||||
pickableProperties={stepPropPicker.pickableProperties}
|
||||
on:select={({ detail }) => {
|
||||
parallelismEditor?.insertAtCursor(detail)
|
||||
@@ -315,7 +316,8 @@
|
||||
}
|
||||
class="small-editor"
|
||||
shouldBindKey={false}
|
||||
extraLib={stepPropPicker.extraLib}
|
||||
extraLib={stepPropPicker.extraLib +
|
||||
`\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`}
|
||||
/>
|
||||
</PropPickerWrapper>
|
||||
</div>
|
||||
@@ -375,6 +377,7 @@
|
||||
<PropPickerWrapper
|
||||
alwaysOn
|
||||
notSelectable
|
||||
env={flowStore.val.value.env_variables}
|
||||
pickableProperties={stepPropPicker.pickableProperties}
|
||||
on:select={({ detail }) => {
|
||||
if ($flowPropPickerConfig?.insertionMode == CONNECT) {
|
||||
@@ -400,7 +403,8 @@
|
||||
bind:code={mod.value.iterator.expr}
|
||||
class="small-editor"
|
||||
shouldBindKey={false}
|
||||
extraLib={stepPropPicker.extraLib}
|
||||
extraLib={stepPropPicker.extraLib +
|
||||
`\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`}
|
||||
/>
|
||||
</PropPickerWrapper>
|
||||
</div>
|
||||
|
||||
@@ -166,6 +166,7 @@
|
||||
<PropPickerWrapper
|
||||
notSelectable
|
||||
flow_input={stepPropPicker.pickableProperties.flow_input}
|
||||
env={flowStore.val.value.env_variables}
|
||||
pickableProperties={undefined}
|
||||
result={earlyStopResult}
|
||||
extraResults={isLoop ? { all_iters: result } : undefined}
|
||||
@@ -181,6 +182,7 @@
|
||||
class="few-lines-editor"
|
||||
extraLib={`declare const result = ${JSON.stringify(earlyStopResult)};` +
|
||||
`\n declare const flow_input = ${JSON.stringify(stepPropPicker.pickableProperties.flow_input)};` +
|
||||
`\n declare const env = ${JSON.stringify(flowStore.val.value.env_variables)};` +
|
||||
(isLoop ? `\ndeclare const all_iters = ${JSON.stringify(result)};` : '')}
|
||||
/>
|
||||
</PropPickerWrapper>
|
||||
@@ -302,6 +304,7 @@
|
||||
<PropPickerWrapper
|
||||
notSelectable
|
||||
flow_input={stepPropPicker.pickableProperties.flow_input}
|
||||
env={flowStore.val.value.env_variables}
|
||||
pickableProperties={undefined}
|
||||
{result}
|
||||
on:select={({ detail }) => {
|
||||
@@ -315,7 +318,8 @@
|
||||
bind:code={flowModule.stop_after_all_iters_if.expr}
|
||||
class="few-lines-editor"
|
||||
extraLib={`declare const result = ${JSON.stringify(result)};` +
|
||||
`\ndeclare const flow_input = ${JSON.stringify(stepPropPicker.pickableProperties.flow_input)};`}
|
||||
`\ndeclare const flow_input = ${JSON.stringify(stepPropPicker.pickableProperties.flow_input)};` +
|
||||
`\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`}
|
||||
/>
|
||||
</PropPickerWrapper>
|
||||
</div>
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
<div class="border w-full">
|
||||
<PropPickerWrapper
|
||||
notSelectable
|
||||
env={flowStore.val.value.env_variables}
|
||||
pickableProperties={stepPropPicker.pickableProperties}
|
||||
on:select={({ detail }) => {
|
||||
editor?.insertAtCursor(detail)
|
||||
@@ -82,7 +83,8 @@
|
||||
lang="javascript"
|
||||
bind:code={flowModule.skip_if.expr}
|
||||
class="few-lines-editor"
|
||||
extraLib={stepPropPicker.extraLib}
|
||||
extraLib={stepPropPicker.extraLib +
|
||||
`\ndeclare const env = ${JSON.stringify(flowStore.val.value.env_variables)};`}
|
||||
/>
|
||||
</PropPickerWrapper>
|
||||
</div>
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
noFlowPlugConnect={true}
|
||||
flow_input={stepPropPicker.pickableProperties.flow_input}
|
||||
notSelectable
|
||||
env={flowStore.val.value.env_variables}
|
||||
{result}
|
||||
displayContext={false}
|
||||
pickableProperties={undefined}
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
import EditableSchemaDrawer from '$lib/components/schema/EditableSchemaDrawer.svelte'
|
||||
import AddProperty from '$lib/components/schema/AddProperty.svelte'
|
||||
|
||||
const { selectedId, flowStateStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const { selectedId, flowStateStore, flowStore } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const result = flowStateStore.val[$selectedId]?.previewResult ?? {}
|
||||
let editor: SimpleEditor | undefined = $state(undefined)
|
||||
|
||||
@@ -219,6 +220,7 @@
|
||||
<PropPickerWrapper
|
||||
{result}
|
||||
noFlowPlugConnect
|
||||
env={flowStore.val.value.env_variables}
|
||||
displayContext={false}
|
||||
pickableProperties={undefined}
|
||||
on:select={({ detail }) => {
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
<PropPickerWrapper
|
||||
flow_input={stepPropPicker.pickableProperties.flow_input}
|
||||
notSelectable
|
||||
env={flowStore.val.value.env_variables}
|
||||
pickableProperties={stepPropPicker.pickableProperties}
|
||||
on:select={({ detail }) => {
|
||||
editor?.insertAtCursor(detail)
|
||||
|
||||
@@ -173,6 +173,7 @@
|
||||
{#if stepPropPicker}
|
||||
<PropPickerWrapper
|
||||
notSelectable
|
||||
env={flowStore?.val.value.env_variables}
|
||||
pickableProperties={stepPropPicker.pickableProperties}
|
||||
{result}
|
||||
on:select={({ detail }) => {
|
||||
@@ -186,7 +187,8 @@
|
||||
bind:code={flowModuleRetry.retry_if.expr}
|
||||
class="few-lines-editor"
|
||||
extraLib={`declare const result = ${JSON.stringify(result)};` +
|
||||
`\ndeclare const flow_input = ${JSON.stringify(stepPropPicker.pickableProperties.flow_input || {})};`}
|
||||
`\ndeclare const flow_input = ${JSON.stringify(stepPropPicker.pickableProperties.flow_input || {})};` +
|
||||
`\ndeclare const env = ${JSON.stringify(flowStore?.val.value.env_variables)};`}
|
||||
/>
|
||||
</PropPickerWrapper>
|
||||
{:else}
|
||||
@@ -195,7 +197,8 @@
|
||||
lang="javascript"
|
||||
bind:code={flowModuleRetry.retry_if.expr}
|
||||
class="few-lines-editor"
|
||||
extraLib={`declare const result = ${JSON.stringify(result)};`}
|
||||
extraLib={`declare const result = ${JSON.stringify(result)};` +
|
||||
`\ndeclare const env = ${JSON.stringify(flowStore?.val.value.env_variables)};`}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<DollarSign size={14} />
|
||||
</button>
|
||||
{#snippet text()}
|
||||
Static Inputs
|
||||
Environment Variables
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{/if}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
export let result: any = undefined
|
||||
export let extraResults: any = undefined
|
||||
export let flow_input: any = undefined
|
||||
export let env: any = undefined
|
||||
export let error: boolean = false
|
||||
export let displayContext = true
|
||||
export let notSelectable = false
|
||||
@@ -125,6 +126,7 @@
|
||||
{result}
|
||||
{extraResults}
|
||||
{flow_input}
|
||||
{env}
|
||||
allowCopy={!notSelectable && !$propPickerConfig}
|
||||
on:select={({ detail }) => {
|
||||
dispatch('select', detail)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
export let result: any
|
||||
export let extraResults: any = undefined
|
||||
export let flow_input: any = undefined
|
||||
export let env: any = undefined
|
||||
</script>
|
||||
|
||||
<div class="w-full px-2">
|
||||
@@ -14,8 +15,14 @@
|
||||
</div>
|
||||
{#if flow_input}
|
||||
<span class="font-normal text-sm text-secondary">Flow Input</span>
|
||||
<div class="overflow-y-auto w-full">
|
||||
<div class="overflow-y-auto mb-2 w-full">
|
||||
<ObjectViewer {allowCopy} json={flow_input} prefix="flow_input" on:select />
|
||||
</div>
|
||||
{/if}
|
||||
{#if env}
|
||||
<span class="font-normal text-sm text-secondary">Environment Variables</span>
|
||||
<div class="overflow-y-auto w-full">
|
||||
<ObjectViewer {allowCopy} json={env} prefix="env" on:select />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -65,6 +65,11 @@ components:
|
||||
chat_input_enabled:
|
||||
type: boolean
|
||||
description: Whether this flow accepts chat-style input
|
||||
env_variables:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: Environment variables for the flow
|
||||
required:
|
||||
- modules
|
||||
|
||||
|
||||
Reference in New Issue
Block a user