lfx-nightly 0.1.12.dev40__py3-none-any.whl → 0.1.12.dev41__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,200 @@ 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 - hidden by default
71
+ SecretStrInput(
72
+ name="client_id",
73
+ display_name="Client ID",
74
+ info="",
75
+ show=False,
76
+ value="",
77
+ required=False,
78
+ real_time_refresh=True,
79
+ ),
80
+ SecretStrInput(
81
+ name="client_secret",
82
+ display_name="Client Secret",
83
+ info="",
84
+ show=False,
85
+ value="",
86
+ required=False,
87
+ real_time_refresh=True,
88
+ ),
89
+ StrInput(
90
+ name="verification_token",
91
+ display_name="Verification Token",
92
+ info="",
93
+ show=False,
94
+ value="",
95
+ required=False,
96
+ real_time_refresh=True,
97
+ ),
98
+ StrInput(
99
+ name="redirect_uri",
100
+ display_name="Redirect URI",
101
+ info="",
102
+ show=False,
103
+ value="",
104
+ required=False,
105
+ real_time_refresh=True,
106
+ ),
107
+ StrInput(
108
+ name="authorization_url",
109
+ display_name="Authorization URL",
110
+ info="",
111
+ show=False,
112
+ value="",
113
+ required=False,
114
+ real_time_refresh=True,
115
+ ),
116
+ StrInput(
117
+ name="token_url",
118
+ display_name="Token URL",
119
+ info="",
120
+ show=False,
121
+ value="",
122
+ required=False,
123
+ real_time_refresh=True,
124
+ ),
125
+ # API Key auth fields
126
+ SecretStrInput(
127
+ name="api_key_field",
128
+ display_name="API Key",
129
+ info="",
130
+ show=False,
131
+ value="",
132
+ required=False,
133
+ real_time_refresh=True,
134
+ ),
135
+ SecretStrInput(
136
+ name="generic_api_key",
137
+ display_name="API Key",
138
+ info="",
139
+ show=False,
140
+ value="",
141
+ required=False,
142
+ real_time_refresh=True,
143
+ ),
144
+ SecretStrInput(
145
+ name="token",
146
+ display_name="Token",
147
+ info="",
148
+ show=False,
149
+ value="",
150
+ required=False,
151
+ real_time_refresh=True,
152
+ ),
153
+ SecretStrInput(
154
+ name="access_token",
155
+ display_name="Access Token",
156
+ info="",
157
+ show=False,
158
+ value="",
159
+ required=False,
160
+ real_time_refresh=True,
161
+ ),
162
+ SecretStrInput(
163
+ name="refresh_token",
164
+ display_name="Refresh Token",
165
+ info="",
166
+ show=False,
167
+ value="",
168
+ required=False,
169
+ real_time_refresh=True,
170
+ ),
171
+ # Basic Auth fields
172
+ StrInput(
173
+ name="username",
174
+ display_name="Username",
175
+ info="",
176
+ show=False,
177
+ value="",
178
+ required=False,
179
+ real_time_refresh=True,
180
+ ),
181
+ SecretStrInput(
182
+ name="password",
183
+ display_name="Password",
184
+ info="",
185
+ show=False,
186
+ value="",
187
+ required=False,
188
+ real_time_refresh=True,
189
+ ),
190
+ # Other common auth fields
191
+ StrInput(
192
+ name="domain",
193
+ display_name="Domain",
194
+ info="",
195
+ show=False,
196
+ value="",
197
+ required=False,
198
+ real_time_refresh=True,
199
+ ),
200
+ StrInput(
201
+ name="base_url",
202
+ display_name="Base URL",
203
+ info="",
204
+ show=False,
205
+ value="",
206
+ required=False,
207
+ real_time_refresh=True,
208
+ ),
209
+ SecretStrInput(
210
+ name="bearer_token",
211
+ display_name="Bearer Token",
212
+ info="",
213
+ show=False,
214
+ value="",
215
+ required=False,
216
+ real_time_refresh=True,
217
+ ),
218
+ SecretStrInput(
219
+ name="authorization_code",
220
+ display_name="Authorization Code",
221
+ info="",
222
+ show=False,
223
+ value="",
224
+ required=False,
225
+ real_time_refresh=True,
226
+ ),
227
+ StrInput(
228
+ name="scopes",
229
+ display_name="Scopes",
230
+ info="",
231
+ show=False,
232
+ value="",
233
+ required=False,
234
+ real_time_refresh=True,
235
+ ),
236
+ # Add more common auth fields
237
+ StrInput(
238
+ name="subdomain",
239
+ display_name="Subdomain",
240
+ info="",
241
+ show=False,
242
+ value="",
243
+ required=False,
244
+ real_time_refresh=True,
245
+ ),
246
+ StrInput(
247
+ name="instance_url",
248
+ display_name="Instance URL",
249
+ info="",
250
+ show=False,
251
+ value="",
252
+ required=False,
253
+ real_time_refresh=True,
254
+ ),
255
+ StrInput(
256
+ name="tenant_id",
257
+ display_name="Tenant ID",
258
+ info="",
259
+ show=False,
260
+ value="",
261
+ required=False,
262
+ real_time_refresh=True,
263
+ ),
70
264
  SortableListInput(
71
265
  name="action_button",
72
266
  display_name="Action",
@@ -87,6 +281,8 @@ class ComposioBaseComponent(Component):
87
281
  # Class-level caches
88
282
  _actions_cache: dict[str, dict[str, Any]] = {}
89
283
  _action_schema_cache: dict[str, dict[str, Any]] = {}
284
+ # Track all auth field names discovered across all toolkits
285
+ _all_auth_field_names: set[str] = set()
90
286
 
91
287
  outputs = [
92
288
  Output(name="dataFrame", display_name="DataFrame", method="as_dataframe"),
@@ -925,26 +1121,11 @@ class ComposioBaseComponent(Component):
925
1121
  return self._toolkit_schema
926
1122
  try:
927
1123
  composio = self._build_wrapper()
928
- # The SDK typically offers a retrieve by slug; if not present, try a few fallbacks
929
1124
  app_slug = getattr(self, "app_name", "").lower()
930
1125
  if not app_slug:
931
1126
  return None
932
- 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
1127
+ # Use the correct Composio SDK method
1128
+ schema = composio.toolkits.get(slug=app_slug)
948
1129
  self._toolkit_schema = self._to_plain_dict(schema)
949
1130
  except (AttributeError, ValueError, ConnectionError, TypeError) as e:
950
1131
  logger.debug(f"Could not retrieve toolkit schema for {getattr(self, 'app_name', '')}: {e}")
@@ -959,8 +1140,12 @@ class ComposioBaseComponent(Component):
959
1140
  modes: list[str] = []
960
1141
  # composio_managed_auth_schemes: list[str]
961
1142
  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)])
1143
+ has_managed_schemes = isinstance(managed, list) and len(managed) > 0
1144
+
1145
+ # Add "Composio_Managed" as first option if there are managed schemes
1146
+ if has_managed_schemes:
1147
+ modes.append("Composio_Managed")
1148
+
964
1149
  # auth_config_details: list with entries containing mode
965
1150
  details = schema.get("auth_config_details") or schema.get("authConfigDetails") or []
966
1151
  for item in details:
@@ -1012,10 +1197,56 @@ class ComposioBaseComponent(Component):
1012
1197
  except (TypeError, ValueError, AttributeError) as e:
1013
1198
  logger.debug(f"Failed to render auth_mode dropdown: {e}")
1014
1199
 
1200
+ def _insert_field_before_action_button(self, build_config: dict, field_name: str, field_data: dict) -> None:
1201
+ """Insert a field in the correct position (before action_button) in build_config."""
1202
+ # If field already exists, don't add it again
1203
+ if field_name in build_config:
1204
+ return
1205
+
1206
+ # If action_button doesn't exist, just add the field normally
1207
+ if "action_button" not in build_config:
1208
+ build_config[field_name] = field_data
1209
+ return
1210
+
1211
+ # Find all the keys we need to preserve order for
1212
+ keys_before_action = []
1213
+ keys_after_action = []
1214
+ found_action = False
1215
+
1216
+ for key in list(build_config.keys()):
1217
+ if key == "action_button":
1218
+ found_action = True
1219
+ keys_after_action.append(key)
1220
+ elif found_action:
1221
+ keys_after_action.append(key)
1222
+ else:
1223
+ keys_before_action.append(key)
1224
+
1225
+ # Create new ordered dict
1226
+ new_config = {}
1227
+
1228
+ # Add all fields before action_button
1229
+ for key in keys_before_action:
1230
+ new_config[key] = build_config[key]
1231
+
1232
+ # Add the new field
1233
+ new_config[field_name] = field_data
1234
+
1235
+ # Add action_button and all fields after it
1236
+ for key in keys_after_action:
1237
+ new_config[key] = build_config[key]
1238
+
1239
+ # Clear and update build_config to maintain reference
1240
+ build_config.clear()
1241
+ build_config.update(new_config)
1242
+
1015
1243
  def _clear_auth_dynamic_fields(self, build_config: dict) -> None:
1016
1244
  for fname in list(self._auth_dynamic_fields):
1017
- if fname in build_config:
1018
- build_config.pop(fname, None)
1245
+ if fname in build_config and isinstance(build_config[fname], dict):
1246
+ # Hide and reset instead of removing
1247
+ build_config[fname]["show"] = False
1248
+ build_config[fname]["value"] = ""
1249
+ build_config[fname]["required"] = False
1019
1250
  self._auth_dynamic_fields.clear()
1020
1251
 
1021
1252
  def _add_text_field(
@@ -1028,19 +1259,61 @@ class ComposioBaseComponent(Component):
1028
1259
  required: bool,
1029
1260
  default_value: str | None = None,
1030
1261
  ) -> 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
1262
+ """Update existing field or add new text input for custom auth forms."""
1263
+ # Check if field already exists in build_config (pre-defined placeholder)
1264
+ if name in build_config:
1265
+ # Update existing field properties
1266
+ build_config[name]["display_name"] = display_name or name.replace("_", " ").title()
1267
+ build_config[name]["info"] = info or ""
1268
+ build_config[name]["required"] = required
1269
+ build_config[name]["show"] = True
1270
+ if default_value is not None and default_value != "":
1271
+ build_config[name]["value"] = default_value
1272
+ else:
1273
+ # Create new field if it doesn't exist
1274
+ # Use SecretStrInput for sensitive fields
1275
+ sensitive_fields = {
1276
+ "client_id",
1277
+ "client_secret",
1278
+ "api_key",
1279
+ "api_key_field",
1280
+ "generic_api_key",
1281
+ "token",
1282
+ "access_token",
1283
+ "refresh_token",
1284
+ "password",
1285
+ "bearer_token",
1286
+ "authorization_code",
1287
+ }
1288
+
1289
+ if name in sensitive_fields:
1290
+ field = SecretStrInput(
1291
+ name=name,
1292
+ display_name=display_name or name.replace("_", " ").title(),
1293
+ info=info or "",
1294
+ required=required,
1295
+ real_time_refresh=True,
1296
+ show=True,
1297
+ ).to_dict()
1298
+ else:
1299
+ field = StrInput(
1300
+ name=name,
1301
+ display_name=display_name or name.replace("_", " ").title(),
1302
+ info=info or "",
1303
+ required=required,
1304
+ real_time_refresh=True,
1305
+ show=True,
1306
+ ).to_dict()
1307
+
1308
+ if default_value is not None and default_value != "":
1309
+ field["value"] = default_value
1310
+
1311
+ # Insert the field in the correct position (before action_button)
1312
+ self._insert_field_before_action_button(build_config, name, field)
1313
+
1043
1314
  self._auth_dynamic_fields.add(name)
1315
+ # Also add to class-level cache for better tracking
1316
+ self.__class__._all_auth_field_names.add(name)
1044
1317
 
1045
1318
  def _render_custom_auth_fields(self, build_config: dict, schema: dict[str, Any], mode: str) -> None:
1046
1319
  """Render fields for custom auth based on schema auth_config_details sections."""
@@ -1053,35 +1326,37 @@ class ComposioBaseComponent(Component):
1053
1326
  if not selected:
1054
1327
  return
1055
1328
  fields = selected.get("fields") or {}
1056
- # a) AuthConfigCreation required fields for OAUTH2 custom
1329
+
1330
+ # Helper function to process fields
1331
+ def process_fields(field_list: list, *, required: bool) -> None:
1332
+ for field in field_list:
1333
+ name = field.get("name")
1334
+ if not name:
1335
+ continue
1336
+ # Skip Access Token field (bearer_token)
1337
+ if name == "bearer_token":
1338
+ continue
1339
+ # Skip fields with default values for both required and optional fields
1340
+ default_val = field.get("default")
1341
+ if default_val is not None:
1342
+ continue
1343
+ disp = field.get("display_name") or field.get("displayName") or name
1344
+ desc = field.get("description")
1345
+ self._add_text_field(build_config, name, disp, desc, required=required, default_value=default_val)
1346
+
1347
+ # a) AuthConfigCreation fields (for custom OAuth2, etc.)
1057
1348
  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
1349
+ # Process required fields
1350
+ process_fields(creation.get("required", []), required=True)
1351
+ # Process optional fields (excluding those with defaults and bearer_token)
1352
+ process_fields(creation.get("optional", []), required=False)
1353
+
1354
+ # b) ConnectedAccountInitiation fields (for API_KEY, etc.)
1068
1355
  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)
1356
+ # Process required fields
1357
+ process_fields(initiation.get("required", []), required=True)
1358
+ # Process optional fields (excluding those with defaults)
1359
+ process_fields(initiation.get("optional", []), required=False)
1085
1360
 
1086
1361
  def _collect_all_auth_field_names(self, schema: dict[str, Any] | None) -> set[str]:
1087
1362
  names: set[str] = set()
@@ -1102,6 +1377,8 @@ class ComposioBaseComponent(Component):
1102
1377
  name = entry.get("name") if isinstance(entry, dict) else None
1103
1378
  if name:
1104
1379
  names.add(name)
1380
+ # Add to class-level cache for tracking all discovered auth fields
1381
+ self.__class__._all_auth_field_names.add(name)
1105
1382
  # Only use names discovered from the toolkit schema; do not add aliases
1106
1383
  return names
1107
1384
 
@@ -1189,9 +1466,11 @@ class ComposioBaseComponent(Component):
1189
1466
  try:
1190
1467
  selected_mode = (build_config.get("auth_mode") or {}).get("value")
1191
1468
  managed = (schema or {}).get("composio_managed_auth_schemes") or []
1192
- if selected_mode and not (isinstance(managed, list) and selected_mode in managed):
1469
+ # Don't render custom fields if "Composio_Managed" is selected
1470
+ if selected_mode and selected_mode != "Composio_Managed":
1193
1471
  self._clear_auth_dynamic_fields(build_config)
1194
1472
  self._render_custom_auth_fields(build_config, schema or {}, selected_mode)
1473
+ # Already reordered in _render_custom_auth_fields
1195
1474
  except (TypeError, ValueError, AttributeError):
1196
1475
  pass
1197
1476
 
@@ -1335,12 +1614,18 @@ class ComposioBaseComponent(Component):
1335
1614
  build_config["create_auth_config"]["value"] = ""
1336
1615
  build_config["create_auth_config"]["helper_text"] = ""
1337
1616
  build_config["create_auth_config"]["options"] = ["create"]
1338
- if isinstance(managed, list) and mode in managed:
1339
- # Managed → no extra fields
1617
+ if mode == "Composio_Managed":
1618
+ # Composio_Managed → no extra fields needed
1340
1619
  pass
1620
+ elif isinstance(managed, list) and mode in managed:
1621
+ # This is a specific managed auth scheme (e.g., OAUTH2) but user can still choose custom
1622
+ # So we should render custom fields for this mode
1623
+ self._render_custom_auth_fields(build_config, schema, mode)
1624
+ # Already reordered in _render_custom_auth_fields
1341
1625
  else:
1342
1626
  # Custom → render only required fields based on the toolkit schema
1343
1627
  self._render_custom_auth_fields(build_config, schema, mode)
1628
+ # Already reordered in _render_custom_auth_fields
1344
1629
  return self.update_input_types(build_config)
1345
1630
 
1346
1631
  # Handle connection initiation when tool mode is enabled
@@ -1357,6 +1642,37 @@ class ComposioBaseComponent(Component):
1357
1642
  build_config["auth_link"]["connection_id"] = connection_id
1358
1643
  build_config["action_button"]["helper_text"] = ""
1359
1644
  build_config["action_button"]["helper_text_metadata"] = {}
1645
+
1646
+ # Clear auth fields when connected
1647
+ schema = self._get_toolkit_schema()
1648
+ self._clear_auth_fields_from_schema(build_config, schema)
1649
+
1650
+ # Convert auth_mode to pill for connected state
1651
+ scheme, _ = self._get_connection_auth_info(connection_id)
1652
+ if scheme:
1653
+ build_config.setdefault("auth_mode", {})
1654
+ build_config["auth_mode"]["value"] = scheme
1655
+ build_config["auth_mode"]["options"] = [scheme]
1656
+ build_config["auth_mode"]["show"] = False
1657
+ try:
1658
+ pill = TabInput(
1659
+ name="auth_mode",
1660
+ display_name="Auth Mode",
1661
+ options=[scheme],
1662
+ value=scheme,
1663
+ ).to_dict()
1664
+ pill["show"] = True
1665
+ build_config["auth_mode"] = pill
1666
+ except (TypeError, ValueError, AttributeError):
1667
+ build_config["auth_mode"] = {
1668
+ "name": "auth_mode",
1669
+ "display_name": "Auth Mode",
1670
+ "type": "tab",
1671
+ "options": [scheme],
1672
+ "value": scheme,
1673
+ "show": True,
1674
+ }
1675
+
1360
1676
  logger.info(f"Using existing ACTIVE connection {connection_id} for {toolkit_slug}")
1361
1677
  return self.update_input_types(build_config)
1362
1678
 
@@ -1373,11 +1689,15 @@ class ComposioBaseComponent(Component):
1373
1689
  mode = build_config["auth_mode"].get("value")
1374
1690
  # If no managed default exists (400 Default auth config), require mode selection
1375
1691
  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"):
1692
+
1693
+ # Handle "Composio_Managed" mode explicitly
1694
+ if mode == "Composio_Managed":
1695
+ # Use Composio_Managed auth flow
1377
1696
  redirect_url, connection_id = self._initiate_connection(toolkit_slug)
1378
1697
  build_config["auth_link"]["value"] = redirect_url
1379
1698
  logger.info(f"New OAuth URL created for {toolkit_slug}: {redirect_url}")
1380
1699
  return self.update_input_types(build_config)
1700
+
1381
1701
  if not mode:
1382
1702
  build_config["auth_link"]["value"] = "connect"
1383
1703
  build_config["auth_link"]["auth_tooltip"] = "Select Auth Mode"
@@ -1585,6 +1905,33 @@ class ComposioBaseComponent(Component):
1585
1905
  self._clear_auth_fields_from_schema(build_config, schema)
1586
1906
  build_config["action_button"]["helper_text"] = ""
1587
1907
  build_config["action_button"]["helper_text_metadata"] = {}
1908
+
1909
+ # Convert auth_mode to pill for connected state
1910
+ if not redirect_url and mode: # API_KEY or similar direct connection
1911
+ build_config["auth_link"]["connection_id"] = connection_id
1912
+ build_config.setdefault("auth_mode", {})
1913
+ build_config["auth_mode"]["value"] = mode
1914
+ build_config["auth_mode"]["options"] = [mode]
1915
+ build_config["auth_mode"]["show"] = False
1916
+ try:
1917
+ pill = TabInput(
1918
+ name="auth_mode",
1919
+ display_name="Auth Mode",
1920
+ options=[mode],
1921
+ value=mode,
1922
+ ).to_dict()
1923
+ pill["show"] = True
1924
+ build_config["auth_mode"] = pill
1925
+ except (TypeError, ValueError, AttributeError):
1926
+ build_config["auth_mode"] = {
1927
+ "name": "auth_mode",
1928
+ "display_name": "Auth Mode",
1929
+ "type": "tab",
1930
+ "options": [mode],
1931
+ "value": mode,
1932
+ "show": True,
1933
+ }
1934
+
1588
1935
  return self.update_input_types(build_config)
1589
1936
  # Generic custom auth flow for any other mode (treat like API_KEY)
1590
1937
  ac = composio.auth_configs.create(
@@ -1619,6 +1966,36 @@ class ComposioBaseComponent(Component):
1619
1966
  else:
1620
1967
  build_config["auth_link"]["value"] = "validated"
1621
1968
  build_config["auth_link"]["auth_tooltip"] = "Disconnect"
1969
+ build_config["auth_link"]["connection_id"] = connection_id
1970
+
1971
+ # Clear auth fields when connected
1972
+ schema = self._get_toolkit_schema()
1973
+ self._clear_auth_fields_from_schema(build_config, schema)
1974
+
1975
+ # Convert auth_mode to pill for connected state
1976
+ if mode:
1977
+ build_config.setdefault("auth_mode", {})
1978
+ build_config["auth_mode"]["value"] = mode
1979
+ build_config["auth_mode"]["options"] = [mode]
1980
+ build_config["auth_mode"]["show"] = False
1981
+ try:
1982
+ pill = TabInput(
1983
+ name="auth_mode",
1984
+ display_name="Auth Mode",
1985
+ options=[mode],
1986
+ value=mode,
1987
+ ).to_dict()
1988
+ pill["show"] = True
1989
+ build_config["auth_mode"] = pill
1990
+ except (TypeError, ValueError, AttributeError):
1991
+ build_config["auth_mode"] = {
1992
+ "name": "auth_mode",
1993
+ "display_name": "Auth Mode",
1994
+ "type": "tab",
1995
+ "options": [mode],
1996
+ "value": mode,
1997
+ "show": True,
1998
+ }
1622
1999
  return self.update_input_types(build_config)
1623
2000
  except (ValueError, ConnectionError, TypeError) as e:
1624
2001
  logger.error(f"Error creating connection: {e}")
@@ -1744,18 +2121,22 @@ class ComposioBaseComponent(Component):
1744
2121
  build_config.setdefault("auth_link", {})
1745
2122
  build_config["auth_link"]["show"] = False
1746
2123
  build_config["auth_link"]["display_name"] = ""
1747
- try:
1748
- schema = self._get_toolkit_schema()
1749
- mode = (build_config.get("auth_mode") or {}).get("value")
1750
- 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
- ):
1756
- self._render_custom_auth_fields(build_config, schema or {}, mode)
1757
- except (TypeError, ValueError, AttributeError):
1758
- pass
2124
+
2125
+ # Only render auth fields if NOT already connected
2126
+ active_connection = self._find_active_connection_for_app(self.app_name)
2127
+ if not active_connection:
2128
+ try:
2129
+ schema = self._get_toolkit_schema()
2130
+ mode = (build_config.get("auth_mode") or {}).get("value")
2131
+ managed = (schema or {}).get("composio_managed_auth_schemes") or []
2132
+ if mode and mode != "Composio_Managed" and not getattr(self, "_auth_dynamic_fields", set()):
2133
+ self._render_custom_auth_fields(build_config, schema or {}, mode)
2134
+ # Already reordered in _render_custom_auth_fields
2135
+ except (TypeError, ValueError, AttributeError):
2136
+ pass
2137
+ else:
2138
+ # If connected, clear any auth fields that might be showing
2139
+ self._clear_auth_dynamic_fields(build_config)
1759
2140
  # Do NOT return here; allow auth flow to run in Tool Mode
1760
2141
 
1761
2142
  if field_name == "tool_mode":
@@ -1942,6 +2323,36 @@ class ComposioBaseComponent(Component):
1942
2323
  build_config["auth_link"]["auth_tooltip"] = "Disconnect"
1943
2324
  build_config["action_button"]["helper_text"] = ""
1944
2325
  build_config["action_button"]["helper_text_metadata"] = {}
2326
+
2327
+ # Clear auth fields when connected
2328
+ schema = self._get_toolkit_schema()
2329
+ self._clear_auth_fields_from_schema(build_config, schema)
2330
+
2331
+ # Convert auth_mode to pill for connected state
2332
+ scheme, _ = self._get_connection_auth_info(active_connection_id)
2333
+ if scheme:
2334
+ build_config.setdefault("auth_mode", {})
2335
+ build_config["auth_mode"]["value"] = scheme
2336
+ build_config["auth_mode"]["options"] = [scheme]
2337
+ build_config["auth_mode"]["show"] = False
2338
+ try:
2339
+ pill = TabInput(
2340
+ name="auth_mode",
2341
+ display_name="Auth Mode",
2342
+ options=[scheme],
2343
+ value=scheme,
2344
+ ).to_dict()
2345
+ pill["show"] = True
2346
+ build_config["auth_mode"] = pill
2347
+ except (TypeError, ValueError, AttributeError):
2348
+ build_config["auth_mode"] = {
2349
+ "name": "auth_mode",
2350
+ "display_name": "Auth Mode",
2351
+ "type": "tab",
2352
+ "options": [scheme],
2353
+ "value": scheme,
2354
+ "show": True,
2355
+ }
1945
2356
  elif stored_connection_id:
1946
2357
  status = self._check_connection_status_by_id(stored_connection_id)
1947
2358
  if status == "INITIATED":
@@ -2186,7 +2597,34 @@ class ComposioBaseComponent(Component):
2186
2597
  "auth_mode",
2187
2598
  "auth_mode_pill",
2188
2599
  "create_auth_config",
2600
+ # Pre-defined auth fields
2601
+ "client_id",
2602
+ "client_secret",
2603
+ "verification_token",
2604
+ "redirect_uri",
2605
+ "authorization_url",
2606
+ "token_url",
2607
+ "api_key_field",
2608
+ "generic_api_key",
2609
+ "token",
2610
+ "access_token",
2611
+ "refresh_token",
2612
+ "username",
2613
+ "password",
2614
+ "domain",
2615
+ "base_url",
2616
+ "bearer_token",
2617
+ "authorization_code",
2618
+ "scopes",
2619
+ "subdomain",
2620
+ "instance_url",
2621
+ "tenant_id",
2189
2622
  }
2623
+ # Add all dynamic auth fields to protected set
2624
+ protected.update(self._auth_dynamic_fields)
2625
+ # Also protect any auth fields discovered across all instances
2626
+ protected.update(self.__class__._all_auth_field_names)
2627
+
2190
2628
  for key, cfg in list(build_config.items()):
2191
2629
  if key in protected:
2192
2630
  continue