Skip to content

Commit 91ba881

Browse files
edwinjosechittilappillyHzaRashidautofix-ci[bot]
authored
fix: add support in Agent to fix ollama (#10499)
* improve ollama format field behaviour in agent component and update ollama tests * chore: update component index * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * [autofix.ci] apply automated fixes (attempt 3/3) --------- Co-authored-by: Hamza Rashid <hzarashid@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent b74e9ee commit 91ba881

5 files changed

Lines changed: 151 additions & 8 deletions

File tree

src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1921,7 +1921,7 @@
19211921
},
19221922
{
19231923
"name": "google",
1924-
"version": "0.8.5"
1924+
"version": "0.6.15"
19251925
},
19261926
{
19271927
"name": "googleapiclient",

src/backend/tests/integration/components/languagemodels/test_chatollama_integration.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ async def test_end_to_end_structured_output_to_data(self, mock_chat_ollama):
2828

2929
# Create component with schema format
3030
component = ChatOllamaComponent(
31-
base_url="http://localhost:11434", model_name="llama3.1", format=json_schema, temperature=0.1
31+
base_url="http://localhost:11434",
32+
model_name="llama3.1",
33+
format=json_schema,
34+
temperature=0.1,
35+
enable_structured_output=True,
3236
)
3337

3438
# Set up input message
@@ -67,7 +71,11 @@ async def test_end_to_end_structured_output_to_dataframe(self, mock_chat_ollama)
6771

6872
# Create component with JSON format
6973
component = ChatOllamaComponent(
70-
base_url="http://localhost:11434", model_name="llama3.1", format="json", temperature=0.1
74+
base_url="http://localhost:11434",
75+
model_name="llama3.1",
76+
format="json",
77+
temperature=0.1,
78+
enable_structured_output=True,
7179
)
7280

7381
# Set up input message
@@ -117,7 +125,11 @@ class PersonInfo(BaseModel):
117125

118126
# Create component with Pydantic schema
119127
component = ChatOllamaComponent(
120-
base_url="http://localhost:11434", model_name="llama3.1", format=pydantic_schema, temperature=0.1
128+
base_url="http://localhost:11434",
129+
model_name="llama3.1",
130+
format=pydantic_schema,
131+
temperature=0.1,
132+
enable_structured_output=True,
121133
)
122134

123135
component.input_value = "Extract person info"

src/backend/tests/unit/components/languagemodels/test_chatollama_component.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def default_kwargs(self):
4141
"system": "",
4242
"tool_model_enabled": True,
4343
"template": "",
44+
"enable_structured_output": True,
4445
}
4546

4647
@pytest.fixture
@@ -1037,3 +1038,121 @@ def test_build_model_cloud_with_v1_suffix_stripped(self, mock_chat_ollama):
10371038
assert call_args["base_url"] == DEFAULT_OLLAMA_API_URL
10381039
assert "/v1" not in call_args["base_url"]
10391040
assert model == mock_model
1041+
1042+
@patch("lfx.components.ollama.ollama.ChatOllama")
1043+
def test_build_model_with_structured_output_disabled(self, mock_chat_ollama, component_class, default_kwargs):
1044+
"""Test that format field is NOT passed when enable_structured_output is False (default)."""
1045+
mock_instance = MagicMock()
1046+
mock_chat_ollama.return_value = mock_instance
1047+
1048+
# Remove enable_structured_output to use default (False)
1049+
kwargs = default_kwargs.copy()
1050+
kwargs.pop("enable_structured_output", None)
1051+
kwargs["format"] = "json" # Set format but it should be ignored
1052+
1053+
component = component_class(**kwargs)
1054+
model = component.build_model()
1055+
1056+
# Verify ChatOllama was called WITHOUT format parameter
1057+
call_args = mock_chat_ollama.call_args[1]
1058+
assert "format" not in call_args, "format should not be passed when enable_structured_output is False"
1059+
assert model == mock_instance
1060+
1061+
@patch("lfx.components.ollama.ollama.ChatOllama")
1062+
def test_build_model_with_structured_output_enabled_string_format(
1063+
self, mock_chat_ollama, component_class, default_kwargs
1064+
):
1065+
"""Test that format field IS passed when enable_structured_output is True with string format."""
1066+
mock_instance = MagicMock()
1067+
mock_chat_ollama.return_value = mock_instance
1068+
1069+
kwargs = default_kwargs.copy()
1070+
kwargs["enable_structured_output"] = True
1071+
kwargs["format"] = "json"
1072+
1073+
component = component_class(**kwargs)
1074+
model = component.build_model()
1075+
1076+
# Verify ChatOllama was called WITH format parameter
1077+
call_args = mock_chat_ollama.call_args[1]
1078+
assert "format" in call_args, "format should be passed when enable_structured_output is True"
1079+
assert call_args["format"] == "json"
1080+
assert model == mock_instance
1081+
1082+
@patch("lfx.components.ollama.ollama.ChatOllama")
1083+
def test_build_model_with_structured_output_enabled_dict_format(
1084+
self, mock_chat_ollama, component_class, default_kwargs
1085+
):
1086+
"""Test that JSON schema format IS passed when enable_structured_output is True."""
1087+
mock_instance = MagicMock()
1088+
mock_chat_ollama.return_value = mock_instance
1089+
1090+
json_schema = {
1091+
"type": "object",
1092+
"properties": {"name": {"type": "string"}},
1093+
"required": ["name"],
1094+
}
1095+
1096+
kwargs = default_kwargs.copy()
1097+
kwargs["enable_structured_output"] = True
1098+
kwargs["format"] = json_schema
1099+
1100+
component = component_class(**kwargs)
1101+
model = component.build_model()
1102+
1103+
# Verify ChatOllama was called WITH format parameter as dict
1104+
call_args = mock_chat_ollama.call_args[1]
1105+
assert "format" in call_args
1106+
assert call_args["format"] == json_schema
1107+
assert call_args["format"]["type"] == "object"
1108+
assert model == mock_instance
1109+
1110+
@patch("lfx.components.ollama.ollama.ChatOllama")
1111+
def test_build_model_with_structured_output_enabled_no_format(
1112+
self, mock_chat_ollama, component_class, default_kwargs
1113+
):
1114+
"""Test that format is not passed when enable_structured_output is True but format is None/empty."""
1115+
mock_instance = MagicMock()
1116+
mock_chat_ollama.return_value = mock_instance
1117+
1118+
kwargs = default_kwargs.copy()
1119+
kwargs["enable_structured_output"] = True
1120+
kwargs["format"] = None # No format specified
1121+
1122+
component = component_class(**kwargs)
1123+
model = component.build_model()
1124+
1125+
# Verify ChatOllama was called WITHOUT format parameter
1126+
call_args = mock_chat_ollama.call_args[1]
1127+
assert "format" not in call_args, "format should not be passed when it's None"
1128+
assert model == mock_instance
1129+
1130+
@patch("lfx.components.ollama.ollama.ChatOllama")
1131+
def test_build_model_structured_output_toggle_behavior(self, mock_chat_ollama, component_class, default_kwargs):
1132+
"""Test toggling enable_structured_output affects format parameter passing."""
1133+
mock_instance = MagicMock()
1134+
mock_chat_ollama.return_value = mock_instance
1135+
1136+
# First: Test with structured output disabled
1137+
kwargs = default_kwargs.copy()
1138+
kwargs["enable_structured_output"] = False
1139+
kwargs["format"] = "json"
1140+
1141+
component = component_class(**kwargs)
1142+
model = component.build_model()
1143+
1144+
call_args = mock_chat_ollama.call_args[1]
1145+
assert "format" not in call_args, "format should not be in call when disabled"
1146+
1147+
# Reset mock
1148+
mock_chat_ollama.reset_mock()
1149+
1150+
# Second: Test with structured output enabled
1151+
kwargs["enable_structured_output"] = True
1152+
component = component_class(**kwargs)
1153+
model = component.build_model()
1154+
1155+
call_args = mock_chat_ollama.call_args[1]
1156+
assert "format" in call_args, "format should be in call when enabled"
1157+
assert call_args["format"] == "json"
1158+
assert model == mock_instance

