mirror of
https://github.com/langflow-ai/langflow.git
synced 2026-07-24 02:32:13 +08:00
fix: Expose Message connection handle on Run Flow's exposed text inputs (LE-1233) (#13180)
* fix: Expose input handle for run flow * Update test_run_flow.py
This commit is contained in:
@ -631,6 +631,79 @@ class TestRunFlowBaseComponentInputOutputHandling:
|
||||
assert updated[1]["input_types"] == ["str"]
|
||||
assert updated[2]["input_types"] == [] # Should be added as empty list
|
||||
|
||||
def test_resolve_exposed_input_types_restores_message_for_empty_text_field(self):
|
||||
"""Empty input_types on a text field should be restored to ["Message"]."""
|
||||
entry = {"type": "str", "input_types": []}
|
||||
|
||||
assert RunFlowBaseComponent._resolve_exposed_input_types(entry) == ["Message"]
|
||||
|
||||
def test_resolve_exposed_input_types_restores_message_when_missing(self):
|
||||
"""Missing input_types on a text field should default to ["Message"]."""
|
||||
entry = {"type": "Text"}
|
||||
|
||||
assert RunFlowBaseComponent._resolve_exposed_input_types(entry) == ["Message"]
|
||||
|
||||
def test_resolve_exposed_input_types_preserves_existing_input_types(self):
|
||||
"""Existing input_types must be preserved without modification."""
|
||||
entry = {"type": "str", "input_types": ["Message", "Data"]}
|
||||
|
||||
result = RunFlowBaseComponent._resolve_exposed_input_types(entry)
|
||||
|
||||
assert result == ["Message", "Data"]
|
||||
# Returns a copy, not the original list, to avoid accidental shared mutation.
|
||||
assert result is not entry["input_types"]
|
||||
|
||||
def test_resolve_exposed_input_types_skips_non_text_fields(self):
|
||||
"""Non-text fields with empty input_types are left untouched."""
|
||||
entry = {"type": "bool", "input_types": []}
|
||||
|
||||
assert RunFlowBaseComponent._resolve_exposed_input_types(entry) == []
|
||||
|
||||
def test_get_new_fields_exposes_message_handle_for_chat_input_value(self):
|
||||
"""Run Flow must expose a Message handle for ChatInput-style input_value fields.
|
||||
|
||||
Regression test for LE-1233: ChatInput sets input_types=[] on its input_value,
|
||||
which previously propagated to the Run Flow dynamic field and prevented users
|
||||
from wiring upstream components into the exposed Input Text field.
|
||||
"""
|
||||
component = RunFlowBaseComponent()
|
||||
|
||||
chat_input_vertex = MagicMock(spec=Vertex)
|
||||
chat_input_vertex.id = "ChatInput-abcde"
|
||||
chat_input_vertex.display_name = "Chat Input"
|
||||
chat_input_vertex.data = {
|
||||
"node": {
|
||||
"template": {
|
||||
"input_value": {
|
||||
"name": "input_value",
|
||||
"display_name": "Input Text",
|
||||
"type": "str",
|
||||
"input_types": [],
|
||||
"advanced": False,
|
||||
},
|
||||
"should_store_message": {
|
||||
"name": "should_store_message",
|
||||
"display_name": "Store Messages",
|
||||
"type": "bool",
|
||||
"input_types": [],
|
||||
"advanced": True,
|
||||
},
|
||||
},
|
||||
"field_order": ["input_value", "should_store_message"],
|
||||
}
|
||||
}
|
||||
|
||||
new_fields = component.get_new_fields([chat_input_vertex])
|
||||
|
||||
input_value_field = next(f for f in new_fields if f["name"].endswith("~input_value"))
|
||||
bool_field = next(f for f in new_fields if f["name"].endswith("~should_store_message"))
|
||||
|
||||
assert input_value_field["input_types"] == ["Message"], (
|
||||
"Text-typed exposed inputs must receive a Message handle so upstream components can be wired in (LE-1233)."
|
||||
)
|
||||
# Bool fields don't expose a Message handle — handle visibility is driven by `type`.
|
||||
assert bool_field["input_types"] == []
|
||||
|
||||
|
||||
class TestRunFlowBaseComponentOutputMethods:
|
||||
"""Test output methods."""
|
||||
|
||||
@ -203,6 +203,7 @@ class RunFlowBaseComponent(Component):
|
||||
),
|
||||
# TODO: make this more robust?
|
||||
"tool_mode": not (field_template[input_name].get("advanced", False)),
|
||||
"input_types": self._resolve_exposed_input_types(field_template[input_name]),
|
||||
}
|
||||
)
|
||||
for input_name in field_order
|
||||
@ -211,6 +212,23 @@ class RunFlowBaseComponent(Component):
|
||||
new_fields.extend(new_vertex_inputs)
|
||||
return new_fields
|
||||
|
||||
@staticmethod
|
||||
def _resolve_exposed_input_types(field_template_entry: dict) -> list[str]:
|
||||
"""Resolve input_types for a flow input re-exposed via Run Flow.
|
||||
|
||||
Some components (e.g. ChatInput) explicitly set ``input_types=[]`` on
|
||||
text-typed inputs to suppress connection handles within their own
|
||||
flow. When Run Flow re-exposes those fields the user expects to wire
|
||||
upstream components into them, so we restore a Message handle for
|
||||
text-typed fields with no input_types.
|
||||
"""
|
||||
existing = field_template_entry.get("input_types") or []
|
||||
if existing:
|
||||
return list(existing)
|
||||
if field_template_entry.get("type") in {"str", "Text"}:
|
||||
return ["Message"]
|
||||
return list(existing)
|
||||
|
||||
def add_new_fields(self, build_config: dotdict, new_fields: list[dotdict]) -> dotdict:
|
||||
"""Add new fields to the build_config."""
|
||||
for field in new_fields:
|
||||
|
||||
Reference in New Issue
Block a user