lfx-nightly 0.2.0.dev41__py3-none-any.whl → 0.3.0.dev3__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 (98) hide show
  1. lfx/__main__.py +137 -6
  2. lfx/_assets/component_index.json +1 -1
  3. lfx/base/agents/agent.py +10 -6
  4. lfx/base/agents/altk_base_agent.py +5 -3
  5. lfx/base/agents/altk_tool_wrappers.py +1 -1
  6. lfx/base/agents/events.py +1 -1
  7. lfx/base/agents/utils.py +4 -0
  8. lfx/base/composio/composio_base.py +78 -41
  9. lfx/base/data/cloud_storage_utils.py +156 -0
  10. lfx/base/data/docling_utils.py +130 -55
  11. lfx/base/datastax/astradb_base.py +75 -64
  12. lfx/base/embeddings/embeddings_class.py +113 -0
  13. lfx/base/models/__init__.py +11 -1
  14. lfx/base/models/google_generative_ai_constants.py +33 -9
  15. lfx/base/models/model_metadata.py +6 -0
  16. lfx/base/models/ollama_constants.py +196 -30
  17. lfx/base/models/openai_constants.py +37 -10
  18. lfx/base/models/unified_models.py +1123 -0
  19. lfx/base/models/watsonx_constants.py +43 -4
  20. lfx/base/prompts/api_utils.py +40 -5
  21. lfx/base/tools/component_tool.py +2 -9
  22. lfx/cli/__init__.py +10 -2
  23. lfx/cli/commands.py +3 -0
  24. lfx/cli/run.py +65 -409
  25. lfx/cli/script_loader.py +18 -7
  26. lfx/cli/validation.py +6 -3
  27. lfx/components/__init__.py +0 -3
  28. lfx/components/composio/github_composio.py +1 -1
  29. lfx/components/cuga/cuga_agent.py +39 -27
  30. lfx/components/data_source/api_request.py +4 -2
  31. lfx/components/datastax/astradb_assistant_manager.py +4 -2
  32. lfx/components/docling/__init__.py +45 -11
  33. lfx/components/docling/docling_inline.py +39 -49
  34. lfx/components/docling/docling_remote.py +1 -0
  35. lfx/components/elastic/opensearch_multimodal.py +1733 -0
  36. lfx/components/files_and_knowledge/file.py +384 -36
  37. lfx/components/files_and_knowledge/ingestion.py +8 -0
  38. lfx/components/files_and_knowledge/retrieval.py +10 -0
  39. lfx/components/files_and_knowledge/save_file.py +91 -88
  40. lfx/components/langchain_utilities/ibm_granite_handler.py +211 -0
  41. lfx/components/langchain_utilities/tool_calling.py +37 -6
  42. lfx/components/llm_operations/batch_run.py +64 -18
  43. lfx/components/llm_operations/lambda_filter.py +213 -101
  44. lfx/components/llm_operations/llm_conditional_router.py +39 -7
  45. lfx/components/llm_operations/structured_output.py +38 -12
  46. lfx/components/models/__init__.py +16 -74
  47. lfx/components/models_and_agents/agent.py +51 -203
  48. lfx/components/models_and_agents/embedding_model.py +171 -255
  49. lfx/components/models_and_agents/language_model.py +54 -318
  50. lfx/components/models_and_agents/mcp_component.py +96 -10
  51. lfx/components/models_and_agents/prompt.py +105 -18
  52. lfx/components/ollama/ollama_embeddings.py +111 -29
  53. lfx/components/openai/openai_chat_model.py +1 -1
  54. lfx/components/processing/text_operations.py +580 -0
  55. lfx/components/vllm/__init__.py +37 -0
  56. lfx/components/vllm/vllm.py +141 -0
  57. lfx/components/vllm/vllm_embeddings.py +110 -0
  58. lfx/custom/custom_component/component.py +65 -10
  59. lfx/custom/custom_component/custom_component.py +8 -6
  60. lfx/events/observability/__init__.py +0 -0
  61. lfx/events/observability/lifecycle_events.py +111 -0
  62. lfx/field_typing/__init__.py +57 -58
  63. lfx/graph/graph/base.py +40 -1
  64. lfx/graph/utils.py +109 -30
  65. lfx/graph/vertex/base.py +75 -23
  66. lfx/graph/vertex/vertex_types.py +0 -5
  67. lfx/inputs/__init__.py +2 -0
  68. lfx/inputs/input_mixin.py +55 -0
  69. lfx/inputs/inputs.py +120 -0
  70. lfx/interface/components.py +24 -7
  71. lfx/interface/initialize/loading.py +42 -12
  72. lfx/io/__init__.py +2 -0
  73. lfx/run/__init__.py +5 -0
  74. lfx/run/base.py +464 -0
  75. lfx/schema/__init__.py +50 -0
  76. lfx/schema/data.py +1 -1
  77. lfx/schema/image.py +26 -7
  78. lfx/schema/message.py +104 -11
  79. lfx/schema/workflow.py +171 -0
  80. lfx/services/deps.py +12 -0
  81. lfx/services/interfaces.py +43 -1
  82. lfx/services/mcp_composer/service.py +7 -1
  83. lfx/services/schema.py +1 -0
  84. lfx/services/settings/auth.py +95 -4
  85. lfx/services/settings/base.py +11 -1
  86. lfx/services/settings/constants.py +2 -0
  87. lfx/services/settings/utils.py +82 -0
  88. lfx/services/storage/local.py +13 -8
  89. lfx/services/transaction/__init__.py +5 -0
  90. lfx/services/transaction/service.py +35 -0
  91. lfx/tests/unit/components/__init__.py +0 -0
  92. lfx/utils/constants.py +2 -0
  93. lfx/utils/mustache_security.py +79 -0
  94. lfx/utils/validate_cloud.py +81 -3
  95. {lfx_nightly-0.2.0.dev41.dist-info → lfx_nightly-0.3.0.dev3.dist-info}/METADATA +7 -2
  96. {lfx_nightly-0.2.0.dev41.dist-info → lfx_nightly-0.3.0.dev3.dist-info}/RECORD +98 -80
  97. {lfx_nightly-0.2.0.dev41.dist-info → lfx_nightly-0.3.0.dev3.dist-info}/WHEEL +0 -0
  98. {lfx_nightly-0.2.0.dev41.dist-info → lfx_nightly-0.3.0.dev3.dist-info}/entry_points.txt +0 -0
@@ -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,26 +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_without_prefix = field_name.replace(prefix, "")
623
- else:
624
- field_name_without_prefix = field_name
625
- build_config = await update_component_build_config(
626
- component_class, build_config, field_value, field_name_without_prefix
627
- )
628
476
  return dotdict({k: v.to_dict() if hasattr(v, "to_dict") else v for k, v in build_config.items()})
629
477
 
630
478
  async def _get_tools(self) -> list[Tool]: