From 7e96c53a2044eec8cfe326e8db4cebea7c2b7a8e Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 27 Jul 2026 07:11:38 +0200 Subject: [PATCH] feat: multiselect valve input type with static or dynamic options (#26884) Adds a multiselect input type for Valves and UserValves so plugin authors can let users pick multiple values from static or runtime-resolved options instead of maintaining comma-separated text fields with hardcoded allowed-value lists in the description. ENABLED_ITEMS: list[str] = Field( default=["foo"], json_schema_extra={"input": {"type": "multiselect", "options": "get_item_options"}}, ) @classmethod def get_item_options(cls): return [{"value": "foo", "label": "Foo"}, {"value": "bar", "label": "Bar"}] Options accept the same shapes as the existing select input: either a static list (strings or {value, label} dicts) or a classmethod name resolved at request time (including __user__ context for UserValves). No backend changes are needed because resolve_valves_schema_options already resolves options independently of the input type. The new MultiSelect component follows the existing Select portal dropdown pattern and renders checkbox rows that stay open while toggling, with the selected labels shown in the trigger. Values bind as a real string array end to end: the array-to-comma-string conversions in the chat controls valves panel and the valves modal are skipped for multiselect fields, so the stored valve is a native list[str] validated by Pydantic. Requested in #26848. --- .../components/chat/Controls/Valves.svelte | 6 + src/lib/components/common/MultiSelect.svelte | 153 ++++++++++++++++++ src/lib/components/common/Valves.svelte | 17 +- .../workspace/common/ValvesModal.svelte | 3 + 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 src/lib/components/common/MultiSelect.svelte diff --git a/src/lib/components/chat/Controls/Valves.svelte b/src/lib/components/chat/Controls/Valves.svelte index 880b55b298..898df9f4ee 100644 --- a/src/lib/components/chat/Controls/Valves.svelte +++ b/src/lib/components/chat/Controls/Valves.svelte @@ -62,6 +62,9 @@ // Convert array to string for (const property in valvesSpec.properties) { if (valvesSpec.properties[property]?.type === 'array') { + if (valvesSpec.properties[property]?.input?.type === 'multiselect') { + continue; + } valves[property] = (valves[property] ?? []).join(','); } } @@ -75,6 +78,9 @@ // Convert string to array for (const property in valvesSpec.properties) { if (valvesSpec.properties[property]?.type === 'array') { + if (valvesSpec.properties[property]?.input?.type === 'multiselect') { + continue; + } valves[property] = (valves[property] ?? '').split(',').map((v) => v.trim()); } } diff --git a/src/lib/components/common/MultiSelect.svelte b/src/lib/components/common/MultiSelect.svelte new file mode 100644 index 0000000000..e81199d821 --- /dev/null +++ b/src/lib/components/common/MultiSelect.svelte @@ -0,0 +1,153 @@ + + + + + + +{#if open} +
+
+ {#each items as item} + + {/each} +
+
+{/if} diff --git a/src/lib/components/common/Valves.svelte b/src/lib/components/common/Valves.svelte index e174a4ef32..8cca8fd583 100644 --- a/src/lib/components/common/Valves.svelte +++ b/src/lib/components/common/Valves.svelte @@ -9,6 +9,7 @@ import Switch from './Switch.svelte'; import SensitiveInput from './SensitiveInput.svelte'; import NativeSelect from './NativeSelect.svelte'; + import MultiSelect from './MultiSelect.svelte'; import MapSelector from './Valves/MapSelector.svelte'; export let valvesSpec = null; @@ -37,7 +38,11 @@ // Initialize to custom value if ((propertySpec?.type ?? null) === 'array') { const defaultArray = propertySpec?.default ?? []; - valves[property] = Array.isArray(defaultArray) ? defaultArray.join(', ') : ''; + if (propertySpec?.input?.type === 'multiselect') { + valves[property] = Array.isArray(defaultArray) ? [...defaultArray] : []; + } else { + valves[property] = Array.isArray(defaultArray) ? defaultArray.join(', ') : ''; + } } else { valves[property] = propertySpec?.default ?? ''; } @@ -95,6 +100,16 @@ /> + {:else if valvesSpec.properties[property]?.input?.type === 'multiselect' && valvesSpec.properties[property]?.input?.options} + { + dispatch('change'); + }} + /> {:else if (valvesSpec.properties[property]?.type ?? null) !== 'string'}