src/lfx/src/lfx/_assets/component_index.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

src/lfx/src/lfx/components/ollama/ollama.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class ChatOllamaComponent(LCModelComponent):
101101
info="Refer to https://ollama.com/library for more models.",
102102
refresh_button=True,
103103
real_time_refresh=True,
104+
required=True,
104105
),
105106
SecretStrInput(
106107
name="api_key",
@@ -122,9 +123,9 @@ class ChatOllamaComponent(LCModelComponent):
122123
name="format",
123124
display_name="Format",
124125
info="Specify the format of the output.",
125-
advanced=False,
126126
table_schema=TABLE_SCHEMA,
127127
value=default_table_row,
128+
show=False,
128129
),
129130
DictInput(name="metadata", display_name="Metadata", info="Metadata to add to the run trace.", advanced=True),
130131
DropdownInput(
@@ -215,6 +216,14 @@ class ChatOllamaComponent(LCModelComponent):
215216
MessageTextInput(
216217
name="template", display_name="Template", info="Template to use for generating text.", advanced=True
217218
),
219+
BoolInput(
220+
name="enable_structured_output",
221+
display_name="Enable Structured Output",
222+
info="Whether to enable structured output in the model.",
223+
value=False,
224+
advanced=False,
225+
real_time_refresh=True,
226+
),
218227
*LCModelComponent.get_base_inputs(),
219228
]
220229

@@ -254,7 +263,7 @@ def build_model(self) -> LanguageModel: # type: ignore[type-var]
254263
)
255264

256265
try:
257-
output_format = self._parse_format_field(self.format)
266+
output_format = self._parse_format_field(self.format) if self.enable_structured_output else None
258267
except Exception as e:
259268
msg = f"Failed to parse the format field: {e}"
260269
raise ValueError(msg) from e
@@ -264,7 +273,7 @@ def build_model(self) -> LanguageModel: # type: ignore[type-var]
264273
"base_url": transformed_base_url,
265274
"model": self.model_name,
266275
"mirostat": mirostat_value,
267-
"format": output_format,
276+
"format": output_format or None,
268277
"metadata": self.metadata,
269278
"tags": self.tags.split(",") if self.tags else None,
270279
"mirostat_eta": mirostat_eta,
@@ -319,6 +328,9 @@ async def is_valid_ollama_url(self, url: str) -> bool:
319328
return False
320329

321330
async def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None):
331+
if field_name == "enable_structured_output": # bind enable_structured_output boolean to format show value
332+
build_config["format"]["show"] = field_value
333+
322334
if field_name == "mirostat":
323335
if field_value == "Disabled":
324336
build_config["mirostat_eta"]["advanced"] = True

0 commit comments

Comments
 (0)