lfx-nightly 0.1.13.dev0__py3-none-any.whl → 0.2.0.dev26__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 (237) hide show
  1. lfx/_assets/component_index.json +1 -1
  2. lfx/base/agents/agent.py +121 -29
  3. lfx/base/agents/altk_base_agent.py +380 -0
  4. lfx/base/agents/altk_tool_wrappers.py +565 -0
  5. lfx/base/agents/events.py +103 -35
  6. lfx/base/agents/utils.py +15 -2
  7. lfx/base/composio/composio_base.py +183 -233
  8. lfx/base/data/base_file.py +88 -21
  9. lfx/base/data/storage_utils.py +192 -0
  10. lfx/base/data/utils.py +178 -14
  11. lfx/base/datastax/__init__.py +5 -0
  12. lfx/{components/vectorstores/astradb.py → base/datastax/astradb_base.py} +84 -473
  13. lfx/base/embeddings/embeddings_class.py +113 -0
  14. lfx/base/io/chat.py +5 -4
  15. lfx/base/mcp/util.py +101 -15
  16. lfx/base/models/groq_constants.py +74 -58
  17. lfx/base/models/groq_model_discovery.py +265 -0
  18. lfx/base/models/model.py +1 -1
  19. lfx/base/models/model_input_constants.py +74 -7
  20. lfx/base/models/model_utils.py +100 -0
  21. lfx/base/models/ollama_constants.py +3 -0
  22. lfx/base/models/openai_constants.py +7 -0
  23. lfx/base/models/watsonx_constants.py +36 -0
  24. lfx/base/tools/run_flow.py +601 -129
  25. lfx/cli/commands.py +7 -4
  26. lfx/cli/common.py +2 -2
  27. lfx/cli/run.py +1 -1
  28. lfx/cli/script_loader.py +53 -11
  29. lfx/components/Notion/create_page.py +1 -1
  30. lfx/components/Notion/list_database_properties.py +1 -1
  31. lfx/components/Notion/list_pages.py +1 -1
  32. lfx/components/Notion/list_users.py +1 -1
  33. lfx/components/Notion/page_content_viewer.py +1 -1
  34. lfx/components/Notion/search.py +1 -1
  35. lfx/components/Notion/update_page_property.py +1 -1
  36. lfx/components/__init__.py +19 -5
  37. lfx/components/altk/__init__.py +34 -0
  38. lfx/components/altk/altk_agent.py +193 -0
  39. lfx/components/amazon/amazon_bedrock_converse.py +1 -1
  40. lfx/components/apify/apify_actor.py +4 -4
  41. lfx/components/composio/__init__.py +70 -18
  42. lfx/components/composio/apollo_composio.py +11 -0
  43. lfx/components/composio/bitbucket_composio.py +11 -0
  44. lfx/components/composio/canva_composio.py +11 -0
  45. lfx/components/composio/coda_composio.py +11 -0
  46. lfx/components/composio/composio_api.py +10 -0
  47. lfx/components/composio/discord_composio.py +1 -1
  48. lfx/components/composio/elevenlabs_composio.py +11 -0
  49. lfx/components/composio/exa_composio.py +11 -0
  50. lfx/components/composio/firecrawl_composio.py +11 -0
  51. lfx/components/composio/fireflies_composio.py +11 -0
  52. lfx/components/composio/gmail_composio.py +1 -1
  53. lfx/components/composio/googlebigquery_composio.py +11 -0
  54. lfx/components/composio/googlecalendar_composio.py +1 -1
  55. lfx/components/composio/googledocs_composio.py +1 -1
  56. lfx/components/composio/googlemeet_composio.py +1 -1
  57. lfx/components/composio/googlesheets_composio.py +1 -1
  58. lfx/components/composio/googletasks_composio.py +1 -1
  59. lfx/components/composio/heygen_composio.py +11 -0
  60. lfx/components/composio/mem0_composio.py +11 -0
  61. lfx/components/composio/peopledatalabs_composio.py +11 -0
  62. lfx/components/composio/perplexityai_composio.py +11 -0
  63. lfx/components/composio/serpapi_composio.py +11 -0
  64. lfx/components/composio/slack_composio.py +3 -574
  65. lfx/components/composio/slackbot_composio.py +1 -1
  66. lfx/components/composio/snowflake_composio.py +11 -0
  67. lfx/components/composio/tavily_composio.py +11 -0
  68. lfx/components/composio/youtube_composio.py +2 -2
  69. lfx/components/{agents → cuga}/__init__.py +5 -7
  70. lfx/components/cuga/cuga_agent.py +730 -0
  71. lfx/components/data/__init__.py +78 -28
  72. lfx/components/data_source/__init__.py +58 -0
  73. lfx/components/{data → data_source}/api_request.py +26 -3
  74. lfx/components/{data → data_source}/csv_to_data.py +15 -10
  75. lfx/components/{data → data_source}/json_to_data.py +15 -8
  76. lfx/components/{data → data_source}/news_search.py +1 -1
  77. lfx/components/{data → data_source}/rss.py +1 -1
  78. lfx/components/{data → data_source}/sql_executor.py +1 -1
  79. lfx/components/{data → data_source}/url.py +1 -1
  80. lfx/components/{data → data_source}/web_search.py +1 -1
  81. lfx/components/datastax/__init__.py +12 -6
  82. lfx/components/datastax/{astra_assistant_manager.py → astradb_assistant_manager.py} +1 -0
  83. lfx/components/datastax/astradb_chatmemory.py +40 -0
  84. lfx/components/datastax/astradb_cql.py +6 -32
  85. lfx/components/datastax/astradb_graph.py +10 -124
  86. lfx/components/datastax/astradb_tool.py +13 -53
  87. lfx/components/datastax/astradb_vectorstore.py +134 -977
  88. lfx/components/datastax/create_assistant.py +1 -0
  89. lfx/components/datastax/create_thread.py +1 -0
  90. lfx/components/datastax/dotenv.py +1 -0
  91. lfx/components/datastax/get_assistant.py +1 -0
  92. lfx/components/datastax/getenvvar.py +1 -0
  93. lfx/components/datastax/graph_rag.py +1 -1
  94. lfx/components/datastax/hcd.py +1 -1
  95. lfx/components/datastax/list_assistants.py +1 -0
  96. lfx/components/datastax/run.py +1 -0
  97. lfx/components/deactivated/json_document_builder.py +1 -1
  98. lfx/components/elastic/elasticsearch.py +1 -1
  99. lfx/components/elastic/opensearch_multimodal.py +1575 -0
  100. lfx/components/files_and_knowledge/__init__.py +47 -0
  101. lfx/components/{data → files_and_knowledge}/directory.py +1 -1
  102. lfx/components/{data → files_and_knowledge}/file.py +246 -18
  103. lfx/components/{knowledge_bases → files_and_knowledge}/ingestion.py +17 -9
  104. lfx/components/{knowledge_bases → files_and_knowledge}/retrieval.py +18 -10
  105. lfx/components/{data → files_and_knowledge}/save_file.py +142 -22
  106. lfx/components/flow_controls/__init__.py +58 -0
  107. lfx/components/{logic → flow_controls}/conditional_router.py +1 -1
  108. lfx/components/{logic → flow_controls}/loop.py +47 -9
  109. lfx/components/flow_controls/run_flow.py +108 -0
  110. lfx/components/glean/glean_search_api.py +1 -1
  111. lfx/components/groq/groq.py +35 -28
  112. lfx/components/helpers/__init__.py +102 -0
  113. lfx/components/ibm/watsonx.py +25 -21
  114. lfx/components/input_output/__init__.py +3 -1
  115. lfx/components/input_output/chat.py +12 -3
  116. lfx/components/input_output/chat_output.py +12 -4
  117. lfx/components/input_output/text.py +1 -1
  118. lfx/components/input_output/text_output.py +1 -1
  119. lfx/components/{data → input_output}/webhook.py +1 -1
  120. lfx/components/knowledge_bases/__init__.py +59 -4
  121. lfx/components/langchain_utilities/character.py +1 -1
  122. lfx/components/langchain_utilities/csv_agent.py +84 -16
  123. lfx/components/langchain_utilities/json_agent.py +67 -12
  124. lfx/components/langchain_utilities/language_recursive.py +1 -1
  125. lfx/components/llm_operations/__init__.py +46 -0
  126. lfx/components/{processing → llm_operations}/batch_run.py +1 -1
  127. lfx/components/{processing → llm_operations}/lambda_filter.py +1 -1
  128. lfx/components/{logic → llm_operations}/llm_conditional_router.py +1 -1
  129. lfx/components/{processing/llm_router.py → llm_operations/llm_selector.py} +3 -3
  130. lfx/components/{processing → llm_operations}/structured_output.py +56 -18
  131. lfx/components/logic/__init__.py +126 -0
  132. lfx/components/mem0/mem0_chat_memory.py +11 -0
  133. lfx/components/mistral/mistral_embeddings.py +1 -1
  134. lfx/components/models/__init__.py +64 -9
  135. lfx/components/models_and_agents/__init__.py +49 -0
  136. lfx/components/{agents → models_and_agents}/agent.py +49 -6
  137. lfx/components/models_and_agents/embedding_model.py +423 -0
  138. lfx/components/models_and_agents/language_model.py +398 -0
  139. lfx/components/{agents → models_and_agents}/mcp_component.py +84 -45
  140. lfx/components/{helpers → models_and_agents}/memory.py +1 -1
  141. lfx/components/nvidia/system_assist.py +1 -1
  142. lfx/components/olivya/olivya.py +1 -1
  143. lfx/components/ollama/ollama.py +235 -14
  144. lfx/components/openrouter/openrouter.py +49 -147
  145. lfx/components/processing/__init__.py +9 -57
  146. lfx/components/processing/converter.py +1 -1
  147. lfx/components/processing/dataframe_operations.py +1 -1
  148. lfx/components/processing/parse_json_data.py +2 -2
  149. lfx/components/processing/parser.py +7 -2
  150. lfx/components/processing/split_text.py +1 -1
  151. lfx/components/qdrant/qdrant.py +1 -1
  152. lfx/components/redis/redis.py +1 -1
  153. lfx/components/twelvelabs/split_video.py +10 -0
  154. lfx/components/twelvelabs/video_file.py +12 -0
  155. lfx/components/utilities/__init__.py +43 -0
  156. lfx/components/{helpers → utilities}/calculator_core.py +1 -1
  157. lfx/components/{helpers → utilities}/current_date.py +1 -1
  158. lfx/components/{processing → utilities}/python_repl_core.py +1 -1
  159. lfx/components/vectorstores/__init__.py +0 -6
  160. lfx/components/vectorstores/local_db.py +9 -0
  161. lfx/components/youtube/youtube_transcripts.py +118 -30
  162. lfx/custom/custom_component/component.py +60 -3
  163. lfx/custom/custom_component/custom_component.py +68 -6
  164. lfx/field_typing/constants.py +1 -0
  165. lfx/graph/edge/base.py +45 -22
  166. lfx/graph/graph/base.py +5 -2
  167. lfx/graph/graph/schema.py +3 -2
  168. lfx/graph/state/model.py +15 -2
  169. lfx/graph/utils.py +6 -0
  170. lfx/graph/vertex/base.py +4 -1
  171. lfx/graph/vertex/param_handler.py +10 -7
  172. lfx/graph/vertex/vertex_types.py +1 -1
  173. lfx/helpers/__init__.py +12 -0
  174. lfx/helpers/flow.py +117 -0
  175. lfx/inputs/input_mixin.py +24 -1
  176. lfx/inputs/inputs.py +13 -1
  177. lfx/interface/components.py +161 -83
  178. lfx/io/schema.py +6 -0
  179. lfx/log/logger.py +5 -3
  180. lfx/schema/schema.py +5 -0
  181. lfx/services/database/__init__.py +5 -0
  182. lfx/services/database/service.py +25 -0
  183. lfx/services/deps.py +87 -22
  184. lfx/services/manager.py +19 -6
  185. lfx/services/mcp_composer/service.py +998 -157
  186. lfx/services/session.py +5 -0
  187. lfx/services/settings/base.py +51 -7
  188. lfx/services/settings/constants.py +8 -0
  189. lfx/services/storage/local.py +76 -46
  190. lfx/services/storage/service.py +152 -29
  191. lfx/template/field/base.py +3 -0
  192. lfx/utils/ssrf_protection.py +384 -0
  193. lfx/utils/validate_cloud.py +26 -0
  194. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev26.dist-info}/METADATA +38 -22
  195. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev26.dist-info}/RECORD +210 -196
  196. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev26.dist-info}/WHEEL +1 -1
  197. lfx/components/agents/cuga_agent.py +0 -1013
  198. lfx/components/datastax/astra_db.py +0 -77
  199. lfx/components/datastax/cassandra.py +0 -92
  200. lfx/components/logic/run_flow.py +0 -71
  201. lfx/components/models/embedding_model.py +0 -114
  202. lfx/components/models/language_model.py +0 -144
  203. lfx/components/vectorstores/astradb_graph.py +0 -326
  204. lfx/components/vectorstores/cassandra.py +0 -264
  205. lfx/components/vectorstores/cassandra_graph.py +0 -238
  206. lfx/components/vectorstores/chroma.py +0 -167
  207. lfx/components/vectorstores/clickhouse.py +0 -135
  208. lfx/components/vectorstores/couchbase.py +0 -102
  209. lfx/components/vectorstores/elasticsearch.py +0 -267
  210. lfx/components/vectorstores/faiss.py +0 -111
  211. lfx/components/vectorstores/graph_rag.py +0 -141
  212. lfx/components/vectorstores/hcd.py +0 -314
  213. lfx/components/vectorstores/milvus.py +0 -115
  214. lfx/components/vectorstores/mongodb_atlas.py +0 -213
  215. lfx/components/vectorstores/opensearch.py +0 -243
  216. lfx/components/vectorstores/pgvector.py +0 -72
  217. lfx/components/vectorstores/pinecone.py +0 -134
  218. lfx/components/vectorstores/qdrant.py +0 -109
  219. lfx/components/vectorstores/supabase.py +0 -76
  220. lfx/components/vectorstores/upstash.py +0 -124
  221. lfx/components/vectorstores/vectara.py +0 -97
  222. lfx/components/vectorstores/vectara_rag.py +0 -164
  223. lfx/components/vectorstores/weaviate.py +0 -89
  224. /lfx/components/{data → data_source}/mock_data.py +0 -0
  225. /lfx/components/datastax/{astra_vectorize.py → astradb_vectorize.py} +0 -0
  226. /lfx/components/{logic → flow_controls}/data_conditional_router.py +0 -0
  227. /lfx/components/{logic → flow_controls}/flow_tool.py +0 -0
  228. /lfx/components/{logic → flow_controls}/listen.py +0 -0
  229. /lfx/components/{logic → flow_controls}/notify.py +0 -0
  230. /lfx/components/{logic → flow_controls}/pass_message.py +0 -0
  231. /lfx/components/{logic → flow_controls}/sub_flow.py +0 -0
  232. /lfx/components/{processing → models_and_agents}/prompt.py +0 -0
  233. /lfx/components/{helpers → processing}/create_list.py +0 -0
  234. /lfx/components/{helpers → processing}/output_parser.py +0 -0
  235. /lfx/components/{helpers → processing}/store_message.py +0 -0
  236. /lfx/components/{helpers → utilities}/id_generator.py +0 -0
  237. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev26.dist-info}/entry_points.txt +0 -0
@@ -28,6 +28,12 @@ from lfx.log.logger import logger
28
28
  from lfx.schema.data import Data
29
29
  from lfx.schema.dataframe import DataFrame
30
30
  from lfx.schema.message import Message
31
+ from lfx.utils.validate_cloud import raise_error_if_astra_cloud_disable_component
32
+
33
+ disable_component_in_astra_cloud_msg = (
34
+ "Composio tools are not supported in Astra cloud environment. "
35
+ "Please use local storage mode or cloud-based versions of the tools."
36
+ )
31
37
 
32
38
 
33
39
  class ComposioBaseComponent(Component):
@@ -135,7 +141,7 @@ class ComposioBaseComponent(Component):
135
141
  SecretStrInput(
136
142
  name="generic_api_key",
137
143
  display_name="API Key",
138
- info="",
144
+ info="Enter API key on Composio page",
139
145
  show=False,
140
146
  value="",
141
147
  required=False,
@@ -284,6 +290,21 @@ class ComposioBaseComponent(Component):
284
290
  # Track all auth field names discovered across all toolkits
285
291
  _all_auth_field_names: set[str] = set()
286
292
 
293
+ @classmethod
294
+ def get_actions_cache(cls) -> dict[str, dict[str, Any]]:
295
+ """Get the class-level actions cache."""
296
+ return cls._actions_cache
297
+
298
+ @classmethod
299
+ def get_action_schema_cache(cls) -> dict[str, dict[str, Any]]:
300
+ """Get the class-level action schema cache."""
301
+ return cls._action_schema_cache
302
+
303
+ @classmethod
304
+ def get_all_auth_field_names(cls) -> set[str]:
305
+ """Get all auth field names discovered across toolkits."""
306
+ return cls._all_auth_field_names
307
+
287
308
  outputs = [
288
309
  Output(name="dataFrame", display_name="DataFrame", method="as_dataframe"),
289
310
  ]
@@ -313,6 +334,8 @@ class ComposioBaseComponent(Component):
313
334
  return Message(text=str(result))
314
335
 
315
336
  def as_dataframe(self) -> DataFrame:
337
+ # Check if we're in Astra cloud environment and raise an error if we are.
338
+ raise_error_if_astra_cloud_disable_component(disable_component_in_astra_cloud_msg)
316
339
  result = self.execute_action()
317
340
 
318
341
  if isinstance(result, dict):
@@ -356,6 +379,8 @@ class ComposioBaseComponent(Component):
356
379
 
357
380
  def _build_wrapper(self) -> Composio:
358
381
  """Build the Composio wrapper."""
382
+ # Check if we're in Astra cloud environment and raise an error if we are.
383
+ raise_error_if_astra_cloud_disable_component(disable_component_in_astra_cloud_msg)
359
384
  try:
360
385
  if not self.api_key:
361
386
  msg = "Composio API Key is required"
@@ -403,11 +428,11 @@ class ComposioBaseComponent(Component):
403
428
 
404
429
  # Try to load from the class-level cache
405
430
  toolkit_slug = self.app_name.lower()
406
- if toolkit_slug in self.__class__._actions_cache:
431
+ if toolkit_slug in self.__class__.get_actions_cache():
407
432
  # Deep-copy so that any mutation on this instance does not affect the
408
433
  # cached master copy.
409
- self._actions_data = copy.deepcopy(self.__class__._actions_cache[toolkit_slug])
410
- self._action_schemas = copy.deepcopy(self.__class__._action_schema_cache.get(toolkit_slug, {}))
434
+ self._actions_data = copy.deepcopy(self.__class__.get_actions_cache()[toolkit_slug])
435
+ self._action_schemas = copy.deepcopy(self.__class__.get_action_schema_cache().get(toolkit_slug, {}))
411
436
  logger.debug(f"Loaded actions for {toolkit_slug} from in-process cache")
412
437
  return
413
438
 
@@ -455,11 +480,17 @@ class ComposioBaseComponent(Component):
455
480
  if parameters_schema is None:
456
481
  logger.warning(f"Parameters schema is None for action key: {action_key}")
457
482
  # Still add the action but with empty fields
483
+ # Extract version information from the tool
484
+ version = tool_dict.get("version")
485
+ available_versions = tool_dict.get("available_versions", [])
486
+
458
487
  self._action_schemas[action_key] = tool_dict
459
488
  self._actions_data[action_key] = {
460
489
  "display_name": display_name,
461
490
  "action_fields": [],
462
491
  "file_upload_fields": set(),
492
+ "version": version,
493
+ "available_versions": available_versions,
463
494
  }
464
495
  continue
465
496
 
@@ -473,11 +504,17 @@ class ComposioBaseComponent(Component):
473
504
  parameters_schema = parameters_schema.__dict__
474
505
  else:
475
506
  logger.warning(f"Cannot process parameters schema for {action_key}, skipping")
507
+ # Extract version information from the tool
508
+ version = tool_dict.get("version")
509
+ available_versions = tool_dict.get("available_versions", [])
510
+
476
511
  self._action_schemas[action_key] = tool_dict
477
512
  self._actions_data[action_key] = {
478
513
  "display_name": display_name,
479
514
  "action_fields": [],
480
515
  "file_upload_fields": set(),
516
+ "version": version,
517
+ "available_versions": available_versions,
481
518
  }
482
519
  continue
483
520
 
@@ -516,22 +553,34 @@ class ComposioBaseComponent(Component):
516
553
  elif field_name in original_descriptions:
517
554
  field_schema["description"] = original_descriptions[field_name]
518
555
  except (KeyError, TypeError, ValueError):
556
+ # Extract version information from the tool
557
+ version = tool_dict.get("version")
558
+ available_versions = tool_dict.get("available_versions", [])
559
+
519
560
  self._action_schemas[action_key] = tool_dict
520
561
  self._actions_data[action_key] = {
521
562
  "display_name": display_name,
522
563
  "action_fields": [],
523
564
  "file_upload_fields": set(),
565
+ "version": version,
566
+ "available_versions": available_versions,
524
567
  }
525
568
  continue
526
569
 
527
570
  if flat_schema is None:
528
571
  logger.warning(f"Flat schema is None for action key: {action_key}")
529
572
  # Still add the action but with empty fields so the UI doesn't break
573
+ # Extract version information from the tool
574
+ version = tool_dict.get("version")
575
+ available_versions = tool_dict.get("available_versions", [])
576
+
530
577
  self._action_schemas[action_key] = tool_dict
531
578
  self._actions_data[action_key] = {
532
579
  "display_name": display_name,
533
580
  "action_fields": [],
534
581
  "file_upload_fields": set(),
582
+ "version": version,
583
+ "available_versions": available_versions,
535
584
  }
536
585
  continue
537
586
 
@@ -604,20 +653,32 @@ class ComposioBaseComponent(Component):
604
653
  clean_field_name = p_name.replace("[0]", "")
605
654
  self._bool_variables.add(clean_field_name)
606
655
 
656
+ # Extract version information from the tool
657
+ version = tool_dict.get("version")
658
+ available_versions = tool_dict.get("available_versions", [])
659
+
607
660
  self._action_schemas[action_key] = tool_dict
608
661
  self._actions_data[action_key] = {
609
662
  "display_name": display_name,
610
663
  "action_fields": action_fields,
611
664
  "file_upload_fields": file_upload_fields,
665
+ "version": version,
666
+ "available_versions": available_versions,
612
667
  }
613
668
 
614
669
  except (KeyError, TypeError, ValueError) as flatten_error:
615
670
  logger.error(f"flatten_schema failed for {action_key}: {flatten_error}")
671
+ # Extract version information from the tool
672
+ version = tool_dict.get("version")
673
+ available_versions = tool_dict.get("available_versions", [])
674
+
616
675
  self._action_schemas[action_key] = tool_dict
617
676
  self._actions_data[action_key] = {
618
677
  "display_name": display_name,
619
678
  "action_fields": [],
620
679
  "file_upload_fields": set(),
680
+ "version": version,
681
+ "available_versions": available_versions,
621
682
  }
622
683
  continue
623
684
 
@@ -630,8 +691,8 @@ class ComposioBaseComponent(Component):
630
691
 
631
692
  # Cache actions for this toolkit so subsequent component instances
632
693
  # can reuse them without hitting the Composio API again.
633
- self.__class__._actions_cache[toolkit_slug] = copy.deepcopy(self._actions_data)
634
- self.__class__._action_schema_cache[toolkit_slug] = copy.deepcopy(self._action_schemas)
694
+ self.__class__.get_actions_cache()[toolkit_slug] = copy.deepcopy(self._actions_data)
695
+ self.__class__.get_action_schema_cache()[toolkit_slug] = copy.deepcopy(self._action_schemas)
635
696
 
636
697
  except ValueError as e:
637
698
  logger.debug(f"Could not populate Composio actions for {self.app_name}: {e}")
@@ -672,6 +733,9 @@ class ComposioBaseComponent(Component):
672
733
  parameters_schema = parameters_schema.copy() # Don't modify the original
673
734
  parameters_schema["required"] = []
674
735
 
736
+ # Also get top-level required fields from original schema
737
+ original_required = set(parameters_schema.get("required", []))
738
+
675
739
  try:
676
740
  # Preserve original descriptions before flattening to restore if lost
677
741
  original_descriptions = {}
@@ -895,6 +959,8 @@ class ComposioBaseComponent(Component):
895
959
  if any(getattr(i, "name", None) == top_name for i in processed_inputs):
896
960
  continue
897
961
  top_schema = props_dict.get(top_name, {})
962
+ # For MultilineInput fields (complex JSON objects/arrays)
963
+ is_required = top_name in original_required
898
964
  processed_inputs.append(
899
965
  MultilineInput(
900
966
  name=top_name,
@@ -902,7 +968,7 @@ class ComposioBaseComponent(Component):
902
968
  info=(
903
969
  top_schema.get("description") or "Provide JSON for this parameter (object or array)."
904
970
  ),
905
- required=top_name in required_fields_set,
971
+ required=is_required, # Setting original schema
906
972
  )
907
973
  )
908
974
 
@@ -992,24 +1058,14 @@ class ComposioBaseComponent(Component):
992
1058
  return auth_config.id
993
1059
 
994
1060
  def _initiate_connection(self, app_name: str) -> tuple[str, str]:
995
- """Initiate OAuth connection and return (redirect_url, connection_id)."""
1061
+ """Initiate connection using link method and return (redirect_url, connection_id)."""
996
1062
  try:
997
1063
  composio = self._build_wrapper()
998
1064
 
999
- auth_configs = composio.auth_configs.list(toolkit_slug=app_name)
1000
- if len(auth_configs.items) == 0:
1001
- auth_config_id = self.create_new_auth_config(app_name)
1002
- else:
1003
- auth_config_id = None
1004
- for auth_config in auth_configs.items:
1005
- if auth_config.auth_scheme == "OAUTH2":
1006
- auth_config_id = auth_config.id
1007
-
1008
- auth_config_id = auth_configs.items[0].id
1065
+ # Always create a new auth config (previous behavior)
1066
+ auth_config_id = self.create_new_auth_config(app_name)
1009
1067
 
1010
- connection_request = composio.connected_accounts.initiate(
1011
- user_id=self.entity_id, auth_config_id=auth_config_id
1012
- )
1068
+ connection_request = composio.connected_accounts.link(user_id=self.entity_id, auth_config_id=auth_config_id)
1013
1069
 
1014
1070
  redirect_url = getattr(connection_request, "redirect_url", None)
1015
1071
  connection_id = getattr(connection_request, "id", None)
@@ -1022,12 +1078,12 @@ class ComposioBaseComponent(Component):
1022
1078
  msg = "No connection ID received from Composio"
1023
1079
  raise ValueError(msg)
1024
1080
 
1025
- logger.info(f"OAuth connection initiated for {app_name}: {redirect_url} (ID: {connection_id})")
1081
+ logger.info(f"Connection initiated for {app_name}: {redirect_url} (ID: {connection_id})")
1026
1082
  return redirect_url, connection_id # noqa: TRY300
1027
1083
 
1028
1084
  except (ValueError, ConnectionError, TypeError, AttributeError) as e:
1029
1085
  logger.error(f"Error initiating connection for {app_name}: {e}")
1030
- msg = f"Failed to initiate OAuth connection: {e}"
1086
+ msg = f"Failed to initiate connection: {e}"
1031
1087
  raise ValueError(msg) from e
1032
1088
 
1033
1089
  def _check_connection_status_by_id(self, connection_id: str) -> str | None:
@@ -1313,7 +1369,7 @@ class ComposioBaseComponent(Component):
1313
1369
 
1314
1370
  self._auth_dynamic_fields.add(name)
1315
1371
  # Also add to class-level cache for better tracking
1316
- self.__class__._all_auth_field_names.add(name)
1372
+ self.__class__.get_all_auth_field_names().add(name)
1317
1373
 
1318
1374
  def _render_custom_auth_fields(self, build_config: dict, schema: dict[str, Any], mode: str) -> None:
1319
1375
  """Render fields for custom auth based on schema auth_config_details sections."""
@@ -1344,20 +1400,14 @@ class ComposioBaseComponent(Component):
1344
1400
  desc = field.get("description")
1345
1401
  self._add_text_field(build_config, name, disp, desc, required=required, default_value=default_val)
1346
1402
 
1347
- # a) AuthConfigCreation fields (for custom OAuth2, etc.)
1403
+ # Only process AuthConfigCreation fields (for custom OAuth2, etc.)
1404
+ # Connection initiation fields are now handled on Composio page via link method
1348
1405
  creation = fields.get("auth_config_creation") or fields.get("authConfigCreation") or {}
1349
1406
  # Process required fields
1350
1407
  process_fields(creation.get("required", []), required=True)
1351
1408
  # Process optional fields (excluding those with defaults and bearer_token)
1352
1409
  process_fields(creation.get("optional", []), required=False)
1353
1410
 
1354
- # b) ConnectedAccountInitiation fields (for API_KEY, etc.)
1355
- initiation = fields.get("connected_account_initiation") or fields.get("connectedAccountInitiation") or {}
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)
1360
-
1361
1411
  def _collect_all_auth_field_names(self, schema: dict[str, Any] | None) -> set[str]:
1362
1412
  names: set[str] = set()
1363
1413
  if not schema:
@@ -1378,7 +1428,7 @@ class ComposioBaseComponent(Component):
1378
1428
  if name:
1379
1429
  names.add(name)
1380
1430
  # Add to class-level cache for tracking all discovered auth fields
1381
- self.__class__._all_auth_field_names.add(name)
1431
+ self.__class__.get_all_auth_field_names().add(name)
1382
1432
  # Only use names discovered from the toolkit schema; do not add aliases
1383
1433
  return names
1384
1434
 
@@ -1443,7 +1493,7 @@ class ComposioBaseComponent(Component):
1443
1493
  # Check if we need to populate actions - but also check cache availability
1444
1494
  actions_available = bool(self._actions_data)
1445
1495
  toolkit_slug = getattr(self, "app_name", "").lower()
1446
- cached_actions_available = toolkit_slug in self.__class__._actions_cache
1496
+ cached_actions_available = toolkit_slug in self.__class__.get_actions_cache()
1447
1497
 
1448
1498
  should_populate = False
1449
1499
 
@@ -1467,10 +1517,15 @@ class ComposioBaseComponent(Component):
1467
1517
  selected_mode = (build_config.get("auth_mode") or {}).get("value")
1468
1518
  managed = (schema or {}).get("composio_managed_auth_schemes") or []
1469
1519
  # Don't render custom fields if "Composio_Managed" is selected
1470
- if selected_mode and selected_mode != "Composio_Managed":
1520
+ # For API_KEY and other token modes, no fields are needed as they use link method
1521
+ token_modes = ["API_KEY", "BEARER_TOKEN", "BASIC"]
1522
+ if selected_mode and selected_mode not in ["Composio_Managed", *token_modes]:
1471
1523
  self._clear_auth_dynamic_fields(build_config)
1472
1524
  self._render_custom_auth_fields(build_config, schema or {}, selected_mode)
1473
1525
  # Already reordered in _render_custom_auth_fields
1526
+ elif selected_mode in token_modes:
1527
+ # Clear any existing auth fields for token-based modes
1528
+ self._clear_auth_dynamic_fields(build_config)
1474
1529
  except (TypeError, ValueError, AttributeError):
1475
1530
  pass
1476
1531
 
@@ -1617,6 +1672,9 @@ class ComposioBaseComponent(Component):
1617
1672
  if mode == "Composio_Managed":
1618
1673
  # Composio_Managed → no extra fields needed
1619
1674
  pass
1675
+ elif mode in ["API_KEY", "BEARER_TOKEN", "BASIC"]:
1676
+ # Token-based modes → no fields needed, user enters on Composio page via link
1677
+ pass
1620
1678
  elif isinstance(managed, list) and mode in managed:
1621
1679
  # This is a specific managed auth scheme (e.g., OAUTH2) but user can still choose custom
1622
1680
  # So we should render custom fields for this mode
@@ -1681,6 +1739,13 @@ class ComposioBaseComponent(Component):
1681
1739
 
1682
1740
  # Create new connection ONLY if we truly have no usable connection yet
1683
1741
  if existing_active is None:
1742
+ # Check if we already have a redirect URL in progress
1743
+ current_auth_link_value = build_config.get("auth_link", {}).get("value", "")
1744
+ if current_auth_link_value and current_auth_link_value.startswith(("http://", "https://")):
1745
+ # We already have a redirect URL, don't create a new one
1746
+ logger.info(f"Redirect URL already exists for {toolkit_slug}, skipping new creation")
1747
+ return self.update_input_types(build_config)
1748
+
1684
1749
  try:
1685
1750
  # Determine auth mode
1686
1751
  schema = self._get_toolkit_schema()
@@ -1703,7 +1768,7 @@ class ComposioBaseComponent(Component):
1703
1768
  build_config["auth_link"]["auth_tooltip"] = "Select Auth Mode"
1704
1769
  return self.update_input_types(build_config)
1705
1770
  # Custom modes: create auth config and/or initiate with config
1706
- # Validate required fields before creating any auth config
1771
+ # Only validate auth_config_creation fields for OAUTH2
1707
1772
  required_missing = []
1708
1773
  if mode == "OAUTH2":
1709
1774
  req_names_pre = self._get_schema_field_names(
@@ -1717,30 +1782,6 @@ class ComposioBaseComponent(Component):
1717
1782
  val = build_config[fname].get("value")
1718
1783
  if val in (None, ""):
1719
1784
  required_missing.append(fname)
1720
- elif mode == "API_KEY":
1721
- req_names_pre = self._get_schema_field_names(
1722
- schema,
1723
- "API_KEY",
1724
- "connected_account_initiation",
1725
- "required",
1726
- )
1727
- for fname in req_names_pre:
1728
- if fname in build_config:
1729
- val = build_config[fname].get("value")
1730
- if val in (None, ""):
1731
- required_missing.append(fname)
1732
- else:
1733
- req_names_pre = self._get_schema_field_names(
1734
- schema,
1735
- mode,
1736
- "connected_account_initiation",
1737
- "required",
1738
- )
1739
- for fname in req_names_pre:
1740
- if fname in build_config:
1741
- val = build_config[fname].get("value")
1742
- if val in (None, ""):
1743
- required_missing.append(fname)
1744
1785
  if required_missing:
1745
1786
  # Surface errors on each missing field
1746
1787
  for fname in required_missing:
@@ -1764,24 +1805,18 @@ class ComposioBaseComponent(Component):
1764
1805
  # If an auth_config was already created via the button, use it and include initiation fields
1765
1806
  stored_ac_id = (build_config.get("auth_link") or {}).get("auth_config_id")
1766
1807
  if stored_ac_id:
1767
- # Build val from schema-declared connected_account_initiation required + rendered fields
1768
- val_payload = {}
1769
- init_req = self._get_schema_field_names(
1770
- schema,
1771
- "OAUTH2",
1772
- "connected_account_initiation",
1773
- "required",
1774
- )
1775
- candidate_names = set(self._auth_dynamic_fields) | init_req
1776
- for fname in candidate_names:
1777
- if fname in build_config:
1778
- v = build_config[fname].get("value")
1779
- if v not in (None, ""):
1780
- val_payload[fname] = v
1781
- redirect = composio.connected_accounts.initiate(
1808
+ # Check if we already have a redirect URL to prevent duplicates
1809
+ current_link_value = build_config.get("auth_link", {}).get("value", "")
1810
+ if current_link_value and current_link_value.startswith(("http://", "https://")):
1811
+ logger.info(
1812
+ f"Redirect URL already exists for {toolkit_slug} OAUTH2, skipping new creation"
1813
+ )
1814
+ return self.update_input_types(build_config)
1815
+
1816
+ # Use link method - no need to collect connection initiation fields
1817
+ redirect = composio.connected_accounts.link(
1782
1818
  user_id=self.entity_id,
1783
1819
  auth_config_id=stored_ac_id,
1784
- config={"auth_scheme": "OAUTH2", "val": val_payload} if val_payload else None,
1785
1820
  )
1786
1821
  redirect_url = getattr(redirect, "redirect_url", None)
1787
1822
  connection_id = getattr(redirect, "id", None)
@@ -1792,6 +1827,9 @@ class ComposioBaseComponent(Component):
1792
1827
  # Clear action blocker text on successful initiation
1793
1828
  build_config["action_button"]["helper_text"] = ""
1794
1829
  build_config["action_button"]["helper_text_metadata"] = {}
1830
+ # Clear any auth fields
1831
+ schema = self._get_toolkit_schema()
1832
+ self._clear_auth_fields_from_schema(build_config, schema)
1795
1833
  return self.update_input_types(build_config)
1796
1834
  # Otherwise, create custom OAuth2 auth config using schema-declared required fields
1797
1835
  credentials = {}
@@ -1812,6 +1850,14 @@ class ComposioBaseComponent(Component):
1812
1850
  else:
1813
1851
  missing.append(fname)
1814
1852
  # proceed even if missing optional; backend will validate
1853
+ # Check if we already have a redirect URL to prevent duplicates
1854
+ current_link_value = build_config.get("auth_link", {}).get("value", "")
1855
+ if current_link_value and current_link_value.startswith(("http://", "https://")):
1856
+ logger.info(
1857
+ f"Redirect URL already exists for {toolkit_slug} OAUTH2, skipping new creation"
1858
+ )
1859
+ return self.update_input_types(build_config)
1860
+
1815
1861
  ac = composio.auth_configs.create(
1816
1862
  toolkit=toolkit_slug,
1817
1863
  options={
@@ -1821,30 +1867,8 @@ class ComposioBaseComponent(Component):
1821
1867
  },
1822
1868
  )
1823
1869
  auth_config_id = getattr(ac, "id", None)
1824
- # If the schema declares initiation required fields, render them and defer initiation
1825
- init_req = self._get_schema_field_names(
1826
- schema,
1827
- "OAUTH2",
1828
- "connected_account_initiation",
1829
- "required",
1830
- )
1831
- if init_req:
1832
- self._clear_auth_dynamic_fields(build_config)
1833
- for name in init_req:
1834
- self._add_text_field(
1835
- build_config,
1836
- name=name,
1837
- display_name=name.replace("_", " ").title(),
1838
- info="Provide connection parameter",
1839
- required=True,
1840
- )
1841
- build_config.setdefault("auth_link", {})
1842
- build_config["auth_link"]["auth_config_id"] = auth_config_id
1843
- build_config["auth_link"]["value"] = "connect"
1844
- build_config["auth_link"]["auth_tooltip"] = "Connect"
1845
- return self.update_input_types(build_config)
1846
- # Otherwise initiate immediately
1847
- redirect = composio.connected_accounts.initiate(
1870
+ # Use link method directly - no need to check for connection initiation fields
1871
+ redirect = composio.connected_accounts.link(
1848
1872
  user_id=self.entity_id,
1849
1873
  auth_config_id=auth_config_id,
1850
1874
  )
@@ -1861,141 +1885,64 @@ class ComposioBaseComponent(Component):
1861
1885
  build_config["action_button"]["helper_text_metadata"] = {}
1862
1886
  return self.update_input_types(build_config)
1863
1887
  if mode == "API_KEY":
1888
+ # Check if we already have a redirect URL to prevent duplicates
1889
+ current_link_value = build_config.get("auth_link", {}).get("value", "")
1890
+ if current_link_value and current_link_value.startswith(("http://", "https://")):
1891
+ logger.info(
1892
+ f"Redirect URL already exists for {toolkit_slug} API_KEY, skipping new creation"
1893
+ )
1894
+ return self.update_input_types(build_config)
1895
+
1864
1896
  ac = composio.auth_configs.create(
1865
1897
  toolkit=toolkit_slug,
1866
1898
  options={"type": "use_custom_auth", "auth_scheme": "API_KEY", "credentials": {}},
1867
1899
  )
1868
1900
  auth_config_id = getattr(ac, "id", None)
1869
- # Build initiation config.val from schema-declared required names and dynamic fields
1870
- val_payload = {}
1871
- missing = []
1872
- # Collect required names from schema
1873
- req_names = self._get_schema_field_names(
1874
- schema,
1875
- "API_KEY",
1876
- "connected_account_initiation",
1877
- "required",
1878
- )
1879
- # Merge rendered dynamic fields and schema-required names
1880
- candidate_names = set(self._auth_dynamic_fields) | req_names
1881
- for fname in candidate_names:
1882
- if fname in build_config:
1883
- val = build_config[fname].get("value")
1884
- if val not in (None, ""):
1885
- val_payload[fname] = val
1886
- else:
1887
- missing.append(fname)
1888
- initiation = composio.connected_accounts.initiate(
1901
+ # Use link method - user will enter API key on Composio page
1902
+ initiation = composio.connected_accounts.link(
1889
1903
  user_id=self.entity_id,
1890
1904
  auth_config_id=auth_config_id,
1891
- config={"auth_scheme": "API_KEY", "val": val_payload},
1892
1905
  )
1893
1906
  connection_id = getattr(initiation, "id", None)
1894
1907
  redirect_url = getattr(initiation, "redirect_url", None)
1895
- # Do not store connection_id on initiation; only when ACTIVE
1908
+ # API_KEY now also returns redirect URL with new link method
1896
1909
  if redirect_url:
1897
1910
  build_config["auth_link"]["value"] = redirect_url
1898
1911
  build_config["auth_link"]["auth_tooltip"] = "Disconnect"
1899
- else:
1900
- # No redirect for API_KEY; mark as connected
1901
- build_config["auth_link"]["value"] = "validated"
1902
- build_config["auth_link"]["auth_tooltip"] = "Disconnect"
1903
- # In both cases, hide auth fields immediately after successful initiation
1912
+ # Hide auth fields immediately after successful initiation
1904
1913
  schema = self._get_toolkit_schema()
1905
1914
  self._clear_auth_fields_from_schema(build_config, schema)
1906
1915
  build_config["action_button"]["helper_text"] = ""
1907
1916
  build_config["action_button"]["helper_text_metadata"] = {}
1908
1917
 
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
-
1935
1918
  return self.update_input_types(build_config)
1936
1919
  # Generic custom auth flow for any other mode (treat like API_KEY)
1920
+ # Check if we already have a redirect URL to prevent duplicates
1921
+ current_link_value = build_config.get("auth_link", {}).get("value", "")
1922
+ if current_link_value and current_link_value.startswith(("http://", "https://")):
1923
+ logger.info(f"Redirect URL already exists for {toolkit_slug} {mode}, skipping new creation")
1924
+ return self.update_input_types(build_config)
1925
+
1937
1926
  ac = composio.auth_configs.create(
1938
1927
  toolkit=toolkit_slug,
1939
1928
  options={"type": "use_custom_auth", "auth_scheme": mode, "credentials": {}},
1940
1929
  )
1941
1930
  auth_config_id = getattr(ac, "id", None)
1942
- val_payload = {}
1943
- req_names = self._get_schema_field_names(
1944
- schema,
1945
- mode,
1946
- "connected_account_initiation",
1947
- "required",
1948
- )
1949
- candidate_names = set(self._auth_dynamic_fields) | req_names
1950
- for fname in candidate_names:
1951
- if fname in build_config:
1952
- val = build_config[fname].get("value")
1953
- if val not in (None, ""):
1954
- val_payload[fname] = val
1955
- initiation = composio.connected_accounts.initiate(
1931
+ # Use link method - user will enter required fields on Composio page
1932
+ initiation = composio.connected_accounts.link(
1956
1933
  user_id=self.entity_id,
1957
1934
  auth_config_id=auth_config_id,
1958
- config={"auth_scheme": mode, "val": val_payload},
1959
1935
  )
1960
1936
  connection_id = getattr(initiation, "id", None)
1961
1937
  redirect_url = getattr(initiation, "redirect_url", None)
1962
- # Do not store connection_id on initiation; only when ACTIVE
1963
1938
  if redirect_url:
1964
1939
  build_config["auth_link"]["value"] = redirect_url
1965
1940
  build_config["auth_link"]["auth_tooltip"] = "Disconnect"
1966
- else:
1967
- build_config["auth_link"]["value"] = "validated"
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
- }
1941
+ # Clear auth fields
1942
+ schema = self._get_toolkit_schema()
1943
+ self._clear_auth_fields_from_schema(build_config, schema)
1944
+ build_config["action_button"]["helper_text"] = ""
1945
+ build_config["action_button"]["helper_text_metadata"] = {}
1999
1946
  return self.update_input_types(build_config)
2000
1947
  except (ValueError, ConnectionError, TypeError) as e:
2001
1948
  logger.error(f"Error creating connection: {e}")
@@ -2129,7 +2076,12 @@ class ComposioBaseComponent(Component):
2129
2076
  schema = self._get_toolkit_schema()
2130
2077
  mode = (build_config.get("auth_mode") or {}).get("value")
2131
2078
  managed = (schema or {}).get("composio_managed_auth_schemes") or []
2132
- if mode and mode != "Composio_Managed" and not getattr(self, "_auth_dynamic_fields", set()):
2079
+ token_modes = ["API_KEY", "BEARER_TOKEN", "BASIC"]
2080
+ if (
2081
+ mode
2082
+ and mode not in ["Composio_Managed", *token_modes]
2083
+ and not getattr(self, "_auth_dynamic_fields", set())
2084
+ ):
2133
2085
  self._render_custom_auth_fields(build_config, schema or {}, mode)
2134
2086
  # Already reordered in _render_custom_auth_fields
2135
2087
  except (TypeError, ValueError, AttributeError):
@@ -2174,6 +2126,12 @@ class ComposioBaseComponent(Component):
2174
2126
  # Handle auth config button click
2175
2127
  if field_name == "create_auth_config" and field_value == "create":
2176
2128
  try:
2129
+ # Check if we already have a redirect URL to prevent duplicates
2130
+ current_link_value = build_config.get("auth_link", {}).get("value", "")
2131
+ if current_link_value and current_link_value.startswith(("http://", "https://")):
2132
+ logger.info("Redirect URL already exists, skipping new auth config creation")
2133
+ return self.update_input_types(build_config)
2134
+
2177
2135
  composio = self._build_wrapper()
2178
2136
  toolkit_slug = self.app_name.lower()
2179
2137
  schema = self._get_toolkit_schema() or {}
@@ -2194,29 +2152,8 @@ class ComposioBaseComponent(Component):
2194
2152
  auth_config_id = getattr(ac, "id", None)
2195
2153
  build_config.setdefault("auth_link", {})
2196
2154
  if auth_config_id:
2197
- # Check if there are connection initiation required fields
2198
- initiation_required = self._get_schema_field_names(
2199
- schema, "OAUTH2", "connected_account_initiation", "required"
2200
- )
2201
- if initiation_required:
2202
- # Populate those fields dynamically for the user to fill
2203
- self._clear_auth_dynamic_fields(build_config)
2204
- for name in initiation_required:
2205
- # Render as text inputs to collect connection fields
2206
- self._add_text_field(
2207
- build_config,
2208
- name=name,
2209
- display_name=name.replace("_", " ").title(),
2210
- info="Provide connection parameter",
2211
- required=True,
2212
- )
2213
- # Store the new auth_config_id so pressing Connect will use it
2214
- build_config["auth_link"]["auth_config_id"] = auth_config_id
2215
- build_config["auth_link"]["value"] = "connect"
2216
- build_config["auth_link"]["auth_tooltip"] = "Connect"
2217
- return self.update_input_types(build_config)
2218
- # If no initiation fields required, initiate immediately
2219
- connection_request = composio.connected_accounts.initiate(
2155
+ # Use link method directly - no need to check for connection initiation fields
2156
+ connection_request = composio.connected_accounts.link(
2220
2157
  user_id=self.entity_id, auth_config_id=auth_config_id
2221
2158
  )
2222
2159
  redirect_url = getattr(connection_request, "redirect_url", None)
@@ -2423,6 +2360,8 @@ class ComposioBaseComponent(Component):
2423
2360
 
2424
2361
  def execute_action(self):
2425
2362
  """Execute the selected Composio tool."""
2363
+ # Check if we're in Astra cloud environment and raise an error if we are.
2364
+ raise_error_if_astra_cloud_disable_component(disable_component_in_astra_cloud_msg)
2426
2365
  composio = self._build_wrapper()
2427
2366
  self._populate_actions_data()
2428
2367
  self._build_action_maps()
@@ -2492,12 +2431,23 @@ class ComposioBaseComponent(Component):
2492
2431
 
2493
2432
  arguments[final_field_name] = value
2494
2433
 
2495
- # Execute using new SDK
2496
- result = composio.tools.execute(
2497
- slug=action_key,
2498
- arguments=arguments,
2499
- user_id=self.entity_id,
2500
- )
2434
+ # Get the version from the action data
2435
+ version = self._actions_data.get(action_key, {}).get("version")
2436
+ if version:
2437
+ logger.info(f"Executing {action_key} with version: {version}")
2438
+
2439
+ # Execute using new SDK with version parameter
2440
+ execute_params = {
2441
+ "slug": action_key,
2442
+ "arguments": arguments,
2443
+ "user_id": self.entity_id,
2444
+ }
2445
+
2446
+ # Only add version if it's available
2447
+ if version:
2448
+ execute_params["version"] = version
2449
+
2450
+ result = composio.tools.execute(**execute_params)
2501
2451
 
2502
2452
  if isinstance(result, dict) and "successful" in result:
2503
2453
  if result["successful"]:
@@ -2623,7 +2573,7 @@ class ComposioBaseComponent(Component):
2623
2573
  # Add all dynamic auth fields to protected set
2624
2574
  protected.update(self._auth_dynamic_fields)
2625
2575
  # Also protect any auth fields discovered across all instances
2626
- protected.update(self.__class__._all_auth_field_names)
2576
+ protected.update(self.__class__.get_all_auth_field_names())
2627
2577
 
2628
2578
  for key, cfg in list(build_config.items()):
2629
2579
  if key in protected: