feat: Adding the new Create Data component#9523
Conversation
This PR enhances the DataOperationsComponent by introducing two new powerful operations: JSON Path: Allows extracting values from nested JSON structures using path expressions, enabling more flexible data selection. JSON Query: Integrates jq to support advanced JSON queries, including filters, projections, and transformations. Additional improvements: Input handling and validations for both new operations. Enhanced error messages and logs for better debugging. Extended UI field visibility logic in update_build_config to support dynamic path selection from JSON. These updates significantly expand the component's capabilities for parsing, transforming, and querying structured JSON data.
feat: Adding new operations to the Data Operations component
This update extends the conditional_router.py logic by introducing two new operators: is blank: Evaluates to true when the target value is None, an empty string, or whitespace only. is not blank: Evaluates to true when the target value contains any non-whitespace characters. These operators enhance conditional routing by enabling checks for empty or missing values, improving flow control in scenarios where data presence or absence must be validated.
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughAdds two operators (“is blank”, “is not blank”) to ConditionalRouterComponent with corresponding evaluation and build-config updates. Extends DataOperationsComponent with JSON path and jq query capabilities, recursive key ops, UI inputs, and build-time path extraction. Introduces a new dynamic form component (CrateData) that generates inputs from table-config and outputs collected Data/Message. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Router as ConditionalRouterComponent
participant Downstream as Targets
User->>Router: input_text, operator, match_text, case_sensitive
alt operator == "is blank"
Router->>Router: check empty/whitespace
else operator == "is not blank"
Router->>Router: check any non-whitespace
else other operators
Router->>Router: evaluate per existing logic
end
Router-->>Downstream: route based on evaluation result
note over Router: Build-config hides case_sensitive/match_text for\nregex/blank operators where applicable
sequenceDiagram
autonumber
actor User
participant Comp as DataOperationsComponent
participant jq as jq/repair_json
User->>Comp: action="Mapped JSON", mapped_json_display, selected_key
Comp->>Comp: update_build_config: parse JSON, extract_all_paths
Comp->>jq: json_path via jq on normalized data
jq-->>Comp: path result
Comp-->>User: Data(result)
User->>Comp: action="jQuery", query
Comp->>jq: repair_json + jq(query)
jq-->>Comp: query result/error
Comp-->>User: Data(result) or error
sequenceDiagram
autonumber
actor User
participant Crate as CrateData
participant UI as Builder
User->>Crate: form_fields (table)
Crate->>Crate: update_build_config: create dynamic_* inputs
UI-->>User: Renders dynamic inputs
User->>Crate: provide values / connect handles
Crate->>Crate: get_dynamic_values (normalize, track connections)
User->>Crate: run process_form
Crate-->>User: Data(form_data)
User->>Crate: run get_message
Crate-->>User: Message(summary)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
✨ Finishing Touches🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
|
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (11)
src/backend/base/langflow/components/logic/conditional_router.py (3)
98-101: Case-folding exemption is reasonable, but reconsider regex behaviorExcluding "regex", "is blank", and "is not blank" from automatic lowercasing is correct. However, hiding case sensitivity for regex (see Lines 169-171) removes the ability to run case-insensitive patterns. If case-insensitive regex is desired, consider honoring
case_sensitivefor regex by applyingre.IGNORECASE.Apply both changes below together if you want to support case-insensitive regex:
- if not case_sensitive and operator not in ["regex", "is blank", "is not blank"]: + if not case_sensitive and operator not in ["regex", "is blank", "is not blank"]: input_text = input_text.lower() match_text = match_text.lower() ... - if operator == "regex": + if operator == "regex": try: - return bool(re.match(match_text, input_text)) + flags = 0 if case_sensitive else re.IGNORECASE + return bool(re.compile(match_text, flags).search(input_text)) except re.error: return False # Return False if the regex is invalidAnd in
update_build_config:- # Hide case_sensitive for regex and blank operators - if field_value in ["regex", "is blank", "is not blank"]: + # Hide case_sensitive for blank operators + if field_value in ["is blank", "is not blank"]: build_config.pop("case_sensitive", None)
117-121: Blank checks are correct; can be simplifiedThe semantics are right and handle None safely. Minor nit: you can simplify with
not input_text or not input_text.strip()andbool(input_text and input_text.strip()).- if operator == "is blank": - return not input_text or input_text.strip() == "" - if operator == "is not blank": - return bool(input_text and input_text.strip() != "") + if operator == "is blank": + return not input_text or not input_text.strip() + if operator == "is not blank": + return bool(input_text and input_text.strip())
169-188: Prefer toggling visibility over popping inputs from build_configPopping inputs removes configuration and can lead to jittery UI or loss of state. Toggle
show=False/Trueinstead to preserve previous values and metadata, mirroring patterns used elsewhere.- # Hide case_sensitive for regex and blank operators - if field_value in ["regex", "is blank", "is not blank"]: - build_config.pop("case_sensitive", None) + # Hide case_sensitive for blank operators + if field_value in ["is blank", "is not blank"]: + if "case_sensitive" in build_config: + build_config["case_sensitive"]["show"] = False elif "case_sensitive" not in build_config: case_sensitive_input = next( (input_field for input_field in self.inputs if input_field.name == "case_sensitive"), None ) if case_sensitive_input: - build_config["case_sensitive"] = case_sensitive_input.to_dict() + build_config["case_sensitive"] = case_sensitive_input.to_dict() + build_config["case_sensitive"]["show"] = True @@ - # Hide match_text for blank operators - if field_value in ["is blank", "is not blank"]: - build_config.pop("match_text", None) + # Hide match_text for blank operators + if field_value in ["is blank", "is not blank"]: + if "match_text" in build_config: + build_config["match_text"]["show"] = False elif "match_text" not in build_config: match_text_input = next( (input_field for input_field in self.inputs if input_field.name == "match_text"), None ) if match_text_input: - build_config["match_text"] = match_text_input.to_dict() + build_config["match_text"] = match_text_input.to_dict() + build_config["match_text"]["show"] = Truesrc/backend/base/langflow/components/processing/new_create_data.py (3)
136-147: Boolean connectivity and Number connectivity may not behave as described
- PR objective states Boolean connects to any type; here BoolInput is created with
input_types=[](and a comment to avoid errors), effectively disabling connections.- Mapping “Number” to IntInput with
input_types=["Text","Message"]might not validate at runtime, since IntInput’s validator expects numeric types.Proposed changes:
- Omit
input_typesfor BoolInput to allow default behavior.- Consider using Str/Message input for numeric, then cast in
process_form, or ensure the framework coerces Message/Text-to-number before IntInput validation.- build_config[dynamic_input_name] = BoolInput( + build_config[dynamic_input_name] = BoolInput( name=dynamic_input_name, display_name=display_name, info=help_text, value=default_bool, - input_types=[], required=required, )If you confirm IntInput accepts Message/Text inputs in this project (via automatic extraction from connected components), no action needed for “Number.” Otherwise, switch Number to MultilineInput with
input_types=["Text","Message"]and parse to int inprocess_form.Also applies to: 237-248
357-371: Honor include_metadata in outputYou expose
include_metadatabut don’t use it. Include connection info when requested.- # Return clean Data object with just the field values - return Data(data=dynamic_values) + # Return Data; optionally include metadata + if self.include_metadata: + return Data(data={"values": dynamic_values, "connections": getattr(self, "_connection_info", {})}) + return Data(data=dynamic_values)
1-9: Minor: unused imports / tighten typing
SecretStrInput,json, andListare unused.-from langflow.io import StrInput, IntInput, BoolInput, MultilineInput, TableInput, Output, SecretStrInput, FloatInput, HandleInput +from langflow.io import StrInput, IntInput, BoolInput, MultilineInput, TableInput, Output, FloatInput, HandleInput @@ -from typing import Any, Dict, List -import json +from typing import Any, Dictsrc/backend/base/langflow/components/processing/data_operations.py (5)
81-93: Path extraction utility — LGTM with a tiny style nitLogic is correct and jq-friendly (“.a.b[0]”). The over-indentation under the list branch is harmless but will be auto-fixed by formatting.
282-285: Duplicate method definition: remove the first operation_exception
operation_exceptionis defined twice; keep a single definition to avoid confusion.- def operation_exception(self, operations: list[str]) -> None: - msg = f"{operations} operations are not supported in combination with each other." - raise ValueError(msg) -
427-432: Duplicate method definition: remove the second compare_values
compare_valuesalso appears earlier (Lines 398-404). Remove the duplicated one.- def compare_values(self, item_value: Any, filter_value: str, operator: str) -> bool: - comparison_func = OPERATORS.get(operator) - if comparison_func: - return comparison_func(item_value, filter_value) - return False
201-227: Clarify UI copy: “jQuery” input descriptionThe info says “Used by Parse JSON operation,” but the action is called “jQuery” and you use jq. Update the text to match.
MessageTextInput( name="query", display_name="jQuery", - info="JSON Query to filter the data. Used by Parse JSON operation.", + info="JQ query to filter the data (powered by jq). Used by the jQuery operation.", placeholder=".properties.hs_object_id", show=False, ),
493-508: json_path robustness: prefer normalized data and ensure jq importGiven jq is now imported, consider using
get_normalized_data()so users can pass either Data with nesteddataor a flat dict. Current approach uses.dataonly.- input_payload = self.data[0].data if isinstance(self.data, list) else self.data.data + input_payload = self.get_normalized_data()
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/backend/base/langflow/components/logic/conditional_router.py(4 hunks)src/backend/base/langflow/components/processing/data_operations.py(14 hunks)src/backend/base/langflow/components/processing/new_create_data.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/backend/base/langflow/components/**/*.py
📄 CodeRabbit inference engine (.cursor/rules/backend_development.mdc)
src/backend/base/langflow/components/**/*.py: Add new backend components to the appropriate subdirectory under src/backend/base/langflow/components/
Implement async component methods using async def and await for asynchronous operations
Use asyncio.create_task for background work in async components and ensure proper cleanup on cancellation
Use asyncio.Queue for non-blocking queue operations in async components and handle timeouts appropriately
Files:
src/backend/base/langflow/components/processing/new_create_data.pysrc/backend/base/langflow/components/logic/conditional_router.pysrc/backend/base/langflow/components/processing/data_operations.py
{src/backend/**/*.py,tests/**/*.py,Makefile}
📄 CodeRabbit inference engine (.cursor/rules/backend_development.mdc)
{src/backend/**/*.py,tests/**/*.py,Makefile}: Run make format_backend to format Python code before linting or committing changes
Run make lint to perform linting checks on backend Python code
Files:
src/backend/base/langflow/components/processing/new_create_data.pysrc/backend/base/langflow/components/logic/conditional_router.pysrc/backend/base/langflow/components/processing/data_operations.py
src/backend/**/components/**/*.py
📄 CodeRabbit inference engine (.cursor/rules/icons.mdc)
In your Python component class, set the
iconattribute to a string matching the frontend icon mapping exactly (case-sensitive).
Files:
src/backend/base/langflow/components/processing/new_create_data.pysrc/backend/base/langflow/components/logic/conditional_router.pysrc/backend/base/langflow/components/processing/data_operations.py
🧬 Code graph analysis (2)
src/backend/base/langflow/components/processing/new_create_data.py (3)
src/backend/base/langflow/inputs/inputs.py (7)
StrInput(128-184)IntInput(343-375)MultilineInput(259-269)TableInput(38-73)SecretStrInput(285-340)FloatInput(378-410)HandleInput(76-87)src/backend/base/langflow/schema/message.py (2)
Message(39-288)json(299-301)src/backend/base/langflow/schema/data.py (1)
Data(23-285)
src/backend/base/langflow/components/processing/data_operations.py (3)
src/backend/base/langflow/inputs/inputs.py (5)
DictInput(449-464)DropdownInput(467-491)MessageTextInput(205-256)SortableListInput(535-545)MultilineInput(259-269)src/backend/base/langflow/schema/data.py (1)
Data(23-285)src/backend/base/langflow/utils/component_utils.py (2)
set_current_fields(119-147)set_field_display(56-60)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Update Starter Projects
- GitHub Check: Run Ruff Check and Format
- GitHub Check: Ruff Style Check (3.13)
🔇 Additional comments (3)
src/backend/base/langflow/components/logic/conditional_router.py (1)
40-42: New operators added — LGTMAdding "is blank" and "is not blank" to the operator list is consistent with the component’s purpose and improves UX. No concerns.
src/backend/base/langflow/components/processing/data_operations.py (2)
239-266: JSON Query path — solid, with good error messagingThe repair, unwrap, jq compile, and result handling are sensible. Nice logging. No change requested.
471-481: Operations update and mapped JSON parsing — LGTM after missing imports fixThe field toggling and dynamic options population look good. With the added
import json, this path should work as intended.Also applies to: 481-490
| import ast | ||
| from typing import TYPE_CHECKING, Any | ||
| from json_repair import repair_json | ||
|
|
||
| from langflow.custom import Component | ||
| from langflow.inputs import DictInput, DropdownInput, MessageTextInput, SortableListInput | ||
| from langflow.inputs import DictInput, DropdownInput, MessageTextInput, SortableListInput, MultilineInput | ||
| from langflow.io import DataInput, Output |
There was a problem hiding this comment.
Missing imports used later
update_build_config uses json, and json_path uses jq, but they are not imported at module level, causing NameError at runtime.
import ast
-from typing import TYPE_CHECKING, Any
-from json_repair import repair_json
+from typing import TYPE_CHECKING, Any
+import json
+import jq
+from json_repair import repair_json📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import ast | |
| from typing import TYPE_CHECKING, Any | |
| from json_repair import repair_json | |
| from langflow.custom import Component | |
| from langflow.inputs import DictInput, DropdownInput, MessageTextInput, SortableListInput | |
| from langflow.inputs import DictInput, DropdownInput, MessageTextInput, SortableListInput, MultilineInput | |
| from langflow.io import DataInput, Output | |
| import ast | |
| from typing import TYPE_CHECKING, Any | |
| import json | |
| import jq | |
| from json_repair import repair_json | |
| from langflow.custom import Component | |
| from langflow.inputs import DictInput, DropdownInput, MessageTextInput, SortableListInput, MultilineInput | |
| from langflow.io import DataInput, Output |
🤖 Prompt for AI Agents
In src/backend/base/langflow/components/processing/data_operations.py around
lines 1 to 7, the functions update_build_config and json_path reference json and
jq but those modules are not imported, causing NameError; add module-level
imports for json and jq at the top of the file (alongside existing imports) so
json and jq are available to the functions when called.
| class CrateData(Component): | ||
| """ | ||
| Dynamic Form Component | ||
|
|
||
| This component creates dynamic inputs that can receive data from other components | ||
| or be filled manually. It demonstrates advanced dynamic input functionality with | ||
| component connectivity. |
There was a problem hiding this comment.
Typo: class name should match “Create Data”; internal name should be consistent
Public surface and PR title say “Create Data,” but the class is CrateData and name is AdvancedDynamicFormBuilder. This will confuse users and break search/discovery.
-class CrateData(Component):
+class CreateData(Component):
@@
- name = "AdvancedDynamicFormBuilder"
+ name = "CreateData"Also applies to: 52-56
🤖 Prompt for AI Agents
In src/backend/base/langflow/components/processing/new_create_data.py around
lines 10-16 (and also fix lines 52-56), the class is misspelled as CrateData and
its internal name is AdvancedDynamicFormBuilder which contradicts the PR title
"Create Data"; rename the class to CreateData and update the internal
name/display name to "Create Data" (or a consistent identifier like CreateData)
at lines 52-56, and search/replace any local references in this file to preserve
behavior and imports so consumers and discovery match the public surface.
| table_schema=[ | ||
| { | ||
| "name": "field_name", | ||
| "display_name": "Field Name", | ||
| "type": "str", | ||
| "description": "Name for the field (used as both internal name and display label)" | ||
| }, | ||
| { | ||
| "name": "field_type", | ||
| "display_name": "Field Type", | ||
| "type": "str", | ||
| "description": "Type of input field to create", | ||
| "options": ["Text", "Data", "Number", "Handle", "Boolean"], | ||
| "value": "Text" | ||
| } | ||
| ], |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Expose Float in the table options (docstring and mapping already support it)
UI schema omits “Float”, making the float branch unreachable despite support in code and docs.
- "options": ["Text", "Data", "Number", "Handle", "Boolean"],
+ "options": ["Text", "Data", "Number", "Float", "Handle", "Boolean"],📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| table_schema=[ | |
| { | |
| "name": "field_name", | |
| "display_name": "Field Name", | |
| "type": "str", | |
| "description": "Name for the field (used as both internal name and display label)" | |
| }, | |
| { | |
| "name": "field_type", | |
| "display_name": "Field Type", | |
| "type": "str", | |
| "description": "Type of input field to create", | |
| "options": ["Text", "Data", "Number", "Handle", "Boolean"], | |
| "value": "Text" | |
| } | |
| ], | |
| table_schema=[ | |
| { | |
| "name": "field_name", | |
| "display_name": "Field Name", | |
| "type": "str", | |
| "description": "Name for the field (used as both internal name and display label)" | |
| }, | |
| { | |
| "name": "field_type", | |
| "display_name": "Field Type", | |
| "type": "str", | |
| "description": "Type of input field to create", | |
| "options": ["Text", "Data", "Number", "Float", "Handle", "Boolean"], | |
| "value": "Text" | |
| } | |
| ], |
🤖 Prompt for AI Agents
In src/backend/base/langflow/components/processing/new_create_data.py around
lines 66 to 81, the UI schema's field_type options omit "Float" which prevents
the float branch from being reachable; add "Float" to the options array (e.g.,
["Text", "Data", "Number", "Float", "Handle", "Boolean"]) and keep the default
value as "Text" so the float option is selectable from the UI.
| for i, field_config in enumerate(field_value): | ||
| # Safety check to ensure field_config is not None | ||
| if field_config is None: | ||
| continue | ||
|
|
||
| field_name = field_config.get("field_name", f"field_{i}") | ||
| display_name = field_name # Use field_name as display_name | ||
| field_type_option = field_config.get("field_type", "Text") | ||
| default_value = "" # All fields have empty default value | ||
| required = False # All fields are optional by default | ||
| help_text = "" # All fields have empty help text | ||
|
|
||
| # Map field type options to actual field types and input types | ||
| field_type_mapping = { | ||
| "Text": {"field_type": "multiline", "input_types": ["Text", "Message"]}, | ||
| "Data": {"field_type": "data", "input_types": ["Data"]}, | ||
| "Number": {"field_type": "number", "input_types": ["Text", "Message"]}, | ||
| "Handle": {"field_type": "handle", "input_types": ["Text", "Data", "Message"]}, | ||
| "Boolean": {"field_type": "boolean", "input_types": None} | ||
| } | ||
|
|
||
| field_config_mapped = field_type_mapping.get(field_type_option, {"field_type": "text", "input_types": []}) | ||
| field_type = field_config_mapped["field_type"] | ||
| input_types_list = field_config_mapped["input_types"] | ||
|
|
||
| # Create the appropriate input type based on field_type | ||
| dynamic_input_name = f"dynamic_{field_name}" | ||
|
|
||
| if field_type == "text": |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Sanitize dynamic field names to safe identifiers and avoid collisions
Using raw field_name in dynamic_{field_name} risks spaces/special chars and duplicates. Normalize and de-duplicate for robust attribute access.
- field_name = field_config.get("field_name", f"field_{i}")
+ raw_field_name = field_config.get("field_name", f"field_{i}")
+ # allow [a-zA-Z0-9_], collapse whitespace, ensure uniqueness
+ import re
+ safe = re.sub(r"\W+", "_", str(raw_field_name)).strip("_") or f"field_{i}"
+ field_name = f"{safe}_{i}" if safe in (name.split("dynamic_")[-1] for name in build_config.keys()) else safe📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for i, field_config in enumerate(field_value): | |
| # Safety check to ensure field_config is not None | |
| if field_config is None: | |
| continue | |
| field_name = field_config.get("field_name", f"field_{i}") | |
| display_name = field_name # Use field_name as display_name | |
| field_type_option = field_config.get("field_type", "Text") | |
| default_value = "" # All fields have empty default value | |
| required = False # All fields are optional by default | |
| help_text = "" # All fields have empty help text | |
| # Map field type options to actual field types and input types | |
| field_type_mapping = { | |
| "Text": {"field_type": "multiline", "input_types": ["Text", "Message"]}, | |
| "Data": {"field_type": "data", "input_types": ["Data"]}, | |
| "Number": {"field_type": "number", "input_types": ["Text", "Message"]}, | |
| "Handle": {"field_type": "handle", "input_types": ["Text", "Data", "Message"]}, | |
| "Boolean": {"field_type": "boolean", "input_types": None} | |
| } | |
| field_config_mapped = field_type_mapping.get(field_type_option, {"field_type": "text", "input_types": []}) | |
| field_type = field_config_mapped["field_type"] | |
| input_types_list = field_config_mapped["input_types"] | |
| # Create the appropriate input type based on field_type | |
| dynamic_input_name = f"dynamic_{field_name}" | |
| if field_type == "text": | |
| for i, field_config in enumerate(field_value): | |
| # Safety check to ensure field_config is not None | |
| if field_config is None: | |
| continue | |
| raw_field_name = field_config.get("field_name", f"field_{i}") | |
| # allow [a-zA-Z0-9_], collapse whitespace, ensure uniqueness | |
| import re | |
| safe = re.sub(r"\W+", "_", str(raw_field_name)).strip("_") or f"field_{i}" | |
| field_name = ( | |
| f"{safe}_{i}" | |
| if safe in (name.split("dynamic_")[-1] for name in build_config.keys()) | |
| else safe | |
| ) | |
| display_name = field_name # Use field_name as display_name | |
| field_type_option = field_config.get("field_type", "Text") | |
| default_value = "" # All fields have empty default value | |
| required = False # All fields are optional by default | |
| help_text = "" # All fields have empty help text | |
| # Map field type options to actual field types and input types | |
| field_type_mapping = { | |
| "Text": {"field_type": "multiline", "input_types": ["Text", "Message"]}, | |
| "Data": {"field_type": "data", "input_types": ["Data"]}, | |
| "Number": {"field_type": "number", "input_types": ["Text", "Message"]}, | |
| "Handle": {"field_type": "handle", "input_types": ["Text", "Data", "Message"]}, | |
| "Boolean": {"field_type": "boolean", "input_types": None} | |
| } | |
| field_config_mapped = field_type_mapping.get( | |
| field_type_option, | |
| {"field_type": "text", "input_types": []}, | |
| ) | |
| field_type = field_config_mapped["field_type"] | |
| input_types_list = field_config_mapped["input_types"] | |
| # Create the appropriate input type based on field_type | |
| dynamic_input_name = f"dynamic_{field_name}" | |
| if field_type == "text": | |
| … |
🤖 Prompt for AI Agents
In src/backend/base/langflow/components/processing/new_create_data.py around
lines 123 to 151, dynamic field names are generated as f"dynamic_{field_name}"
which can include spaces/special characters and can collide; sanitize and
de-duplicate names by: normalize field_name to a safe identifier (trim, lower,
replace non-alphanumeric characters with underscores, ensure it starts with a
letter or underscore), maintain a set/dict of used identifiers and if a
collision occurs append an incrementing suffix (e.g., _1, _2), and then use that
sanitized unique identifier to build dynamic_input_name; apply this logic before
creating inputs so all downstream attribute access uses the safe, unique name.
| if field_type == "text": | ||
| if input_types_list: | ||
| build_config[dynamic_input_name] = StrInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=f"{help_text} (Can connect to: {', '.join(input_types_list)})", | ||
| value=default_value, | ||
| required=required, | ||
| input_types=input_types_list, | ||
| ) | ||
| else: | ||
| build_config[dynamic_input_name] = StrInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=help_text, | ||
| value=default_value, | ||
| required=required, | ||
| ) | ||
|
|
||
| elif field_type == "multiline": | ||
| if input_types_list: | ||
| build_config[dynamic_input_name] = MultilineInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=f"{help_text} (Can connect to: {', '.join(input_types_list)})", | ||
| value=default_value, | ||
| required=required, | ||
| input_types=input_types_list, | ||
| ) | ||
| else: | ||
| build_config[dynamic_input_name] = MultilineInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=help_text, | ||
| value=default_value, | ||
| required=required, | ||
| ) | ||
|
|
||
| elif field_type == "number": | ||
| try: | ||
| default_int = int(default_value) if default_value else 0 | ||
| except ValueError: | ||
| default_int = 0 | ||
|
|
||
| if input_types_list: | ||
| build_config[dynamic_input_name] = IntInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=f"{help_text} (Can connect to: {', '.join(input_types_list)})", | ||
| value=default_int, | ||
| required=required, | ||
| input_types=input_types_list, | ||
| ) | ||
| else: | ||
| build_config[dynamic_input_name] = IntInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=help_text, | ||
| value=default_int, | ||
| required=required, | ||
| ) | ||
|
|
||
| elif field_type == "float": | ||
| try: | ||
| default_float = float(default_value) if default_value else 0.0 | ||
| except ValueError: | ||
| default_float = 0.0 | ||
|
|
||
| if input_types_list: | ||
| build_config[dynamic_input_name] = FloatInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=f"{help_text} (Can connect to: {', '.join(input_types_list)})", | ||
| value=default_float, | ||
| required=required, | ||
| input_types=input_types_list, | ||
| ) | ||
| else: | ||
| build_config[dynamic_input_name] = FloatInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=help_text, | ||
| value=default_float, | ||
| required=required, | ||
| ) | ||
|
|
||
| elif field_type == "boolean": | ||
| default_bool = default_value.lower() in ["true", "1", "yes"] if default_value else False | ||
|
|
||
| # Boolean fields don't use input_types parameter to avoid errors | ||
| build_config[dynamic_input_name] = BoolInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=help_text, | ||
| value=default_bool, | ||
| input_types=[], | ||
| required=required, | ||
| ) | ||
|
|
||
| elif field_type == "handle": | ||
| # HandleInput for generic data connections | ||
| build_config[dynamic_input_name] = HandleInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=f"{help_text} (Accepts: {', '.join(input_types_list) if input_types_list else 'Any'})", | ||
| input_types=input_types_list if input_types_list else ["Data", "Text", "Message"], | ||
| required=required, | ||
| ) | ||
|
|
||
| elif field_type == "data": | ||
| # Specialized for Data type connections | ||
| build_config[dynamic_input_name] = HandleInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=f"{help_text} (Data input)", | ||
| input_types=["Data"] if not input_types_list else input_types_list, | ||
| required=required, | ||
| ) | ||
|
|
||
| else: | ||
| # Default to text input for unknown types | ||
| build_config[dynamic_input_name] = StrInput( | ||
| name=dynamic_input_name, | ||
| display_name=display_name, | ||
| info=f"{help_text} (Unknown type '{field_type}', defaulting to text)", | ||
| value=default_value, | ||
| required=required, | ||
| ) | ||
|
|
There was a problem hiding this comment.
Critical: build_config must receive serializable dicts, not Input objects
All dynamic assignments store Input instances directly in build_config. Other components call .to_dict() when mutating build_config. Keeping objects here will break serialization/UI.
Apply .to_dict() to all dynamic input assignments (pattern shown below; repeat for each branch):
- build_config[dynamic_input_name] = StrInput(
+ build_config[dynamic_input_name] = StrInput(
name=dynamic_input_name,
display_name=display_name,
info=f"{help_text} (Can connect to: {', '.join(input_types_list)})",
value=default_value,
required=required,
input_types=input_types_list,
- )
+ ).to_dict()
@@
- build_config[dynamic_input_name] = MultilineInput(
+ build_config[dynamic_input_name] = MultilineInput(
name=dynamic_input_name,
display_name=display_name,
info=help_text,
value=default_value,
required=required,
- )
+ ).to_dict()
@@
- build_config[dynamic_input_name] = IntInput(
+ build_config[dynamic_input_name] = IntInput(
name=dynamic_input_name,
display_name=display_name,
info=f"{help_text} (Can connect to: {', '.join(input_types_list)})",
value=default_int,
required=required,
input_types=input_types_list,
- )
+ ).to_dict()
@@
- build_config[dynamic_input_name] = FloatInput(
+ build_config[dynamic_input_name] = FloatInput(
name=dynamic_input_name,
display_name=display_name,
info=help_text,
value=default_float,
required=required,
- )
+ ).to_dict()
@@
- build_config[dynamic_input_name] = BoolInput(
+ build_config[dynamic_input_name] = BoolInput(
name=dynamic_input_name,
display_name=display_name,
info=help_text,
value=default_bool,
required=required,
- )
+ ).to_dict()
@@
- build_config[dynamic_input_name] = HandleInput(
+ build_config[dynamic_input_name] = HandleInput(
name=dynamic_input_name,
display_name=display_name,
info=f"{help_text} (Accepts: {', '.join(input_types_list) if input_types_list else 'Any'})",
input_types=input_types_list if input_types_list else ["Data", "Text", "Message"],
required=required,
- )
+ ).to_dict()
@@
- build_config[dynamic_input_name] = HandleInput(
+ build_config[dynamic_input_name] = HandleInput(
name=dynamic_input_name,
display_name=display_name,
info=f"{help_text} (Data input)",
input_types=["Data"] if not input_types_list else input_types_list,
required=required,
- )
+ ).to_dict()
@@
- build_config[dynamic_input_name] = StrInput(
+ build_config[dynamic_input_name] = StrInput(
name=dynamic_input_name,
display_name=display_name,
info=f"{help_text} (Unknown type '{field_type}', defaulting to text)",
value=default_value,
required=required,
- )
+ ).to_dict()Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/backend/base/langflow/components/processing/new_create_data.py around
lines 151 to 279, build_config is being assigned Input class instances
(StrInput, MultilineInput, IntInput, FloatInput, BoolInput, HandleInput) which
are not JSON-serializable and will break downstream code; update every branch so
the value stored in build_config is the result of calling .to_dict() on the
Input instance (i.e., create the Input(...) as currently written, then call
.to_dict() and assign that dict to build_config[dynamic_input_name]); apply this
change to all branches including text, multiline, number, float, boolean,
handle, data and the default case, and preserve any input_types overrides and
default value conversions before calling .to_dict().



Create Data Component - Dynamic Form Builder
Overview
Key Features
How It Works
Field Type Mappings
Use Cases
Outputs
Summary by CodeRabbit
New Features
Improvements