lfx-nightly 0.2.0.dev26__py3-none-any.whl → 0.2.1.dev7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. lfx/_assets/component_index.json +1 -1
  2. lfx/base/agents/agent.py +9 -4
  3. lfx/base/agents/altk_base_agent.py +16 -3
  4. lfx/base/agents/altk_tool_wrappers.py +1 -1
  5. lfx/base/agents/utils.py +4 -0
  6. lfx/base/composio/composio_base.py +78 -41
  7. lfx/base/data/base_file.py +14 -4
  8. lfx/base/data/cloud_storage_utils.py +156 -0
  9. lfx/base/data/docling_utils.py +191 -65
  10. lfx/base/data/storage_utils.py +109 -0
  11. lfx/base/datastax/astradb_base.py +75 -64
  12. lfx/base/mcp/util.py +2 -2
  13. lfx/base/models/__init__.py +11 -1
  14. lfx/base/models/anthropic_constants.py +21 -12
  15. lfx/base/models/google_generative_ai_constants.py +33 -9
  16. lfx/base/models/model_metadata.py +6 -0
  17. lfx/base/models/ollama_constants.py +196 -30
  18. lfx/base/models/openai_constants.py +37 -10
  19. lfx/base/models/unified_models.py +1123 -0
  20. lfx/base/models/watsonx_constants.py +36 -0
  21. lfx/base/tools/component_tool.py +2 -9
  22. lfx/cli/commands.py +6 -1
  23. lfx/cli/run.py +65 -409
  24. lfx/cli/script_loader.py +13 -3
  25. lfx/components/__init__.py +0 -3
  26. lfx/components/composio/github_composio.py +1 -1
  27. lfx/components/cuga/cuga_agent.py +39 -27
  28. lfx/components/data_source/api_request.py +4 -2
  29. lfx/components/docling/__init__.py +45 -11
  30. lfx/components/docling/chunk_docling_document.py +3 -1
  31. lfx/components/docling/docling_inline.py +39 -49
  32. lfx/components/docling/export_docling_document.py +3 -1
  33. lfx/components/elastic/opensearch_multimodal.py +215 -57
  34. lfx/components/files_and_knowledge/file.py +439 -39
  35. lfx/components/files_and_knowledge/ingestion.py +8 -0
  36. lfx/components/files_and_knowledge/retrieval.py +10 -0
  37. lfx/components/files_and_knowledge/save_file.py +123 -53
  38. lfx/components/ibm/watsonx.py +7 -1
  39. lfx/components/input_output/chat_output.py +7 -1
  40. lfx/components/langchain_utilities/tool_calling.py +14 -6
  41. lfx/components/llm_operations/batch_run.py +80 -25
  42. lfx/components/llm_operations/lambda_filter.py +33 -6
  43. lfx/components/llm_operations/llm_conditional_router.py +39 -7
  44. lfx/components/llm_operations/structured_output.py +38 -12
  45. lfx/components/models/__init__.py +16 -74
  46. lfx/components/models_and_agents/agent.py +51 -201
  47. lfx/components/models_and_agents/embedding_model.py +185 -339
  48. lfx/components/models_and_agents/language_model.py +54 -318
  49. lfx/components/models_and_agents/mcp_component.py +58 -9
  50. lfx/components/ollama/ollama.py +9 -4
  51. lfx/components/ollama/ollama_embeddings.py +2 -1
  52. lfx/components/openai/openai_chat_model.py +1 -1
  53. lfx/components/processing/__init__.py +0 -3
  54. lfx/components/vllm/__init__.py +37 -0
  55. lfx/components/vllm/vllm.py +141 -0
  56. lfx/components/vllm/vllm_embeddings.py +110 -0
  57. lfx/custom/custom_component/custom_component.py +8 -6
  58. lfx/custom/directory_reader/directory_reader.py +5 -2
  59. lfx/graph/utils.py +64 -18
  60. lfx/inputs/__init__.py +2 -0
  61. lfx/inputs/input_mixin.py +54 -0
  62. lfx/inputs/inputs.py +115 -0
  63. lfx/interface/initialize/loading.py +42 -12
  64. lfx/io/__init__.py +2 -0
  65. lfx/run/__init__.py +5 -0
  66. lfx/run/base.py +494 -0
  67. lfx/schema/data.py +1 -1
  68. lfx/schema/image.py +28 -19
  69. lfx/schema/message.py +19 -3
  70. lfx/services/interfaces.py +5 -0
  71. lfx/services/manager.py +5 -4
  72. lfx/services/mcp_composer/service.py +45 -13
  73. lfx/services/settings/auth.py +18 -11
  74. lfx/services/settings/base.py +12 -24
  75. lfx/services/settings/constants.py +2 -0
  76. lfx/services/storage/local.py +37 -0
  77. lfx/services/storage/service.py +19 -0
  78. lfx/utils/constants.py +1 -0
  79. lfx/utils/image.py +29 -11
  80. lfx/utils/validate_cloud.py +14 -3
  81. {lfx_nightly-0.2.0.dev26.dist-info → lfx_nightly-0.2.1.dev7.dist-info}/METADATA +5 -2
  82. {lfx_nightly-0.2.0.dev26.dist-info → lfx_nightly-0.2.1.dev7.dist-info}/RECORD +84 -78
  83. lfx/components/processing/dataframe_to_toolset.py +0 -259
  84. {lfx_nightly-0.2.0.dev26.dist-info → lfx_nightly-0.2.1.dev7.dist-info}/WHEEL +0 -0
  85. {lfx_nightly-0.2.0.dev26.dist-info → lfx_nightly-0.2.1.dev7.dist-info}/entry_points.txt +0 -0
