# Authoring web forms (CustomInputForm) Flows that should render a web form start with a `CustomInputForm` node (this package — the `kirche` category in the Langflow palette). The API derives a JSON Schema + uiSchema from the node's template (`app/flows/input_schema.py`) and the webapp renders it with RJSF. ## Adding a new form field 1. Open the Langflow authoring UI (http://localhost:7860) and load the flow from `LANGFLOW_LOAD_FLOWS_PATH` (canonical files are preloaded). 2. Edit the `CustomInputForm` node: every entry in its `inputs` list is one web-form field. Add an `lfx.io` input, e.g. ```python from lfx.io import DropdownInput DropdownInput( name="room", # JSON-Schema property key display_name="Room", # form label (title) info="Where should it take place", # help text (description) options=["Chapel", "Hall"], # dropdown choices (enum) value="Chapel", # prefill (default) required=True, # marks the field required ) ``` Supported input types → widgets: `StrInput`/`MessageTextInput` → text, `MultilineInput` → textarea, `SecretStrInput` → password, `IntInput` → integer, `FloatInput` → number, `BoolInput` → checkbox, `DropdownInput` → select, `MultiselectInput` → multi-select, `SliderInput` → slider (needs `range_spec`), `FileInput` → text stub (real upload later). 3. Add the new field to `collect()` so it reaches the JSON output. 4. **Wire the envelope**: keep the `ChatInput → form_values` edge (hidden input, `show=False`). Submitted web-form values arrive there as `{"payload": {...}}` and are merged over the defaults. Never make form fields required *with* an incoming edge — that's the envelope's job. 5. Export the flow (JSON) into `backend/flows/.json` and commit. The API watcher syncs + deploys it automatically; the form schema is regenerated per version. ## Conventions the pipeline relies on - `required=True` on a template field: `lfx validate --level 4` demands a value or an edge, so the canonical-flow generator (`backend/scripts/make_flow_fixtures.py`) writes a single-space sentinel value for such fields. The form parser treats whitespace-only values as "no default" — don't ship real whitespace defaults. - Field key = `name` (stable); label = `display_name`; help = `info`. - Unknown future input types degrade to a plain text field in the web form (parser fallback), so adding exotic lfx inputs never breaks runs. ## Regenerating the bundled fixtures After editing the component source (`form_inputs.py`), refresh the canonical + test-fixture exports: docker compose exec api /opt/venv/bin/python scripts/make_flow_fixtures.py docker compose exec -T api uv run pytest tests/test_lfx_validate.py