Skip to content

Commit 7ad6e87

Browse files
authored
feat(flow): allow additional inputs in chat mode (#7503)
* draft * better * cleaning * cleaning * show warning if missing required * make it work with dyn args * cleaning
1 parent 4763eda commit 7ad6e87

File tree

7 files changed

+272
-56
lines changed

7 files changed

+272
-56
lines changed

frontend/src/lib/components/FlowPreviewContent.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,12 +385,13 @@
385385
<div class="flex flex-row justify-center w-full">
386386
<FlowChat
387387
useStreaming={shouldUseStreaming}
388-
onRunFlow={async (userMessage, conversationId) => {
389-
await runPreview({ user_message: userMessage }, undefined, conversationId)
388+
onRunFlow={async (userMessage, conversationId, additionalInputs) => {
389+
await runPreview({ user_message: userMessage, ...(additionalInputs ?? {}) }, undefined, conversationId)
390390
return jobId ?? ''
391391
}}
392392
hideSidebar={true}
393393
path={$pathStore}
394+
inputSchema={flowStore.val.schema}
394395
/>
395396
</div>
396397
{:else}

frontend/src/lib/components/flows/content/FlowInput.svelte

Lines changed: 94 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
Code,
2424
Save,
2525
X,
26-
Check
26+
Check,
27+
Settings2
2728
} from 'lucide-svelte'
2829
import CaptureIcon from '$lib/components/triggers/CaptureIcon.svelte'
2930
import FlowInputEditor from './FlowInputEditor.svelte'
@@ -84,7 +85,10 @@
8485
8586
// Detect if we're in "review mode" (AI has made schema changes that are pending)
8687
const hasAiSchemaChanges = $derived(
87-
Boolean(diffManager?.moduleActions[SPECIAL_MODULE_IDS.INPUT]?.pending && diffManager?.beforeFlow?.schema)
88+
Boolean(
89+
diffManager?.moduleActions[SPECIAL_MODULE_IDS.INPUT]?.pending &&
90+
diffManager?.beforeFlow?.schema
91+
)
8892
)
8993
9094
let chatInputEnabled = $state(Boolean(flowStore.val.value?.chat_input_enabled))
@@ -98,6 +102,9 @@
98102
)
99103
})
100104
let showChatModeWarning = $state(false)
105+
let showAdditionalInputs = $state(false)
106+
let chatInputsEditTab = $state(false)
107+
let chatInputsAddPropertyV2: AddPropertyV2 | undefined = $state(undefined)
101108
102109
let addPropertyV2: AddPropertyV2 | undefined = $state(undefined)
103110
let previewSchema: Record<string, any> | undefined = $state(undefined)
@@ -435,9 +442,7 @@
435442
const parentPath = path.slice(0, -1)
436443
437444
const getSchemaAtPath = (schema: Record<string, any>) =>
438-
parentPath.length === 0
439-
? schema
440-
: getNestedProperty(schema, parentPath, 'properties')
445+
parentPath.length === 0 ? schema : getNestedProperty(schema, parentPath, 'properties')
441446
442447
const getProperties = (schema: Record<string, any>) => getSchemaAtPath(schema)?.properties
443448
@@ -457,9 +462,7 @@
457462
if (targetProperties && arg.label in targetProperties) {
458463
delete targetProperties[arg.label]
459464
if (targetSchemaAtPath?.order) {
460-
targetSchemaAtPath.order = targetSchemaAtPath.order.filter(
461-
(x: string) => x !== arg.label
462-
)
465+
targetSchemaAtPath.order = targetSchemaAtPath.order.filter((x: string) => x !== arg.label)
463466
}
464467
}
465468
}
@@ -502,9 +505,13 @@
502505
503506
async function runFlowWithMessage(
504507
message: string,
505-
conversationId: string
508+
conversationId: string,
509+
additionalInputs?: Record<string, any>
506510
): Promise<string | undefined> {
507-
previewArgs.val = { user_message: message }
511+
previewArgs.val = {
512+
user_message: message,
513+
...(additionalInputs ?? {})
514+
}
508515
const jobId = await onTestFlow?.(conversationId)
509516
return jobId
510517
}
@@ -641,29 +648,91 @@
641648
<FlowCard {noEditor} title="Flow Input">
642649
{#snippet action()}
643650
{#if !disabled}
644-
<Toggle
645-
size="sm"
646-
bind:checked={chatInputEnabled}
647-
on:change={() => {
648-
handleToggleChatMode()
649-
}}
650-
options={{
651-
right: 'Chat Mode',
652-
rightTooltip:
653-
'When enabled, the flow execution page will show a chat interface where each message sent runs the flow with the message as "user_message" input parameter. The flow schema will be automatically set to accept only a user_message string input.'
654-
}}
655-
/>
651+
<div class="flex items-center gap-2">
652+
<Toggle
653+
size="sm"
654+
bind:checked={chatInputEnabled}
655+
on:change={() => {
656+
handleToggleChatMode()
657+
}}
658+
options={{
659+
right: 'Chat Mode',
660+
rightTooltip:
661+
'When enabled, the flow execution page will show a chat interface where each message sent runs the flow with the message as "user_message" input parameter. The flow schema will be automatically set to accept only a user_message string input.'
662+
}}
663+
/>
664+
{#if flowStore.val.value?.chat_input_enabled}
665+
<Button
666+
size="xs"
667+
variant="border"
668+
color={showAdditionalInputs ? 'blue' : 'light'}
669+
startIcon={{ icon: Settings2 }}
670+
title="Manage inputs"
671+
on:click={() => (showAdditionalInputs = !showAdditionalInputs)}
672+
>
673+
Manage inputs
674+
</Button>
675+
{/if}
676+
</div>
656677
{/if}
657678
{/snippet}
658679
{#if !disabled}
659680
<div class="flex flex-col h-full">
660681
{#if flowStore.val.value?.chat_input_enabled}
661682
<div class="flex flex-col h-full">
683+
{#if showAdditionalInputs}
684+
<div class="border-b p-2">
685+
<EditableSchemaForm
686+
bind:schema={flowStore.val.schema}
687+
hiddenArgs={['user_message']}
688+
isFlowInput
689+
editTab={chatInputsEditTab ? 'inputEditor' : undefined}
690+
showDynOpt
691+
bind:dynCode
692+
bind:dynLang
693+
on:delete={(e) => {
694+
chatInputsAddPropertyV2?.handleDeleteArgument([e.detail])
695+
}}
696+
>
697+
{#snippet openEditTab()}
698+
<Button
699+
size="xs"
700+
variant={chatInputsEditTab ? 'contained' : 'border'}
701+
color={chatInputsEditTab ? 'blue' : 'light'}
702+
startIcon={{ icon: chatInputsEditTab ? ChevronRight : Pen }}
703+
title={chatInputsEditTab ? 'Close editor' : 'Edit inputs'}
704+
onClick={() => {
705+
chatInputsEditTab = !chatInputsEditTab
706+
}}
707+
/>
708+
{/snippet}
709+
{#snippet addProperty()}
710+
<AddPropertyV2
711+
bind:this={chatInputsAddPropertyV2}
712+
bind:schema={flowStore.val.schema}
713+
onAddNew={() => {}}
714+
>
715+
{#snippet trigger()}
716+
<Button
717+
size="xs"
718+
color="light"
719+
startIcon={{ icon: Plus }}
720+
title="Add additional input"
721+
>
722+
Add input
723+
</Button>
724+
{/snippet}
725+
</AddPropertyV2>
726+
{/snippet}
727+
</EditableSchemaForm>
728+
</div>
729+
{/if}
662730
<FlowChat
663731
onRunFlow={runFlowWithMessage}
664732
path={$pathStore}
665733
hideSidebar={true}
666734
useStreaming={shouldUseStreaming}
735+
inputSchema={flowStore.val.schema}
667736
/>
668737
</div>
669738
{:else}
@@ -756,7 +825,7 @@
756825
disabled={!previewSchema}
757826
shortCut={{ Icon: CornerDownLeft, hide: false, withoutModifier: true }}
758827
startIcon={{ icon: Check }}
759-
on:click={() => {
828+
onClick={() => {
760829
applySchemaAndArgs()
761830
connectFirstNode()
762831
}}
@@ -774,7 +843,7 @@
774843
size="xs"
775844
startIcon={{ icon: X }}
776845
shortCut={{ key: 'esc', withoutModifier: true }}
777-
on:click={() => {
846+
onClick={() => {
778847
if (hasAiSchemaChanges) {
779848
if (diffManager?.beforeFlow?.schema) {
780849
diffManager.rejectModule(SPECIAL_MODULE_IDS.INPUT, flowStore)
@@ -928,7 +997,7 @@
928997
disabled={runDisabled || !isValid}
929998
size="xs"
930999
shortCut={{ Icon: CornerDownLeft, hide: false }}
931-
on:click={() => {
1000+
onClick={() => {
9321001
runPreview()
9331002
}}
9341003
>

frontend/src/lib/components/flows/conversations/FlowChat.svelte

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,25 @@
66
import { untrack } from 'svelte'
77
88
interface Props {
9-
onRunFlow: (userMessage: string, conversationId: string) => Promise<string | undefined>
9+
onRunFlow: (
10+
userMessage: string,
11+
conversationId: string,
12+
additionalInputs?: Record<string, any>
13+
) => Promise<string | undefined>
1014
useStreaming?: boolean
1115
deploymentInProgress?: boolean
1216
path: string
1317
hideSidebar?: boolean
18+
inputSchema?: Record<string, any>
1419
}
1520
1621
let {
1722
onRunFlow,
1823
deploymentInProgress = false,
1924
useStreaming = false,
2025
path,
21-
hideSidebar = false
26+
hideSidebar = false,
27+
inputSchema = undefined,
2228
}: Props = $props()
2329
2430
const manager = createFlowChatManager()
@@ -42,11 +48,27 @@
4248
})
4349
}
4450
})
51+
52+
// Derive additional inputs schema (excluding user_message) for chat mode
53+
const additionalInputsSchema = $derived.by(() => {
54+
const props = inputSchema?.properties ?? {}
55+
const filtered = Object.fromEntries(
56+
Object.entries(props).filter(([k]) => k !== 'user_message')
57+
)
58+
if (Object.keys(filtered).length === 0) return undefined
59+
const required = inputSchema?.required
60+
const requiredArray: string[] = Array.isArray(required) ? required : []
61+
return {
62+
...inputSchema,
63+
properties: filtered,
64+
required: requiredArray.filter((k: string) => k !== 'user_message')
65+
}
66+
})
4567
</script>
4668

4769
<div class="flex border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden flex-1">
4870
{#if !hideSidebar}
4971
<FlowConversationsSidebar {manager} />
5072
{/if}
51-
<FlowChatInterface {manager} {deploymentInProgress} />
73+
<FlowChatInterface {manager} {deploymentInProgress} {additionalInputsSchema} {path} />
5274
</div>

0 commit comments

Comments
 (0)