Skip to content

Commit f685d29

Browse files
fix: materialize Blog Writer URL prompt input (#14186)
* fix: materialize Blog Writer URL prompt input * [autofix.ci] apply automated fixes * fix: preserve prompt template custom fields --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 0b5500f commit f685d29

4 files changed

Lines changed: 99 additions & 4 deletions

File tree

src/backend/base/langflow/initial_setup/setup.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
# importable via the bundle shims) -- it is what the migration table and the
6767
# template tests resolve.
6868
_RUNTIME_EXT_MODULE_PREFIX = "_lfx_ext."
69+
_PROMPT_COMPONENT_TYPES = frozenset({"Prompt", "Prompt Template"})
6970

7071

7172
def _merge_node_metadata(current_metadata, latest_metadata):
@@ -96,6 +97,7 @@ def update_projects_components_with_latest_component_versions(project_data, all_
9697
for node in project_data_copy.get("nodes", []):
9798
node_data = node.get("data").get("node")
9899
node_type = node.get("data").get("type")
100+
is_prompt_component = node_type in _PROMPT_COMPONENT_TYPES
99101

100102
if node_type in all_types_dict_flat:
101103
latest_node = all_types_dict_flat.get(node_type)
@@ -142,7 +144,7 @@ def update_projects_components_with_latest_component_versions(project_data, all_
142144

143145
if node_data["template"]["_type"] != latest_template["_type"]:
144146
node_data["template"]["_type"] = latest_template["_type"]
145-
if node_type != "Prompt":
147+
if not is_prompt_component:
146148
node_data["template"] = deepcopy(latest_template)
147149
else:
148150
for key, value in latest_template.items():
@@ -227,7 +229,7 @@ def update_projects_components_with_latest_component_versions(project_data, all_
227229
)
228230
node_data["template"][field_name][attr] = deepcopy(field_dict[attr])
229231
# Remove fields that are not in the latest template
230-
if node_type != "Prompt":
232+
if not is_prompt_component:
231233
for field_name in list(node_data["template"].keys()):
232234
is_tool_mode_and_field_is_tools_metadata = (
233235
node_data.get("tool_mode", False) and field_name == "tools_metadata"

src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@
105105
"fieldName": "URL",
106106
"id": "Prompt Template-3URhM",
107107
"inputTypes": [
108-
"Message"
108+
"Message",
109+
"Text"
109110
],
110111
"type": "str"
111112
}
@@ -115,7 +116,7 @@
115116
"source": "ChatInput-w522Z",
116117
"sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-w522Zœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}",
117118
"target": "Prompt Template-3URhM",
118-
"targetHandle": "{œfieldNameœ: œURLœ, œidœ: œPrompt Template-3URhMœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
119+
"targetHandle": "{œfieldNameœ: œURLœ, œidœ: œPrompt Template-3URhMœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
119120
}
120121
],
121122
"nodes": [
@@ -1812,6 +1813,29 @@
18121813
"priority": null,
18131814
"replacement": null,
18141815
"template": {
1816+
"URL": {
1817+
"advanced": false,
1818+
"display_name": "URL",
1819+
"dynamic": false,
1820+
"field_type": "str",
1821+
"fileTypes": [],
1822+
"file_path": "",
1823+
"info": "",
1824+
"input_types": [
1825+
"Message",
1826+
"Text"
1827+
],
1828+
"list": false,
1829+
"load_from_db": false,
1830+
"multiline": true,
1831+
"name": "URL",
1832+
"placeholder": "",
1833+
"required": false,
1834+
"show": true,
1835+
"title_case": false,
1836+
"type": "str",
1837+
"value": ""
1838+
},
18151839
"_type": "Component",
18161840
"code": {
18171841
"advanced": true,

src/backend/tests/test_starter_projects.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,29 @@ def test_json_validity(self, json_file: Path):
122122
data = load_json_file(json_file)
123123
assert isinstance(data, dict), f"{json_file.name} should be a valid JSON object"
124124

125+
def test_prompt_custom_fields_are_materialized(self, json_file: Path):
126+
"""Prompt variables declared in custom_fields must exist in the serialized template."""
127+
data = load_json_file(json_file)
128+
missing_fields: list[str] = []
129+
130+
for node in data.get("data", {}).get("nodes", []):
131+
node_data = node.get("data", {}) or {}
132+
node_config = node_data.get("node", {}) or {}
133+
template = node_config.get("template", {}) or {}
134+
custom_fields = node_config.get("custom_fields", {}) or {}
135+
136+
for field_names in custom_fields.values():
137+
if not isinstance(field_names, list):
138+
continue
139+
for field_name in field_names:
140+
if field_name not in template:
141+
node_id = node_data.get("id", node.get("id", "<unknown>"))
142+
missing_fields.append(f"{node_id}.{field_name}")
143+
144+
assert not missing_fields, (
145+
f"{json_file.name}: custom prompt fields are missing from the serialized node template: {missing_fields}"
146+
)
147+
125148
def test_width_height_at_node_level(self, json_file: Path):
126149
"""Test that width/height are removed from node root level for all node types EXCEPT noteNode.
127150

src/backend/tests/unit/test_initial_setup.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,52 @@ def test_update_projects_resolves_prompt_via_component_type_alias():
674674
)
675675

676676

677+
def test_update_projects_preserves_current_prompt_custom_fields():
678+
"""Current Prompt Template nodes must retain serialized dynamic inputs."""
679+
url_field = {
680+
"name": "URL",
681+
"type": "str",
682+
"value": "",
683+
"input_types": ["Message", "Text"],
684+
}
685+
all_types_dict = {
686+
"models_and_agents": {
687+
"Prompt Template": {
688+
"template": {
689+
"_type": "Component",
690+
"code": {"value": "new_prompt_code"},
691+
"template": {"type": "prompt", "value": ""},
692+
},
693+
"display_name": "Prompt Template",
694+
}
695+
}
696+
}
697+
project_data = {
698+
"nodes": [
699+
{
700+
"data": {
701+
"type": "Prompt Template",
702+
"node": {
703+
"custom_fields": {"template": ["URL"]},
704+
"template": {
705+
"_type": "Component",
706+
"code": {"value": "old_prompt_code"},
707+
"template": {"type": "prompt", "value": "Source: {URL}"},
708+
"URL": url_field,
709+
},
710+
"outputs": [],
711+
},
712+
}
713+
}
714+
]
715+
}
716+
717+
updated_project = update_projects_components_with_latest_component_versions(project_data, all_types_dict)
718+
719+
updated_template = updated_project["nodes"][0]["data"]["node"]["template"]
720+
assert updated_template["URL"] == url_field
721+
722+
677723
def test_update_projects_direct_key_takes_precedence_over_alias():
678724
"""Test that a direct key match is preferred over the derived alias."""
679725
all_types_dict = {

0 commit comments

Comments
 (0)