@@ -1,7 +1,21 @@
1
1
  from typing import Any
2
2
 
3
+ from lfx.base.models.unified_models import (
4
+ get_language_model_options,
5
+ get_llm,
6
+ update_model_options_in_build_config,
7
+ )
3
8
  from lfx.custom import Component
4
- from lfx.io import BoolInput, HandleInput, MessageInput, MessageTextInput, MultilineInput, Output, TableInput
9
+ from lfx.io import (
10
+ BoolInput,
11
+ MessageInput,
12
+ MessageTextInput,
13
+ ModelInput,
14
+ MultilineInput,
15
+ Output,
16
+ SecretStrInput,
17
+ TableInput,
18
+ )
5
19
  from lfx.schema.message import Message
6
20
  from lfx.schema.table import EditMode
7
21
 
@@ -17,13 +31,20 @@ class SmartRouterComponent(Component):
17
31
  self._matched_category = None
18
32
 
19
33
  inputs = [
20
- HandleInput(
21
- name="llm",
34
+ ModelInput(
35
+ name="model",
22
36
  display_name="Language Model",
23
- info="LLM to use for categorization.",
24
- input_types=["LanguageModel"],
37
+ info="Select your model provider",
38
+ real_time_refresh=True,
25
39
  required=True,
26
40
  ),
41
+ SecretStrInput(
42
+ name="api_key",
43
+ display_name="API Key",
44
+ info="Model Provider API key",
45
+ real_time_refresh=True,
46
+ advanced=True,
47
+ ),
27
48
  MessageTextInput(
28
49
  name="input_text",
29
50
  display_name="Input",
@@ -111,6 +132,17 @@ class SmartRouterComponent(Component):
111
132
 
112
133
  outputs: list[Output] = []
113
134
 
135
+ def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):
136
+ """Dynamically update build config with user-filtered model options."""
137
+ return update_model_options_in_build_config(
138
+ component=self,
139
+ build_config=build_config,
140
+ cache_key_prefix="language_model_options",
141
+ get_options_func=get_language_model_options,
142
+ field_name=field_name,
143
+ field_value=field_value,
144
+ )
145
+
114
146
  def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:
115
147
  """Create a dynamic output for each category in the categories table."""
116
148
  if field_name in {"routes", "enable_else_output"}:
@@ -153,7 +185,7 @@ class SmartRouterComponent(Component):
153
185
 
154
186
  # Find the matching category using LLM-based categorization
155
187
  matched_category = None
156
- llm = getattr(self, "llm", None)
188
+ llm = get_llm(model=self.model, user_id=self.user_id, api_key=self.api_key)
157
189
 
158
190
  if llm and categories:
159
191
  # Create prompt for categorization
@@ -315,7 +347,7 @@ class SmartRouterComponent(Component):
315
347
 
316
348
  # Check if any category matches using LLM categorization
317
349
  has_match = False
318
- llm = getattr(self, "llm", None)
350
+ llm = get_llm(model=self.model, user_id=self.user_id, api_key=self.api_key)
319
351
 
320
352
  if llm and categories:
321
353
  try:
@@ -2,13 +2,19 @@ from pydantic import BaseModel, Field, create_model
2
2
  from trustcall import create_extractor
3
3
 
4
4
  from lfx.base.models.chat_result import get_chat_result
5
+ from lfx.base.models.unified_models import (
6
+ get_language_model_options,
7
+ get_llm,
8
+ update_model_options_in_build_config,
9
+ )
5
10
  from lfx.custom.custom_component.component import Component
6
11
  from lfx.helpers.base_model import build_model_from_schema
7
12
  from lfx.io import (
8
- HandleInput,
9
13
  MessageTextInput,
14
+ ModelInput,
10
15
  MultilineInput,
11
16
  Output,
17
+ SecretStrInput,
12
18
  TableInput,
13
19
  )
14
20
  from lfx.log.logger import logger
@@ -25,13 +31,20 @@ class StructuredOutputComponent(Component):
25
31
  icon = "braces"
26
32
 
27
33
  inputs = [
28
- HandleInput(
29
- name="llm",
34
+ ModelInput(
35
+ name="model",
30
36
  display_name="Language Model",
31
- info="The language model to use to generate the structured output.",
32
- input_types=["LanguageModel"],
37
+ info="Select your model provider",
38
+ real_time_refresh=True,
33
39
  required=True,
34
40
  ),
41
+ SecretStrInput(
42
+ name="api_key",
43
+ display_name="API Key",
44
+ info="Model Provider API key",
45
+ real_time_refresh=True,
46
+ advanced=True,
47
+ ),
35
48
  MultilineInput(
36
49
  name="input_value",
37
50
  display_name="Input Message",
@@ -126,10 +139,23 @@ class StructuredOutputComponent(Component):
126
139
  ),
127
140
  ]
128
141
 
142
+ def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):
143
+ """Dynamically update build config with user-filtered model options."""
144
+ return update_model_options_in_build_config(
145
+ component=self,
146
+ build_config=build_config,
147
+ cache_key_prefix="language_model_options",
148
+ get_options_func=get_language_model_options,
149
+ field_name=field_name,
150
+ field_value=field_value,
151
+ )
152
+
129
153
  def build_structured_output_base(self):
130
154
  schema_name = self.schema_name or "OutputModel"
131
155
 
132
- if not hasattr(self.llm, "with_structured_output"):
156
+ llm = get_llm(model=self.model, user_id=self.user_id, api_key=self.api_key)
157
+
158
+ if not hasattr(llm, "with_structured_output"):
133
159
  msg = "Language model does not support structured output."
134
160
  raise TypeError(msg)
135
161
  if not self.output_schema:
@@ -155,9 +181,9 @@ class StructuredOutputComponent(Component):
155
181
  "callbacks": self.get_langchain_callbacks(),
156
182
  }
157
183
  # Generate structured output using Trustcall first, then fallback to Langchain if it fails
158
- result = self._extract_output_with_trustcall(output_model, config_dict)
184
+ result = self._extract_output_with_trustcall(llm, output_model, config_dict)
159
185
  if result is None:
160
- result = self._extract_output_with_langchain(output_model, config_dict)
186
+ result = self._extract_output_with_langchain(llm, output_model, config_dict)
161
187
 
162
188
  # OPTIMIZATION NOTE: Simplified processing based on trustcall response structure
163
189
  # Handle non-dict responses (shouldn't happen with trustcall, but defensive)
@@ -204,9 +230,9 @@ class StructuredOutputComponent(Component):
204
230
  return DataFrame(output)
205
231
  return DataFrame()
206
232
 
207
- def _extract_output_with_trustcall(self, schema: BaseModel, config_dict: dict) -> list[BaseModel] | None:
233
+ def _extract_output_with_trustcall(self, llm, schema: BaseModel, config_dict: dict) -> list[BaseModel] | None:
208
234
  try:
209
- llm_with_structured_output = create_extractor(self.llm, tools=[schema], tool_choice=schema.__name__)
235
+ llm_with_structured_output = create_extractor(llm, tools=[schema], tool_choice=schema.__name__)
210
236
  result = get_chat_result(
211
237
  runnable=llm_with_structured_output,
212
238
  system_message=self.system_prompt,
@@ -222,9 +248,9 @@ class StructuredOutputComponent(Component):
222
248
  return None
223
249
  return result or None # langchain fallback is used if error occurs or the result is empty
224
250
 
225
- def _extract_output_with_langchain(self, schema: BaseModel, config_dict: dict) -> list[BaseModel] | None:
251
+ def _extract_output_with_langchain(self, llm, schema: BaseModel, config_dict: dict) -> list[BaseModel] | None:
226
252
  try:
227
- llm_with_structured_output = self.llm.with_structured_output(schema)
253
+ llm_with_structured_output = llm.with_structured_output(schema)
228
254
  result = get_chat_result(
229
255
  runnable=llm_with_structured_output,
230
256
  system_message=self.system_prompt,
@@ -1,89 +1,31 @@
1
- """Models module - backwards compatibility alias for models_and_agents.
1
+ """Compatibility layer for lfx.components.models.
2
2
 
3
- This module provides backwards compatibility by forwarding model-related imports
4
- to models_and_agents where the actual model components are located.
3
+ This module redirects imports to lfx.components.models_and_agents for backward compatibility.
5
4
  """
6
5
 
7
6
  from __future__ import annotations
8
7
 
9
- import sys
10
8
  from typing import Any
11
9
 
12
- from lfx.components._importing import import_mod
13
-
14
- # Forward model components from models_and_agents
15
- _dynamic_imports = {
16
- "LanguageModelComponent": "language_model",
17
- "EmbeddingModelComponent": "embedding_model",
18
- }
19
-
20
- __all__ = [
21
- "EmbeddingModelComponent",
22
- "LanguageModelComponent",
23
- ]
24
-
25
- # Register redirected submodules in sys.modules for direct importlib.import_module() calls
26
- # This allows imports like: import lfx.components.models.embedding_model
27
- _redirected_submodules = {
28
- "lfx.components.models.embedding_model": "lfx.components.models_and_agents.embedding_model",
29
- "lfx.components.models.language_model": "lfx.components.models_and_agents.language_model",
30
- }
31
-
32
- for old_path, new_path in _redirected_submodules.items():
33
- if old_path not in sys.modules:
34
- # Use a lazy loader that imports the actual module when accessed
35
- class _RedirectedModule:
36
- def __init__(self, target_path: str, original_path: str):
37
- self._target_path = target_path
38
- self._original_path = original_path
39
- self._module = None
40
-
41
- def __getattr__(self, name: str) -> Any:
42
- if self._module is None:
43
- from importlib import import_module
44
-
45
- self._module = import_module(self._target_path)
46
- # Also register under the original path for future imports
47
- sys.modules[self._original_path] = self._module
48
- return getattr(self._module, name)
49
-
50
- def __repr__(self) -> str:
51
- return f"<redirected module '{self._original_path}' -> '{self._target_path}'>"
52
-
53
- sys.modules[old_path] = _RedirectedModule(new_path, old_path)
10
+ # Import everything from models_and_agents to maintain backward compatibility
11
+ from lfx.components.models_and_agents import * # noqa: F403
12
+ from lfx.components.models_and_agents import __all__ # noqa: F401
54
13
 
55
14
 
15
+ # Set up module-level __getattr__ to handle dynamic imports
56
16
  def __getattr__(attr_name: str) -> Any:
57
- """Forward attribute access to models_and_agents components."""
58
- # Handle submodule access for backwards compatibility
59
- if attr_name == "embedding_model":
60
- from importlib import import_module
61
-
62
- result = import_module("lfx.components.models_and_agents.embedding_model")
63
- globals()[attr_name] = result
64
- return result
65
- if attr_name == "language_model":
66
- from importlib import import_module
17
+ """Redirect all attribute access to models_and_agents module."""
18
+ from lfx.components import models_and_agents
67
19
 
68
- result = import_module("lfx.components.models_and_agents.language_model")
69
- globals()[attr_name] = result
70
- return result
20
+ if hasattr(models_and_agents, attr_name):
21
+ return getattr(models_and_agents, attr_name)
71
22
 
72
- if attr_name not in _dynamic_imports:
73
- msg = f"module '{__name__}' has no attribute '{attr_name}'"
74
- raise AttributeError(msg)
75
-
76
- # Import from models_and_agents using the correct package path
77
- package = "lfx.components.models_and_agents"
78
- try:
79
- result = import_mod(attr_name, _dynamic_imports[attr_name], package)
80
- except (ModuleNotFoundError, ImportError, AttributeError) as e:
81
- msg = f"Could not import '{attr_name}' from '{__name__}': {e}"
82
- raise AttributeError(msg) from e
83
- globals()[attr_name] = result
84
- return result
23
+ msg = f"module '{__name__}' has no attribute '{attr_name}'"
24
+ raise AttributeError(msg)
85
25
 
86
26
 
87
27
  def __dir__() -> list[str]:
88
- """Return directory of available components."""
89
- return list(__all__)
28
+ """Return the directory of available attributes."""
29
+ from lfx.components import models_and_agents
30
+
31
+ return dir(models_and_agents)
@@ -1,27 +1,29 @@
1
+ from __future__ import annotations
2
+
1
3
  import json
2
4
  import re
5
+ from typing import TYPE_CHECKING
3
6
 
4
- from langchain_core.tools import StructuredTool, Tool
5
7
  from pydantic import ValidationError
6
8
 
9
+ from lfx.components.models_and_agents.memory import MemoryComponent
10
+
11
+ if TYPE_CHECKING:
12
+ from langchain_core.tools import Tool
13
+
7
14
  from lfx.base.agents.agent import LCToolsAgentComponent
8
15
  from lfx.base.agents.events import ExceptionWithMessageError
9
- from lfx.base.models.model_input_constants import (
10
- ALL_PROVIDER_FIELDS,
11
- MODEL_DYNAMIC_UPDATE_FIELDS,
12
- MODEL_PROVIDERS_DICT,
13
- MODEL_PROVIDERS_LIST,
14
- MODELS_METADATA,
16
+ from lfx.base.models.unified_models import (
17
+ get_language_model_options,
18
+ get_llm,
19
+ update_model_options_in_build_config,
15
20
  )
16
- from lfx.base.models.model_utils import get_model_name
17
21
  from lfx.components.helpers import CurrentDateComponent
18
22
  from lfx.components.langchain_utilities.tool_calling import ToolCallingAgentComponent
19
- from lfx.components.models_and_agents.memory import MemoryComponent
20
23
  from lfx.custom.custom_component.component import get_component_toolkit
21
- from lfx.custom.utils import update_component_build_config
22
24
  from lfx.helpers.base_model import build_model_from_schema
23
- from lfx.inputs.inputs import BoolInput, SecretStrInput, StrInput
24
- from lfx.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output, TableInput
25
+ from lfx.inputs.inputs import BoolInput, ModelInput
26
+ from lfx.io import IntInput, MessageTextInput, MultilineInput, Output, SecretStrInput, TableInput
25
27
  from lfx.log.logger import logger
26
28
  from lfx.schema.data import Data
27
29
  from lfx.schema.dotdict import dotdict
@@ -44,66 +46,21 @@ class AgentComponent(ToolCallingAgentComponent):
44
46
 
45
47
  memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]
46
48
 
47
- # Filter out json_mode from OpenAI inputs since we handle structured output differently
48
- if "OpenAI" in MODEL_PROVIDERS_DICT:
49
- openai_inputs_filtered = [
50
- input_field
51
- for input_field in MODEL_PROVIDERS_DICT["OpenAI"]["inputs"]
52
- if not (hasattr(input_field, "name") and input_field.name == "json_mode")
53
- ]
54
- else:
55
- openai_inputs_filtered = []
56
-
57
49
  inputs = [
58
- DropdownInput(
59
- name="agent_llm",
60
- display_name="Model Provider",
61
- info="The provider of the language model that the agent will use to generate responses.",
62
- options=[*MODEL_PROVIDERS_LIST],
63
- value="OpenAI",
50
+ ModelInput(
51
+ name="model",
52
+ display_name="Language Model",
53
+ info="Select your model provider",
64
54
  real_time_refresh=True,
65
- refresh_button=False,
66
- input_types=[],
67
- options_metadata=[MODELS_METADATA[key] for key in MODEL_PROVIDERS_LIST if key in MODELS_METADATA],
68
- external_options={
69
- "fields": {
70
- "data": {
71
- "node": {
72
- "name": "connect_other_models",
73
- "display_name": "Connect other models",
74
- "icon": "CornerDownLeft",
75
- }
76
- }
77
- },
78
- },
55
+ required=True,
79
56
  ),
80
57
  SecretStrInput(
81
58
  name="api_key",
82
59
  display_name="API Key",
83
- info="The API key to use for the model.",
84
- required=True,
85
- ),
86
- StrInput(
87
- name="base_url",
88
- display_name="Base URL",
89
- info="The base URL of the API.",
90
- required=True,
91
- show=False,
92
- ),
93
- StrInput(
94
- name="project_id",
95
- display_name="Project ID",
96
- info="The project ID of the model.",
97
- required=True,
98
- show=False,
99
- ),
100
- IntInput(
101
- name="max_output_tokens",
102
- display_name="Max Output Tokens",
103
- info="The maximum number of tokens to generate.",
104
- show=False,
60
+ info="Model Provider API key",
61
+ real_time_refresh=True,
62
+ advanced=True,
105
63
  ),
106
- *openai_inputs_filtered,
107
64
  MultilineInput(
108
65
  name="system_prompt",
109
66
  display_name="Agent Instructions",
@@ -204,11 +161,16 @@ class AgentComponent(ToolCallingAgentComponent):
204
161
 
205
162
  async def get_agent_requirements(self):
206
163
  """Get the agent requirements for the agent."""
207
- llm_model, display_name = await self.get_llm()
164
+ from langchain_core.tools import StructuredTool
165
+
166
+ llm_model = get_llm(
167
+ model=self.model,
168
+ user_id=self.user_id,
169
+ api_key=self.api_key,
170
+ )
208
171
  if llm_model is None:
209
172
  msg = "No language model selected. Please choose a model to proceed."
210
173
  raise ValueError(msg)
211
- self.model_name = get_model_name(llm_model, display_name=display_name)
212
174
 
213
175
  # Get memory data
214
176
  self.chat_history = await self.get_memory_data()
@@ -456,55 +418,6 @@ class AgentComponent(ToolCallingAgentComponent):
456
418
  message for message in messages if getattr(message, "id", None) != getattr(self.input_value, "id", None)
457
419
  ]
458
420
 
459
- async def get_llm(self):
460
- if not isinstance(self.agent_llm, str):
461
- return self.agent_llm, None
462
-
463
- try:
464
- provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)
465
- if not provider_info:
466
- msg = f"Invalid model provider: {self.agent_llm}"
467
- raise ValueError(msg)
468
-
469
- component_class = provider_info.get("component_class")
470
- display_name = component_class.display_name
471
- inputs = provider_info.get("inputs")
472
- prefix = provider_info.get("prefix", "")
473
-
474
- return self._build_llm_model(component_class, inputs, prefix), display_name
475
-
476
- except (AttributeError, ValueError, TypeError, RuntimeError) as e:
477
- await logger.aerror(f"Error building {self.agent_llm} language model: {e!s}")
478
- msg = f"Failed to initialize language model: {e!s}"
479
- raise ValueError(msg) from e
480
-
481
- def _build_llm_model(self, component, inputs, prefix=""):
482
- model_kwargs = {}
483
- for input_ in inputs:
484
- if hasattr(self, f"{prefix}{input_.name}"):
485
- model_kwargs[input_.name] = getattr(self, f"{prefix}{input_.name}")
486
- return component.set(**model_kwargs).build_model()
487
-
488
- def set_component_params(self, component):
489
- provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)
490
- if provider_info:
491
- inputs = provider_info.get("inputs")
492
- prefix = provider_info.get("prefix")
493
- # Filter out json_mode and only use attributes that exist on this component
494
- model_kwargs = {}
495
- for input_ in inputs:
496
- if hasattr(self, f"{prefix}{input_.name}"):
497
- model_kwargs[input_.name] = getattr(self, f"{prefix}{input_.name}")
498
-
499
- return component.set(**model_kwargs)
500
- return component
501
-
502
- def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:
503
- """Delete specified fields from build_config."""
504
- for field in fields:
505
- if build_config is not None and field in build_config:
506
- build_config.pop(field, None)
507
-
508
421
  def update_input_types(self, build_config: dotdict) -> dotdict:
509
422
  """Update input types for all fields in build_config."""
510
423
  for key, value in build_config.items():
@@ -516,74 +429,29 @@ class AgentComponent(ToolCallingAgentComponent):
516
429
  return build_config
517
430
 
518
431
  async def update_build_config(
519
- self, build_config: dotdict, field_value: str, field_name: str | None = None
432
+ self,
433
+ build_config: dotdict,
434
+ field_value: list[dict],
435
+ field_name: str | None = None,
520
436
  ) -> dotdict:
521
- # Iterate over all providers in the MODEL_PROVIDERS_DICT
522
- # Existing logic for updating build_config
523
- if field_name in ("agent_llm",):
524
- build_config["agent_llm"]["value"] = field_value
525
- provider_info = MODEL_PROVIDERS_DICT.get(field_value)
526
- if provider_info:
527
- component_class = provider_info.get("component_class")
528
- if component_class and hasattr(component_class, "update_build_config"):
529
- # Call the component class's update_build_config method
530
- build_config = await update_component_build_config(
531
- component_class, build_config, field_value, "model_name"
532
- )
437
+ # Update model options with caching (for all field changes)
438
+ # Agents require tool calling, so filter for only tool-calling capable models
439
+ def get_tool_calling_model_options(user_id=None):
440
+ return get_language_model_options(user_id=user_id, tool_calling=True)
441
+
442
+ build_config = update_model_options_in_build_config(
443
+ component=self,
444
+ build_config=dict(build_config),
445
+ cache_key_prefix="language_model_options_tool_calling",
446
+ get_options_func=get_tool_calling_model_options,
447
+ field_name=field_name,
448
+ field_value=field_value,
449
+ )
450
+ build_config = dotdict(build_config)
533
451
 
534
- provider_configs: dict[str, tuple[dict, list[dict]]] = {
535
- provider: (
536
- MODEL_PROVIDERS_DICT[provider]["fields"],
537
- [
538
- MODEL_PROVIDERS_DICT[other_provider]["fields"]
539
- for other_provider in MODEL_PROVIDERS_DICT
540
- if other_provider != provider
541
- ],
542
- )
543
- for provider in MODEL_PROVIDERS_DICT
544
- }
545
- if field_value in provider_configs:
546
- fields_to_add, fields_to_delete = provider_configs[field_value]
547
-
548
- # Delete fields from other providers
549
- for fields in fields_to_delete:
550
- self.delete_fields(build_config, fields)
551
-
552
- # Add provider-specific fields
553
- if field_value == "OpenAI" and not any(field in build_config for field in fields_to_add):
554
- build_config.update(fields_to_add)
555
- else:
556
- build_config.update(fields_to_add)
557
- # Reset input types for agent_llm
558
- build_config["agent_llm"]["input_types"] = []
559
- build_config["agent_llm"]["display_name"] = "Model Provider"
560
- elif field_value == "connect_other_models":
561
- # Delete all provider fields
562
- self.delete_fields(build_config, ALL_PROVIDER_FIELDS)
563
- # # Update with custom component
564
- custom_component = DropdownInput(
565
- name="agent_llm",
566
- display_name="Language Model",
567
- info="The provider of the language model that the agent will use to generate responses.",
568
- options=[*MODEL_PROVIDERS_LIST],
569
- real_time_refresh=True,
570
- refresh_button=False,
571
- input_types=["LanguageModel"],
572
- placeholder="Awaiting model input.",
573
- options_metadata=[MODELS_METADATA[key] for key in MODEL_PROVIDERS_LIST if key in MODELS_METADATA],
574
- external_options={
575
- "fields": {
576
- "data": {
577
- "node": {
578
- "name": "connect_other_models",
579
- "display_name": "Connect other models",
580
- "icon": "CornerDownLeft",
581
- },
582
- }
583
- },
584
- },
585
- )
586
- build_config.update({"agent_llm": custom_component.to_dict()})
452
+ # Iterate over all providers in the MODEL_PROVIDERS_DICT
453
+ if field_name == "model":
454
+ self.log(str(field_value))
587
455
  # Update input types for all fields
588
456
  build_config = self.update_input_types(build_config)
589
457
 
@@ -591,7 +459,7 @@ class AgentComponent(ToolCallingAgentComponent):
591
459
  default_keys = [
592
460
  "code",
593
461
  "_type",
594
- "agent_llm",
462
+ "model",
595
463
  "tools",
596
464
  "input_value",
597
465
  "add_current_date_tool",
@@ -605,24 +473,6 @@ class AgentComponent(ToolCallingAgentComponent):
605
473
  if missing_keys:
606
474
  msg = f"Missing required keys in build_config: {missing_keys}"
607
475
  raise ValueError(msg)
608
- if (
609
- isinstance(self.agent_llm, str)
610
- and self.agent_llm in MODEL_PROVIDERS_DICT
611
- and field_name in MODEL_DYNAMIC_UPDATE_FIELDS
612
- ):
613
- provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)
614
- if provider_info:
615
- component_class = provider_info.get("component_class")
616
- component_class = self.set_component_params(component_class)
617
- prefix = provider_info.get("prefix")
618
- if component_class and hasattr(component_class, "update_build_config"):
619
- # Call each component class's update_build_config method
620
- # remove the prefix from the field_name
621
- if isinstance(field_name, str) and isinstance(prefix, str):
622
- field_name = field_name.replace(prefix, "")
623
- build_config = await update_component_build_config(
624
- component_class, build_config, field_value, "model_name"
625
- )
626
476
  return dotdict({k: v.to_dict() if hasattr(v, "to_dict") else v for k, v in build_config.items()})
627
477
 
628
478
  async def _get_tools(self) -> list[Tool]: