lfx-nightly 0.1.13.dev0__py3-none-any.whl → 0.2.0.dev0__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 (86) hide show
  1. lfx/_assets/component_index.json +1 -1
  2. lfx/base/agents/agent.py +109 -29
  3. lfx/base/agents/events.py +102 -35
  4. lfx/base/agents/utils.py +15 -2
  5. lfx/base/composio/composio_base.py +24 -9
  6. lfx/base/datastax/__init__.py +5 -0
  7. lfx/{components/vectorstores/astradb.py → base/datastax/astradb_base.py} +84 -473
  8. lfx/base/io/chat.py +5 -4
  9. lfx/base/mcp/util.py +101 -15
  10. lfx/base/models/model_input_constants.py +74 -7
  11. lfx/base/models/ollama_constants.py +3 -0
  12. lfx/base/models/watsonx_constants.py +12 -0
  13. lfx/cli/commands.py +1 -1
  14. lfx/components/agents/__init__.py +3 -1
  15. lfx/components/agents/agent.py +47 -4
  16. lfx/components/agents/altk_agent.py +366 -0
  17. lfx/components/agents/cuga_agent.py +1 -1
  18. lfx/components/agents/mcp_component.py +32 -2
  19. lfx/components/amazon/amazon_bedrock_converse.py +1 -1
  20. lfx/components/apify/apify_actor.py +3 -3
  21. lfx/components/datastax/__init__.py +12 -6
  22. lfx/components/datastax/{astra_assistant_manager.py → astradb_assistant_manager.py} +1 -0
  23. lfx/components/datastax/astradb_chatmemory.py +40 -0
  24. lfx/components/datastax/astradb_cql.py +5 -31
  25. lfx/components/datastax/astradb_graph.py +9 -123
  26. lfx/components/datastax/astradb_tool.py +12 -52
  27. lfx/components/datastax/astradb_vectorstore.py +133 -976
  28. lfx/components/datastax/create_assistant.py +1 -0
  29. lfx/components/datastax/create_thread.py +1 -0
  30. lfx/components/datastax/dotenv.py +1 -0
  31. lfx/components/datastax/get_assistant.py +1 -0
  32. lfx/components/datastax/getenvvar.py +1 -0
  33. lfx/components/datastax/graph_rag.py +1 -1
  34. lfx/components/datastax/list_assistants.py +1 -0
  35. lfx/components/datastax/run.py +1 -0
  36. lfx/components/docling/__init__.py +3 -0
  37. lfx/components/docling/docling_remote_vlm.py +284 -0
  38. lfx/components/ibm/watsonx.py +25 -21
  39. lfx/components/input_output/chat.py +8 -0
  40. lfx/components/input_output/chat_output.py +8 -0
  41. lfx/components/knowledge_bases/ingestion.py +17 -9
  42. lfx/components/knowledge_bases/retrieval.py +16 -8
  43. lfx/components/logic/loop.py +4 -0
  44. lfx/components/mistral/mistral_embeddings.py +1 -1
  45. lfx/components/models/embedding_model.py +88 -7
  46. lfx/components/ollama/ollama.py +221 -14
  47. lfx/components/openrouter/openrouter.py +49 -147
  48. lfx/components/processing/parser.py +6 -1
  49. lfx/components/processing/structured_output.py +55 -17
  50. lfx/components/vectorstores/__init__.py +0 -6
  51. lfx/custom/custom_component/component.py +3 -2
  52. lfx/field_typing/constants.py +1 -0
  53. lfx/graph/edge/base.py +2 -2
  54. lfx/graph/graph/base.py +1 -1
  55. lfx/graph/graph/schema.py +3 -2
  56. lfx/graph/vertex/vertex_types.py +1 -1
  57. lfx/io/schema.py +6 -0
  58. lfx/schema/schema.py +5 -0
  59. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev0.dist-info}/METADATA +1 -1
  60. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev0.dist-info}/RECORD +63 -81
  61. lfx/components/datastax/astra_db.py +0 -77
  62. lfx/components/datastax/cassandra.py +0 -92
  63. lfx/components/vectorstores/astradb_graph.py +0 -326
  64. lfx/components/vectorstores/cassandra.py +0 -264
  65. lfx/components/vectorstores/cassandra_graph.py +0 -238
  66. lfx/components/vectorstores/chroma.py +0 -167
  67. lfx/components/vectorstores/clickhouse.py +0 -135
  68. lfx/components/vectorstores/couchbase.py +0 -102
  69. lfx/components/vectorstores/elasticsearch.py +0 -267
  70. lfx/components/vectorstores/faiss.py +0 -111
  71. lfx/components/vectorstores/graph_rag.py +0 -141
  72. lfx/components/vectorstores/hcd.py +0 -314
  73. lfx/components/vectorstores/milvus.py +0 -115
  74. lfx/components/vectorstores/mongodb_atlas.py +0 -213
  75. lfx/components/vectorstores/opensearch.py +0 -243
  76. lfx/components/vectorstores/pgvector.py +0 -72
  77. lfx/components/vectorstores/pinecone.py +0 -134
  78. lfx/components/vectorstores/qdrant.py +0 -109
  79. lfx/components/vectorstores/supabase.py +0 -76
  80. lfx/components/vectorstores/upstash.py +0 -124
  81. lfx/components/vectorstores/vectara.py +0 -97
  82. lfx/components/vectorstores/vectara_rag.py +0 -164
  83. lfx/components/vectorstores/weaviate.py +0 -89
  84. /lfx/components/datastax/{astra_vectorize.py → astradb_vectorize.py} +0 -0
  85. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev0.dist-info}/WHEEL +0 -0
  86. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev0.dist-info}/entry_points.txt +0 -0
