lfx-nightly 0.1.12.dev38__py3-none-any.whl → 0.1.12.dev39__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.

Potentially problematic release.


This version of lfx-nightly might be problematic. Click here for more details.

@@ -67,6 +67,96 @@ class ComposioBaseComponent(Component):
67
67
  auth_tooltip="Please insert a valid Composio API Key.",
68
68
  show=False,
69
69
  ),
70
+ # Pre-defined placeholder fields for dynamic auth - will be updated dynamically
71
+ StrInput(
72
+ name="client_id",
73
+ display_name="Client id",
74
+ info="",
75
+ show=False,
76
+ value="",
77
+ required=False,
78
+ ),
79
+ StrInput(
80
+ name="client_secret",
81
+ display_name="Client secret",
82
+ info="",
83
+ show=False,
84
+ value="",
85
+ required=False,
86
+ ),
87
+ StrInput(
88
+ name="verification_token",
89
+ display_name="Verification Token",
90
+ info="",
91
+ show=False,
92
+ value="",
93
+ required=False,
94
+ ),
95
+ StrInput(
96
+ name="api_key_field",
97
+ display_name="API Key",
98
+ info="",
99
+ show=False,
100
+ value="",
101
+ required=False,
102
+ ),
103
+ StrInput(
104
+ name="token",
105
+ display_name="Token",
106
+ info="",
107
+ show=False,
108
+ value="",
109
+ required=False,
110
+ ),
111
+ # Additional common auth fields
112
+ StrInput(
113
+ name="access_token",
114
+ display_name="Access Token",
115
+ info="",
116
+ show=False,
117
+ value="",
118
+ required=False,
119
+ ),
120
+ StrInput(
121
+ name="refresh_token",
122
+ display_name="Refresh Token",
123
+ info="",
124
+ show=False,
125
+ value="",
126
+ required=False,
127
+ ),
128
+ StrInput(
129
+ name="username",
130
+ display_name="Username",
131
+ info="",
132
+ show=False,
133
+ value="",
134
+ required=False,
135
+ ),
136
+ StrInput(
137
+ name="password",
138
+ display_name="Password",
139
+ info="",
140
+ show=False,
141
+ value="",
142
+ required=False,
143
+ ),
144
+ StrInput(
145
+ name="domain",
146
+ display_name="Domain",
147
+ info="",
148
+ show=False,
149
+ value="",
150
+ required=False,
151
+ ),
152
+ StrInput(
153
+ name="base_url",
154
+ display_name="Base URL",
155
+ info="",
156
+ show=False,
157
+ value="",
158
+ required=False,
159
+ ),
70
160
  SortableListInput(
71
161
  name="action_button",
72
162
  display_name="Action",
@@ -761,9 +851,6 @@ class ComposioBaseComponent(Component):
761
851
  for inp in lf_inputs:
762
852
  if inp.name is not None:
763
853
  inp_dict = inp.to_dict() if hasattr(inp, "to_dict") else inp.__dict__.copy()
764
-
765
- # Do not mutate input_types here; keep original configuration
766
-
767
854
  inp_dict.setdefault("show", True) # visible once action selected
768
855
  # Preserve previously entered value if user already filled something
769
856
  if inp.name in build_config:
@@ -925,26 +1012,14 @@ class ComposioBaseComponent(Component):
925
1012
  return self._toolkit_schema
926
1013
  try:
927
1014
  composio = self._build_wrapper()
928
- # The SDK typically offers a retrieve by slug; if not present, try a few fallbacks
929
1015
  app_slug = getattr(self, "app_name", "").lower()
930
1016
  if not app_slug:
931
1017
  return None
932
1018
  try:
933
- schema = composio.toolkits.retrieve(slug=app_slug)
934
- except (AttributeError, ValueError, ConnectionError, TypeError):
935
- schema = None
936
- for method_name, kwargs in (
937
- ("retrieve", {"toolkit_slug": app_slug}),
938
- ("get", {"slug": app_slug}),
939
- ("get", {"toolkit_slug": app_slug}),
940
- ):
941
- try:
942
- method = getattr(composio.toolkits, method_name)
943
- schema = method(**kwargs)
944
- if schema:
945
- break
946
- except (AttributeError, ValueError, ConnectionError, TypeError):
947
- continue
1019
+ schema = composio.toolkits.get(slug=app_slug)
1020
+ except (AttributeError, ValueError, ConnectionError, TypeError) as e:
1021
+ logger.debug(f"Could not get toolkit schema for {app_slug}: {e}")
1022
+ return None
948
1023
  self._toolkit_schema = self._to_plain_dict(schema)
949
1024
  except (AttributeError, ValueError, ConnectionError, TypeError) as e:
950
1025
  logger.debug(f"Could not retrieve toolkit schema for {getattr(self, 'app_name', '')}: {e}")
@@ -959,8 +1034,12 @@ class ComposioBaseComponent(Component):
959
1034
  modes: list[str] = []
960
1035
  # composio_managed_auth_schemes: list[str]
961
1036
  managed = schema.get("composio_managed_auth_schemes") or schema.get("composioManagedAuthSchemes") or []
962
- if isinstance(managed, list):
963
- modes.extend([m for m in managed if isinstance(m, str)])
1037
+ has_managed_schemes = isinstance(managed, list) and len(managed) > 0
1038
+
1039
+ # Add "Composio managed" as first option if there are managed schemes
1040
+ if has_managed_schemes:
1041
+ modes.append("Composio managed")
1042
+
964
1043
  # auth_config_details: list with entries containing mode
965
1044
  details = schema.get("auth_config_details") or schema.get("authConfigDetails") or []
966
1045
  for item in details:
@@ -1014,8 +1093,11 @@ class ComposioBaseComponent(Component):
1014
1093
 
1015
1094
  def _clear_auth_dynamic_fields(self, build_config: dict) -> None:
1016
1095
  for fname in list(self._auth_dynamic_fields):
1017
- if fname in build_config:
1018
- build_config.pop(fname, None)
1096
+ if fname in build_config and isinstance(build_config[fname], dict):
1097
+ # Hide and reset instead of removing
1098
+ build_config[fname]["show"] = False
1099
+ build_config[fname]["value"] = ""
1100
+ build_config[fname]["required"] = False
1019
1101
  self._auth_dynamic_fields.clear()
1020
1102
 
1021
1103
  def _add_text_field(
@@ -1028,18 +1110,29 @@ class ComposioBaseComponent(Component):
1028
1110
  required: bool,
1029
1111
  default_value: str | None = None,
1030
1112
  ) -> None:
1031
- """Add a simple text input (StrInput) for custom auth forms, prefilled with schema defaults when available."""
1032
- field = StrInput(
1033
- name=name,
1034
- display_name=display_name or name.replace("_", " ").title(),
1035
- info=info or "",
1036
- required=required,
1037
- real_time_refresh=True,
1038
- show=True,
1039
- ).to_dict()
1040
- if default_value is not None and default_value != "":
1041
- field["value"] = default_value
1042
- build_config[name] = field
1113
+ """Update existing field or add new text input for custom auth forms."""
1114
+ # Check if field already exists in build_config (pre-defined placeholder)
1115
+ if name in build_config:
1116
+ # Update existing field properties
1117
+ build_config[name]["display_name"] = display_name or name.replace("_", " ").title()
1118
+ build_config[name]["info"] = info or ""
1119
+ build_config[name]["required"] = required
1120
+ build_config[name]["show"] = True
1121
+ if default_value is not None and default_value != "":
1122
+ build_config[name]["value"] = default_value
1123
+ else:
1124
+ # Create new field if not pre-defined
1125
+ field = StrInput(
1126
+ name=name,
1127
+ display_name=display_name or name.replace("_", " ").title(),
1128
+ info=info or "",
1129
+ required=required,
1130
+ real_time_refresh=True,
1131
+ show=True,
1132
+ ).to_dict()
1133
+ if default_value is not None and default_value != "":
1134
+ field["value"] = default_value
1135
+ build_config[name] = field
1043
1136
  self._auth_dynamic_fields.add(name)
1044
1137
 
1045
1138
  def _render_custom_auth_fields(self, build_config: dict, schema: dict[str, Any], mode: str) -> None:
@@ -1053,35 +1146,37 @@ class ComposioBaseComponent(Component):
1053
1146
  if not selected:
1054
1147
  return
1055
1148
  fields = selected.get("fields") or {}
1056
- # a) AuthConfigCreation required fields for OAUTH2 custom
1149
+
1150
+ # Helper function to process fields
1151
+ def process_fields(field_list: list, *, required: bool) -> None:
1152
+ for field in field_list:
1153
+ name = field.get("name")
1154
+ if not name:
1155
+ continue
1156
+ # Skip Access Token field (bearer_token)
1157
+ if name == "bearer_token":
1158
+ continue
1159
+ # Skip fields with default values for both required and optional fields
1160
+ default_val = field.get("default")
1161
+ if default_val is not None:
1162
+ continue
1163
+ disp = field.get("display_name") or field.get("displayName") or name
1164
+ desc = field.get("description")
1165
+ self._add_text_field(build_config, name, disp, desc, required=required, default_value=default_val)
1166
+
1167
+ # a) AuthConfigCreation fields (for custom OAuth2, etc.)
1057
1168
  creation = fields.get("auth_config_creation") or fields.get("authConfigCreation") or {}
1058
- for req in creation.get("required", []):
1059
- name = req.get("name")
1060
- if not name:
1061
- continue
1062
- disp = req.get("display_name") or req.get("displayName") or name
1063
- desc = req.get("description")
1064
- default_val = req.get("default")
1065
- self._add_text_field(build_config, name, disp, desc, required=True, default_value=default_val)
1066
- # Optional auth_config_creation fields intentionally not rendered
1067
- # b) ConnectedAccountInitiation fields for API_KEY mode
1169
+ # Process required fields
1170
+ process_fields(creation.get("required", []), required=True)
1171
+ # Process optional fields (excluding those with defaults and bearer_token)
1172
+ process_fields(creation.get("optional", []), required=False)
1173
+
1174
+ # b) ConnectedAccountInitiation fields (for API_KEY, etc.)
1068
1175
  initiation = fields.get("connected_account_initiation") or fields.get("connectedAccountInitiation") or {}
1069
- for req in initiation.get("required", []):
1070
- name = req.get("name")
1071
- if not name:
1072
- continue
1073
- disp = req.get("display_name") or req.get("displayName") or name
1074
- desc = req.get("description")
1075
- default_val = req.get("default")
1076
- self._add_text_field(build_config, name, disp, desc, required=True, default_value=default_val)
1077
- for opt in initiation.get("optional", []):
1078
- name = opt.get("name")
1079
- if not name:
1080
- continue
1081
- disp = opt.get("display_name") or opt.get("displayName") or name
1082
- desc = opt.get("description")
1083
- default_val = opt.get("default")
1084
- self._add_text_field(build_config, name, disp, desc, required=False, default_value=default_val)
1176
+ # Process required fields
1177
+ process_fields(initiation.get("required", []), required=True)
1178
+ # Process optional fields (excluding those with defaults)
1179
+ process_fields(initiation.get("optional", []), required=False)
1085
1180
 
1086
1181
  def _collect_all_auth_field_names(self, schema: dict[str, Any] | None) -> set[str]:
1087
1182
  names: set[str] = set()
@@ -1131,8 +1226,6 @@ class ComposioBaseComponent(Component):
1131
1226
 
1132
1227
  def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:
1133
1228
  """Update build config for auth and action selection."""
1134
- # Avoid normalizing legacy input_types here; rely on upstream fixes
1135
-
1136
1229
  # BULLETPROOF tool_mode checking - check all possible places where tool_mode could be stored
1137
1230
  instance_tool_mode = getattr(self, "tool_mode", False) if hasattr(self, "tool_mode") else False
1138
1231
 
@@ -1189,7 +1282,8 @@ class ComposioBaseComponent(Component):
1189
1282
  try:
1190
1283
  selected_mode = (build_config.get("auth_mode") or {}).get("value")
1191
1284
  managed = (schema or {}).get("composio_managed_auth_schemes") or []
1192
- if selected_mode and not (isinstance(managed, list) and selected_mode in managed):
1285
+ # Don't render custom fields if "Composio managed" is selected
1286
+ if selected_mode and selected_mode != "Composio managed":
1193
1287
  self._clear_auth_dynamic_fields(build_config)
1194
1288
  self._render_custom_auth_fields(build_config, schema or {}, selected_mode)
1195
1289
  except (TypeError, ValueError, AttributeError):
@@ -1335,9 +1429,13 @@ class ComposioBaseComponent(Component):
1335
1429
  build_config["create_auth_config"]["value"] = ""
1336
1430
  build_config["create_auth_config"]["helper_text"] = ""
1337
1431
  build_config["create_auth_config"]["options"] = ["create"]
1338
- if isinstance(managed, list) and mode in managed:
1339
- # Managed → no extra fields
1432
+ if mode == "Composio managed":
1433
+ # Composio managed → no extra fields needed
1340
1434
  pass
1435
+ elif isinstance(managed, list) and mode in managed:
1436
+ # This is a specific managed auth scheme (e.g., OAUTH2) but user can still choose custom
1437
+ # So we should render custom fields for this mode
1438
+ self._render_custom_auth_fields(build_config, schema, mode)
1341
1439
  else:
1342
1440
  # Custom → render only required fields based on the toolkit schema
1343
1441
  self._render_custom_auth_fields(build_config, schema, mode)
@@ -1373,7 +1471,10 @@ class ComposioBaseComponent(Component):
1373
1471
  mode = build_config["auth_mode"].get("value")
1374
1472
  # If no managed default exists (400 Default auth config), require mode selection
1375
1473
  managed = (schema or {}).get("composio_managed_auth_schemes") or []
1376
- if isinstance(managed, list) and "OAUTH2" in managed and (mode is None or mode == "OAUTH2"):
1474
+
1475
+ # Handle "Composio managed" mode explicitly
1476
+ if mode == "Composio managed":
1477
+ # Use Composio managed auth flow
1377
1478
  redirect_url, connection_id = self._initiate_connection(toolkit_slug)
1378
1479
  build_config["auth_link"]["value"] = redirect_url
1379
1480
  logger.info(f"New OAuth URL created for {toolkit_slug}: {redirect_url}")
@@ -1748,11 +1849,7 @@ class ComposioBaseComponent(Component):
1748
1849
  schema = self._get_toolkit_schema()
1749
1850
  mode = (build_config.get("auth_mode") or {}).get("value")
1750
1851
  managed = (schema or {}).get("composio_managed_auth_schemes") or []
1751
- if (
1752
- mode
1753
- and not (isinstance(managed, list) and mode in managed)
1754
- and not getattr(self, "_auth_dynamic_fields", set())
1755
- ):
1852
+ if mode and mode != "Composio managed" and not getattr(self, "_auth_dynamic_fields", set()):
1756
1853
  self._render_custom_auth_fields(build_config, schema or {}, mode)
1757
1854
  except (TypeError, ValueError, AttributeError):
1758
1855
  pass
lfx/cli/run.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import re
2
3
  import sys
3
4
  import tempfile
4
5
  from functools import partial
@@ -243,9 +244,12 @@ async def run(
243
244
  if "ModuleNotFoundError" in str(e) or "No module named" in str(e):
244
245
  logger.info("This appears to be a missing dependency issue")
245
246
  if "langchain" in str(e).lower():
246
- logger.info(
247
- "Missing LangChain dependency detected. Try: pip install langchain-<provider>",
248
- )
247
+ match = re.search(r"langchain_(.*)", str(e).lower())
248
+ if match:
249
+ module_name = match.group(1)
250
+ logger.info(
251
+ f"Missing LangChain dependency detected. Try: pip install langchain-{module_name}",
252
+ )
249
253
  elif "ImportError" in str(e):
250
254
  logger.info("This appears to be an import issue - check component dependencies")
251
255
  elif "AttributeError" in str(e):
@@ -179,6 +179,7 @@ class AgentComponent(ToolCallingAgentComponent):
179
179
 
180
180
  # Get memory data
181
181
  self.chat_history = await self.get_memory_data()
182
+ await logger.adebug(f"Retrieved {len(self.chat_history)} chat history messages")
182
183
  if isinstance(self.chat_history, Message):
183
184
  self.chat_history = [self.chat_history]
184
185