lfx/base/io/chat.py CHANGED
@@ -6,8 +6,9 @@ class ChatComponent(Component):
6
6
  description = "Use as base for chat components."
7
7
 
8
8
  def get_properties_from_source_component(self):
9
- if hasattr(self, "_vertex") and hasattr(self._vertex, "incoming_edges") and self._vertex.incoming_edges:
10
- source_id = self._vertex.incoming_edges[0].source_id
9
+ vertex = self.get_vertex()
10
+ if vertex and hasattr(vertex, "incoming_edges") and vertex.incoming_edges:
11
+ source_id = vertex.incoming_edges[0].source_id
11
12
  source_vertex = self.graph.get_vertex(source_id)
12
13
  component = source_vertex.custom_component
13
14
  source = component.display_name
@@ -15,6 +16,6 @@ class ChatComponent(Component):
15
16
  possible_attributes = ["model_name", "model_id", "model"]
16
17
  for attribute in possible_attributes:
17
18
  if hasattr(component, attribute) and getattr(component, attribute):
18
- return getattr(component, attribute), icon, source, component._id
19
- return source, icon, component.display_name, component._id
19
+ return getattr(component, attribute), icon, source, component.get_id()
20
+ return source, icon, component.display_name, component.get_id()
20
21
  return None, None, None, None
lfx/base/mcp/util.py CHANGED
@@ -35,13 +35,33 @@ HTTP_INTERNAL_SERVER_ERROR = 500
35
35
  HTTP_UNAUTHORIZED = 401
36
36
  HTTP_FORBIDDEN = 403
37
37
 
38
- # MCP Session Manager constants
39
- settings = get_settings_service().settings
40
- MAX_SESSIONS_PER_SERVER = (
41
- settings.mcp_max_sessions_per_server
42
- ) # Maximum number of sessions per server to prevent resource exhaustion
43
- SESSION_IDLE_TIMEOUT = settings.mcp_session_idle_timeout # 5 minutes idle timeout for sessions
44
- SESSION_CLEANUP_INTERVAL = settings.mcp_session_cleanup_interval # Cleanup interval in seconds
38
+ # MCP Session Manager constants - lazy loaded
39
+ _mcp_settings_cache: dict[str, Any] = {}
40
+
41
+
42
+ def _get_mcp_setting(key: str, default: Any = None) -> Any:
43
+ """Lazy load MCP settings from settings service."""
44
+ if key not in _mcp_settings_cache:
45
+ settings = get_settings_service().settings
46
+ _mcp_settings_cache[key] = getattr(settings, key, default)
47
+ return _mcp_settings_cache[key]
48
+
49
+
50
+ def get_max_sessions_per_server() -> int:
51
+ """Get maximum number of sessions per server to prevent resource exhaustion."""
52
+ return _get_mcp_setting("mcp_max_sessions_per_server")
53
+
54
+
55
+ def get_session_idle_timeout() -> int:
56
+ """Get 5 minutes idle timeout for sessions."""
57
+ return _get_mcp_setting("mcp_session_idle_timeout")
58
+
59
+
60
+ def get_session_cleanup_interval() -> int:
61
+ """Get cleanup interval in seconds."""
62
+ return _get_mcp_setting("mcp_session_cleanup_interval")
63
+
64
+
45
65
  # RFC 7230 compliant header name pattern: token = 1*tchar
46
66
  # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
47
67
  # "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
@@ -65,6 +85,46 @@ ALLOWED_HEADERS = {
65
85
  }
66
86
 
67
87
 
88
+ def create_mcp_http_client_with_ssl_option(
89
+ headers: dict[str, str] | None = None,
90
+ timeout: httpx.Timeout | None = None,
91
+ auth: httpx.Auth | None = None,
92
+ *,
93
+ verify_ssl: bool = True,
94
+ ) -> httpx.AsyncClient:
95
+ """Create an httpx AsyncClient with configurable SSL verification.
96
+
97
+ This is a custom factory that extends the standard MCP client factory
98
+ to support disabling SSL verification for self-signed certificates.
99
+
100
+ Args:
101
+ headers: Optional headers to include with all requests.
102
+ timeout: Request timeout as httpx.Timeout object.
103
+ auth: Optional authentication handler.
104
+ verify_ssl: Whether to verify SSL certificates (default: True).
105
+
106
+ Returns:
107
+ Configured httpx.AsyncClient instance.
108
+ """
109
+ kwargs: dict[str, Any] = {
110
+ "follow_redirects": True,
111
+ "verify": verify_ssl,
112
+ }
113
+
114
+ if timeout is None:
115
+ kwargs["timeout"] = httpx.Timeout(30.0)
116
+ else:
117
+ kwargs["timeout"] = timeout
118
+
119
+ if headers is not None:
120
+ kwargs["headers"] = headers
121
+
122
+ if auth is not None:
123
+ kwargs["auth"] = auth
124
+
125
+ return httpx.AsyncClient(**kwargs)
126
+
127
+
68
128
  def validate_headers(headers: dict[str, str]) -> dict[str, str]:
69
129
  """Validate and sanitize HTTP headers according to RFC 7230.
70
130
 
@@ -432,7 +492,7 @@ class MCPSessionManager:
432
492
  """Periodically clean up idle sessions."""
433
493
  while True:
434
494
  try:
435
- await asyncio.sleep(SESSION_CLEANUP_INTERVAL)
495
+ await asyncio.sleep(get_session_cleanup_interval())
436
496
  await self._cleanup_idle_sessions()
437
497
  except asyncio.CancelledError:
438
498
  break
@@ -450,7 +510,7 @@ class MCPSessionManager:
450
510
  sessions_to_remove = []
451
511
 
452
512
  for session_id, session_info in list(sessions.items()):
453
- if current_time - session_info["last_used"] > SESSION_IDLE_TIMEOUT:
513
+ if current_time - session_info["last_used"] > get_session_idle_timeout():
454
514
  sessions_to_remove.append(session_id)
455
515
 
456
516
  # Clean up idle sessions
@@ -573,7 +633,7 @@ class MCPSessionManager:
573
633
  await self._cleanup_session_by_id(server_key, session_id)
574
634
 
575
635
  # Check if we've reached the maximum number of sessions for this server
576
- if len(sessions) >= MAX_SESSIONS_PER_SERVER:
636
+ if len(sessions) >= get_max_sessions_per_server():
577
637
  # Remove the oldest session
578
638
  oldest_session_id = min(sessions.keys(), key=lambda x: sessions[x]["last_used"])
579
639
  await logger.ainfo(
@@ -675,7 +735,7 @@ class MCPSessionManager:
675
735
 
676
736
  Args:
677
737
  session_id: Unique identifier for this session
678
- connection_params: Connection parameters including URL, headers, timeouts
738
+ connection_params: Connection parameters including URL, headers, timeouts, verify_ssl
679
739
  preferred_transport: If set to "sse", skip Streamable HTTP and go directly to SSE
680
740
 
681
741
  Returns:
@@ -691,6 +751,19 @@ class MCPSessionManager:
691
751
  # Track which transport succeeded
692
752
  used_transport: list[str] = []
693
753
 
754
+ # Get verify_ssl option from connection params, default to True
755
+ verify_ssl = connection_params.get("verify_ssl", True)
756
+
757
+ # Create custom httpx client factory with SSL verification option
758
+ def custom_httpx_factory(
759
+ headers: dict[str, str] | None = None,
760
+ timeout: httpx.Timeout | None = None,
761
+ auth: httpx.Auth | None = None,
762
+ ) -> httpx.AsyncClient:
763
+ return create_mcp_http_client_with_ssl_option(
764
+ headers=headers, timeout=timeout, auth=auth, verify_ssl=verify_ssl
765
+ )
766
+
694
767
  async def session_task():
695
768
  """Background task that keeps the session alive."""
696
769
  streamable_error = None
@@ -705,6 +778,7 @@ class MCPSessionManager:
705
778
  url=connection_params["url"],
706
779
  headers=connection_params["headers"],
707
780
  timeout=connection_params["timeout_seconds"],
781
+ httpx_client_factory=custom_httpx_factory,
708
782
  ) as (read, write, _):
709
783
  session = ClientSession(read, write)
710
784
  async with session:
@@ -745,6 +819,7 @@ class MCPSessionManager:
745
819
  connection_params["headers"],
746
820
  connection_params["timeout_seconds"],
747
821
  sse_read_timeout,
822
+ httpx_client_factory=custom_httpx_factory,
748
823
  ) as (read, write):
749
824
  session = ClientSession(read, write)
750
825
  async with session:
@@ -1196,6 +1271,8 @@ class MCPStreamableHttpClient:
1196
1271
  headers: dict[str, str] | None = None,
1197
1272
  timeout_seconds: int = 30,
1198
1273
  sse_read_timeout_seconds: int = 30,
1274
+ *,
1275
+ verify_ssl: bool = True,
1199
1276
  ) -> list[StructuredTool]:
1200
1277
  """Connect to MCP server using Streamable HTTP transport with SSE fallback (SDK style)."""
1201
1278
  # Validate and sanitize headers early
@@ -1213,12 +1290,13 @@ class MCPStreamableHttpClient:
1213
1290
  msg = f"Invalid Streamable HTTP or SSE URL ({url}): {error_msg}"
1214
1291
  raise ValueError(msg)
1215
1292
  # Store connection parameters for later use in run_tool
1216
- # Include SSE read timeout for fallback
1293
+ # Include SSE read timeout for fallback and SSL verification option
1217
1294
  self._connection_params = {
1218
1295
  "url": url,
1219
1296
  "headers": validated_headers,
1220
1297
  "timeout_seconds": timeout_seconds,
1221
1298
  "sse_read_timeout_seconds": sse_read_timeout_seconds,
1299
+ "verify_ssl": verify_ssl,
1222
1300
  }
1223
1301
  elif headers:
1224
1302
  self._connection_params["headers"] = validated_headers
@@ -1238,11 +1316,18 @@ class MCPStreamableHttpClient:
1238
1316
  return response.tools
1239
1317
 
1240
1318
  async def connect_to_server(
1241
- self, url: str, headers: dict[str, str] | None = None, sse_read_timeout_seconds: int = 30
1319
+ self,
1320
+ url: str,
1321
+ headers: dict[str, str] | None = None,
1322
+ sse_read_timeout_seconds: int = 30,
1323
+ *,
1324
+ verify_ssl: bool = True,
1242
1325
  ) -> list[StructuredTool]:
1243
1326
  """Connect to MCP server using Streamable HTTP with SSE fallback transport (SDK style)."""
1244
1327
  return await asyncio.wait_for(
1245
- self._connect_to_server(url, headers, sse_read_timeout_seconds=sse_read_timeout_seconds),
1328
+ self._connect_to_server(
1329
+ url, headers, sse_read_timeout_seconds=sse_read_timeout_seconds, verify_ssl=verify_ssl
1330
+ ),
1246
1331
  timeout=get_settings_service().settings.mcp_server_timeout,
1247
1332
  )
1248
1333
 
@@ -1473,7 +1558,8 @@ async def update_tools(
1473
1558
  client = mcp_stdio_client
1474
1559
  elif mode in ["Streamable_HTTP", "SSE"]:
1475
1560
  # Streamable HTTP connection with SSE fallback
1476
- tools = await mcp_streamable_http_client.connect_to_server(url, headers=headers)
1561
+ verify_ssl = server_config.get("verify_ssl", True)
1562
+ tools = await mcp_streamable_http_client.connect_to_server(url, headers=headers, verify_ssl=verify_ssl)
1477
1563
  client = mcp_streamable_http_client
1478
1564
  else:
1479
1565
  logger.error(f"Invalid MCP server mode for '{server_name}': {mode}")
@@ -14,14 +14,18 @@ class ModelProvidersDict(TypedDict):
14
14
  is_active: bool
15
15
 
16
16
 
17
- def get_filtered_inputs(component_class):
17
+ def get_filtered_inputs(component_class, provider_name: str | None = None):
18
18
  base_input_names = {field.name for field in LCModelComponent.get_base_inputs()}
19
19
  component_instance = component_class()
20
20
 
21
- return [process_inputs(input_) for input_ in component_instance.inputs if input_.name not in base_input_names]
21
+ return [
22
+ process_inputs(input_, provider_name)
23
+ for input_ in component_instance.inputs
24
+ if input_.name not in base_input_names
25
+ ]
22
26
 
23
27
 
24
- def process_inputs(component_data: Input):
28
+ def process_inputs(component_data: Input, provider_name: str | None = None):
25
29
  """Processes and modifies an input configuration based on its type or name.
26
30
 
27
31
  Adjusts properties such as value, advanced status, real-time refresh, and additional information for specific
@@ -29,6 +33,7 @@ def process_inputs(component_data: Input):
29
33
 
30
34
  Args:
31
35
  component_data: The input configuration to process.
36
+ provider_name: The name of the provider to process the inputs for.
32
37
 
33
38
  Returns:
34
39
  The modified input configuration.
@@ -43,9 +48,11 @@ def process_inputs(component_data: Input):
43
48
  component_data.advanced = True
44
49
  component_data.value = True
45
50
  elif component_data.name in {"temperature", "base_url"}:
46
- component_data = set_advanced_true(component_data)
51
+ if provider_name not in ["IBM watsonx.ai", "Ollama"]:
52
+ component_data = set_advanced_true(component_data)
47
53
  elif component_data.name == "model_name":
48
- component_data = set_real_time_refresh_false(component_data)
54
+ if provider_name not in ["IBM watsonx.ai"]:
55
+ component_data = set_real_time_refresh_false(component_data)
49
56
  component_data = add_combobox_true(component_data)
50
57
  component_data = add_info(
51
58
  component_data,
@@ -79,6 +86,28 @@ def create_input_fields_dict(inputs: list[Input], prefix: str) -> dict[str, Inpu
79
86
  return {f"{prefix}{input_.name}": input_.to_dict() for input_ in inputs}
80
87
 
81
88
 
89
+ def _get_ollama_inputs_and_fields():
90
+ try:
91
+ from lfx.components.ollama.ollama import ChatOllamaComponent
92
+
93
+ ollama_inputs = get_filtered_inputs(ChatOllamaComponent, provider_name="Ollama")
94
+ except ImportError as e:
95
+ msg = "Ollama is not installed. Please install it with `pip install langchain-ollama`."
96
+ raise ImportError(msg) from e
97
+ return ollama_inputs, create_input_fields_dict(ollama_inputs, "")
98
+
99
+
100
+ def _get_watsonx_inputs_and_fields():
101
+ try:
102
+ from lfx.components.ibm.watsonx import WatsonxAIComponent
103
+
104
+ watsonx_inputs = get_filtered_inputs(WatsonxAIComponent, provider_name="IBM watsonx.ai")
105
+ except ImportError as e:
106
+ msg = "IBM watsonx.ai is not installed. Please install it with `pip install langchain-ibm-watsonx`."
107
+ raise ImportError(msg) from e
108
+ return watsonx_inputs, create_input_fields_dict(watsonx_inputs, "")
109
+
110
+
82
111
  def _get_google_generative_ai_inputs_and_fields():
83
112
  try:
84
113
  from lfx.components.google.google_generative_ai import GoogleGenerativeAIComponent
@@ -293,6 +322,36 @@ try:
293
322
  except ImportError:
294
323
  pass
295
324
 
325
+ try:
326
+ from lfx.components.ibm.watsonx import WatsonxAIComponent
327
+
328
+ watsonx_inputs, watsonx_fields = _get_watsonx_inputs_and_fields()
329
+ MODEL_PROVIDERS_DICT["IBM watsonx.ai"] = {
330
+ "fields": watsonx_fields,
331
+ "inputs": watsonx_inputs,
332
+ "prefix": "",
333
+ "component_class": WatsonxAIComponent(),
334
+ "icon": WatsonxAIComponent.icon,
335
+ "is_active": True,
336
+ }
337
+ except ImportError:
338
+ pass
339
+
340
+ try:
341
+ from lfx.components.ollama.ollama import ChatOllamaComponent
342
+
343
+ ollama_inputs, ollama_fields = _get_ollama_inputs_and_fields()
344
+ MODEL_PROVIDERS_DICT["Ollama"] = {
345
+ "fields": ollama_fields,
346
+ "inputs": ollama_inputs,
347
+ "prefix": "",
348
+ "component_class": ChatOllamaComponent(),
349
+ "icon": ChatOllamaComponent.icon,
350
+ "is_active": True,
351
+ }
352
+ except ImportError:
353
+ pass
354
+
296
355
  # Expose only active providers ----------------------------------------------
297
356
  ACTIVE_MODEL_PROVIDERS_DICT: dict[str, ModelProvidersDict] = {
298
357
  name: prov for name, prov in MODEL_PROVIDERS_DICT.items() if prov.get("is_active", True)
@@ -302,10 +361,18 @@ MODEL_PROVIDERS: list[str] = list(ACTIVE_MODEL_PROVIDERS_DICT.keys())
302
361
 
303
362
  ALL_PROVIDER_FIELDS: list[str] = [field for prov in ACTIVE_MODEL_PROVIDERS_DICT.values() for field in prov["fields"]]
304
363
 
305
- MODEL_DYNAMIC_UPDATE_FIELDS = ["api_key", "model", "tool_model_enabled", "base_url", "model_name"]
364
+ MODEL_DYNAMIC_UPDATE_FIELDS = [
365
+ "api_key",
366
+ "model",
367
+ "tool_model_enabled",
368
+ "base_url",
369
+ "model_name",
370
+ "watsonx_endpoint",
371
+ "url",
372
+ ]
306
373
 
307
374
  MODELS_METADATA = {name: {"icon": prov["icon"]} for name, prov in ACTIVE_MODEL_PROVIDERS_DICT.items()}
308
375
 
309
- MODEL_PROVIDERS_LIST = ["Anthropic", "Google Generative AI", "OpenAI"]
376
+ MODEL_PROVIDERS_LIST = ["Anthropic", "Google Generative AI", "OpenAI", "IBM watsonx.ai", "Ollama"]
310
377
 
311
378
  MODEL_OPTIONS_METADATA = [MODELS_METADATA[key] for key in MODEL_PROVIDERS_LIST if key in MODELS_METADATA]
@@ -47,3 +47,6 @@ URL_LIST = [
47
47
  "http://127.0.0.1:11434",
48
48
  "http://0.0.0.0:11434",
49
49
  ]
50
+
51
+
52
+ DEFAULT_OLLAMA_API_URL = "https://ollama.com"
@@ -0,0 +1,12 @@
1
+ from .model_metadata import create_model_metadata
2
+
3
+ # Granite Embedding models
4
+ WATSONX_EMBEDDING_MODELS_DETAILED = [
5
+ create_model_metadata(provider="IBM Watsonx", name="ibm/granite-embedding-125m-english", icon="IBMWatsonx"),
6
+ create_model_metadata(provider="IBM Watsonx", name="ibm/granite-embedding-278m-multilingual", icon="IBMWatsonx"),
7
+ create_model_metadata(provider="IBM Watsonx", name="ibm/granite-embedding-30m-english", icon="IBMWatsonx"),
8
+ create_model_metadata(provider="IBM Watsonx", name="ibm/granite-embedding-107m-multilingual", icon="IBMWatsonx"),
9
+ create_model_metadata(provider="IBM Watsonx", name="ibm/granite-embedding-30m-sparse", icon="IBMWatsonx"),
10
+ ]
11
+
12
+ WATSONX_EMBEDDING_MODEL_NAMES = [metadata["name"] for metadata in WATSONX_EMBEDDING_MODELS_DETAILED]
lfx/cli/commands.py CHANGED
@@ -43,7 +43,7 @@ def serve_command(
43
43
  host: str = typer.Option("127.0.0.1", "--host", "-h", help="Host to bind the server to"),
44
44
  port: int = typer.Option(8000, "--port", "-p", help="Port to bind the server to"),
45
45
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Show diagnostic output and execution details"), # noqa: FBT001, FBT003
46
- env_file: Path | None = typer.Option( # noqa: B008
46
+ env_file: Path | None = typer.Option(
47
47
  None,
48
48
  "--env-file",
49
49
  help="Path to the .env file containing environment variables",
@@ -6,6 +6,7 @@ from lfx.components._importing import import_mod
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from lfx.components.agents.agent import AgentComponent
9
+ from lfx.components.agents.altk_agent import ALTKAgentComponent
9
10
  from lfx.components.agents.cuga_agent import CugaComponent
10
11
  from lfx.components.agents.mcp_component import MCPToolsComponent
11
12
 
@@ -13,9 +14,10 @@ _dynamic_imports = {
13
14
  "AgentComponent": "agent",
14
15
  "CugaComponent": "cuga_agent",
15
16
  "MCPToolsComponent": "mcp_component",
17
+ "ALTKAgentComponent": "altk_agent",
16
18
  }
17
19
 
18
- __all__ = ["AgentComponent", "CugaComponent", "MCPToolsComponent"]
20
+ __all__ = ["ALTKAgentComponent", "AgentComponent", "CugaComponent", "MCPToolsComponent"]
19
21
 
20
22
 
21
23
  def __getattr__(attr_name: str) -> Any:
@@ -20,8 +20,8 @@ from lfx.components.langchain_utilities.tool_calling import ToolCallingAgentComp
20
20
  from lfx.custom.custom_component.component import get_component_toolkit
21
21
  from lfx.custom.utils import update_component_build_config
22
22
  from lfx.helpers.base_model import build_model_from_schema
23
- from lfx.inputs.inputs import BoolInput
24
- from lfx.io import DropdownInput, IntInput, MultilineInput, Output, TableInput
23
+ from lfx.inputs.inputs import BoolInput, SecretStrInput, StrInput
24
+ from lfx.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output, TableInput
25
25
  from lfx.log.logger import logger
26
26
  from lfx.schema.data import Data
27
27
  from lfx.schema.dotdict import dotdict
@@ -77,6 +77,32 @@ class AgentComponent(ToolCallingAgentComponent):
77
77
  },
78
78
  },
79
79
  ),
80
+ SecretStrInput(
81
+ name="api_key",
82
+ 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,
105
+ ),
80
106
  *openai_inputs_filtered,
81
107
  MultilineInput(
82
108
  name="system_prompt",
@@ -85,6 +111,13 @@ class AgentComponent(ToolCallingAgentComponent):
85
111
  value="You are a helpful assistant that can use tools to answer questions and perform tasks.",
86
112
  advanced=False,
87
113
  ),
114
+ MessageTextInput(
115
+ name="context_id",
116
+ display_name="Context ID",
117
+ info="The context ID of the chat. Adds an extra layer to the local memory.",
118
+ value="",
119
+ advanced=True,
120
+ ),
88
121
  IntInput(
89
122
  name="n_messages",
90
123
  display_name="Number of Chat History Messages",
@@ -154,7 +187,7 @@ class AgentComponent(ToolCallingAgentComponent):
154
187
  },
155
188
  ],
156
189
  ),
157
- *LCToolsAgentComponent._base_inputs,
190
+ *LCToolsAgentComponent.get_base_inputs(),
158
191
  # removed memory inputs from agent component
159
192
  # *memory_inputs,
160
193
  BoolInput(
@@ -188,10 +221,15 @@ class AgentComponent(ToolCallingAgentComponent):
188
221
  if not isinstance(self.tools, list): # type: ignore[has-type]
189
222
  self.tools = []
190
223
  current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)
224
+
191
225
  if not isinstance(current_date_tool, StructuredTool):
192
226
  msg = "CurrentDateComponent must be converted to a StructuredTool"
193
227
  raise TypeError(msg)
194
228
  self.tools.append(current_date_tool)
229
+
230
+ # Set shared callbacks for tracing the tools used by the agent
231
+ self.set_tools_callbacks(self.tools, self._get_shared_callbacks())
232
+
195
233
  return llm_model, self.chat_history, self.tools
196
234
 
197
235
  async def message_response(self) -> Message:
@@ -408,6 +446,7 @@ class AgentComponent(ToolCallingAgentComponent):
408
446
  await MemoryComponent(**self.get_base_args())
409
447
  .set(
410
448
  session_id=self.graph.session_id,
449
+ context_id=self.context_id,
411
450
  order="Ascending",
412
451
  n_messages=self.n_messages,
413
452
  )
@@ -463,7 +502,8 @@ class AgentComponent(ToolCallingAgentComponent):
463
502
  def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:
464
503
  """Delete specified fields from build_config."""
465
504
  for field in fields:
466
- build_config.pop(field, None)
505
+ if build_config is not None and field in build_config:
506
+ build_config.pop(field, None)
467
507
 
468
508
  def update_input_types(self, build_config: dotdict) -> dotdict:
469
509
  """Update input types for all fields in build_config."""
@@ -591,11 +631,14 @@ class AgentComponent(ToolCallingAgentComponent):
591
631
  agent_description = self.get_tool_description()
592
632
  # TODO: Agent Description Depreciated Feature to be removed
593
633
  description = f"{agent_description}{tools_names}"
634
+
594
635
  tools = component_toolkit(component=self).get_tools(
595
636
  tool_name="Call_Agent",
596
637
  tool_description=description,
638
+ # here we do not use the shared callbacks as we are exposing the agent as a tool
597
639
  callbacks=self.get_langchain_callbacks(),
598
640
  )
599
641
  if hasattr(self, "tools_metadata"):
600
642
  tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)
643
+
601
644
  return tools