lfx-nightly 0.2.0.dev0__py3-none-any.whl → 0.2.0.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.
- lfx/_assets/component_index.json +1 -1
- lfx/base/agents/agent.py +21 -4
- lfx/base/agents/altk_base_agent.py +393 -0
- lfx/base/agents/altk_tool_wrappers.py +565 -0
- lfx/base/agents/events.py +2 -1
- lfx/base/composio/composio_base.py +159 -224
- lfx/base/data/base_file.py +97 -20
- lfx/base/data/docling_utils.py +61 -10
- lfx/base/data/storage_utils.py +301 -0
- lfx/base/data/utils.py +178 -14
- lfx/base/mcp/util.py +2 -2
- lfx/base/models/anthropic_constants.py +21 -12
- lfx/base/models/groq_constants.py +74 -58
- lfx/base/models/groq_model_discovery.py +265 -0
- lfx/base/models/model.py +1 -1
- lfx/base/models/model_utils.py +100 -0
- lfx/base/models/openai_constants.py +7 -0
- lfx/base/models/watsonx_constants.py +32 -8
- lfx/base/tools/run_flow.py +601 -129
- lfx/cli/commands.py +9 -4
- lfx/cli/common.py +2 -2
- lfx/cli/run.py +1 -1
- lfx/cli/script_loader.py +53 -11
- lfx/components/Notion/create_page.py +1 -1
- lfx/components/Notion/list_database_properties.py +1 -1
- lfx/components/Notion/list_pages.py +1 -1
- lfx/components/Notion/list_users.py +1 -1
- lfx/components/Notion/page_content_viewer.py +1 -1
- lfx/components/Notion/search.py +1 -1
- lfx/components/Notion/update_page_property.py +1 -1
- lfx/components/__init__.py +19 -5
- lfx/components/{agents → altk}/__init__.py +5 -9
- lfx/components/altk/altk_agent.py +193 -0
- lfx/components/apify/apify_actor.py +1 -1
- lfx/components/composio/__init__.py +70 -18
- lfx/components/composio/apollo_composio.py +11 -0
- lfx/components/composio/bitbucket_composio.py +11 -0
- lfx/components/composio/canva_composio.py +11 -0
- lfx/components/composio/coda_composio.py +11 -0
- lfx/components/composio/composio_api.py +10 -0
- lfx/components/composio/discord_composio.py +1 -1
- lfx/components/composio/elevenlabs_composio.py +11 -0
- lfx/components/composio/exa_composio.py +11 -0
- lfx/components/composio/firecrawl_composio.py +11 -0
- lfx/components/composio/fireflies_composio.py +11 -0
- lfx/components/composio/gmail_composio.py +1 -1
- lfx/components/composio/googlebigquery_composio.py +11 -0
- lfx/components/composio/googlecalendar_composio.py +1 -1
- lfx/components/composio/googledocs_composio.py +1 -1
- lfx/components/composio/googlemeet_composio.py +1 -1
- lfx/components/composio/googlesheets_composio.py +1 -1
- lfx/components/composio/googletasks_composio.py +1 -1
- lfx/components/composio/heygen_composio.py +11 -0
- lfx/components/composio/mem0_composio.py +11 -0
- lfx/components/composio/peopledatalabs_composio.py +11 -0
- lfx/components/composio/perplexityai_composio.py +11 -0
- lfx/components/composio/serpapi_composio.py +11 -0
- lfx/components/composio/slack_composio.py +3 -574
- lfx/components/composio/slackbot_composio.py +1 -1
- lfx/components/composio/snowflake_composio.py +11 -0
- lfx/components/composio/tavily_composio.py +11 -0
- lfx/components/composio/youtube_composio.py +2 -2
- lfx/components/cuga/__init__.py +34 -0
- lfx/components/cuga/cuga_agent.py +730 -0
- lfx/components/data/__init__.py +78 -28
- lfx/components/data_source/__init__.py +58 -0
- lfx/components/{data → data_source}/api_request.py +26 -3
- lfx/components/{data → data_source}/csv_to_data.py +15 -10
- lfx/components/{data → data_source}/json_to_data.py +15 -8
- lfx/components/{data → data_source}/news_search.py +1 -1
- lfx/components/{data → data_source}/rss.py +1 -1
- lfx/components/{data → data_source}/sql_executor.py +1 -1
- lfx/components/{data → data_source}/url.py +1 -1
- lfx/components/{data → data_source}/web_search.py +1 -1
- lfx/components/datastax/astradb_cql.py +1 -1
- lfx/components/datastax/astradb_graph.py +1 -1
- lfx/components/datastax/astradb_tool.py +1 -1
- lfx/components/datastax/astradb_vectorstore.py +1 -1
- lfx/components/datastax/hcd.py +1 -1
- lfx/components/deactivated/json_document_builder.py +1 -1
- lfx/components/docling/__init__.py +0 -3
- lfx/components/docling/chunk_docling_document.py +3 -1
- lfx/components/docling/export_docling_document.py +3 -1
- lfx/components/elastic/elasticsearch.py +1 -1
- lfx/components/files_and_knowledge/__init__.py +47 -0
- lfx/components/{data → files_and_knowledge}/directory.py +1 -1
- lfx/components/{data → files_and_knowledge}/file.py +304 -24
- lfx/components/{knowledge_bases → files_and_knowledge}/retrieval.py +2 -2
- lfx/components/{data → files_and_knowledge}/save_file.py +218 -31
- lfx/components/flow_controls/__init__.py +58 -0
- lfx/components/{logic → flow_controls}/conditional_router.py +1 -1
- lfx/components/{logic → flow_controls}/loop.py +43 -9
- lfx/components/flow_controls/run_flow.py +108 -0
- lfx/components/glean/glean_search_api.py +1 -1
- lfx/components/groq/groq.py +35 -28
- lfx/components/helpers/__init__.py +102 -0
- lfx/components/ibm/watsonx.py +7 -1
- lfx/components/input_output/__init__.py +3 -1
- lfx/components/input_output/chat.py +4 -3
- lfx/components/input_output/chat_output.py +10 -4
- lfx/components/input_output/text.py +1 -1
- lfx/components/input_output/text_output.py +1 -1
- lfx/components/{data → input_output}/webhook.py +1 -1
- lfx/components/knowledge_bases/__init__.py +59 -4
- lfx/components/langchain_utilities/character.py +1 -1
- lfx/components/langchain_utilities/csv_agent.py +84 -16
- lfx/components/langchain_utilities/json_agent.py +67 -12
- lfx/components/langchain_utilities/language_recursive.py +1 -1
- lfx/components/llm_operations/__init__.py +46 -0
- lfx/components/{processing → llm_operations}/batch_run.py +17 -8
- lfx/components/{processing → llm_operations}/lambda_filter.py +1 -1
- lfx/components/{logic → llm_operations}/llm_conditional_router.py +1 -1
- lfx/components/{processing/llm_router.py → llm_operations/llm_selector.py} +3 -3
- lfx/components/{processing → llm_operations}/structured_output.py +1 -1
- lfx/components/logic/__init__.py +126 -0
- lfx/components/mem0/mem0_chat_memory.py +11 -0
- lfx/components/models/__init__.py +64 -9
- lfx/components/models_and_agents/__init__.py +49 -0
- lfx/components/{agents → models_and_agents}/agent.py +6 -4
- lfx/components/models_and_agents/embedding_model.py +353 -0
- lfx/components/models_and_agents/language_model.py +398 -0
- lfx/components/{agents → models_and_agents}/mcp_component.py +53 -44
- lfx/components/{helpers → models_and_agents}/memory.py +1 -1
- lfx/components/nvidia/system_assist.py +1 -1
- lfx/components/olivya/olivya.py +1 -1
- lfx/components/ollama/ollama.py +24 -5
- lfx/components/processing/__init__.py +9 -60
- lfx/components/processing/converter.py +1 -1
- lfx/components/processing/dataframe_operations.py +1 -1
- lfx/components/processing/parse_json_data.py +2 -2
- lfx/components/processing/parser.py +1 -1
- lfx/components/processing/split_text.py +1 -1
- lfx/components/qdrant/qdrant.py +1 -1
- lfx/components/redis/redis.py +1 -1
- lfx/components/twelvelabs/split_video.py +10 -0
- lfx/components/twelvelabs/video_file.py +12 -0
- lfx/components/utilities/__init__.py +43 -0
- lfx/components/{helpers → utilities}/calculator_core.py +1 -1
- lfx/components/{helpers → utilities}/current_date.py +1 -1
- lfx/components/{processing → utilities}/python_repl_core.py +1 -1
- lfx/components/vectorstores/local_db.py +9 -0
- lfx/components/youtube/youtube_transcripts.py +118 -30
- lfx/custom/custom_component/component.py +57 -1
- lfx/custom/custom_component/custom_component.py +68 -6
- lfx/custom/directory_reader/directory_reader.py +5 -2
- lfx/graph/edge/base.py +43 -20
- lfx/graph/state/model.py +15 -2
- lfx/graph/utils.py +6 -0
- lfx/graph/vertex/param_handler.py +10 -7
- lfx/helpers/__init__.py +12 -0
- lfx/helpers/flow.py +117 -0
- lfx/inputs/input_mixin.py +24 -1
- lfx/inputs/inputs.py +13 -1
- lfx/interface/components.py +161 -83
- lfx/log/logger.py +5 -3
- lfx/schema/image.py +2 -12
- lfx/services/database/__init__.py +5 -0
- lfx/services/database/service.py +25 -0
- lfx/services/deps.py +87 -22
- lfx/services/interfaces.py +5 -0
- lfx/services/manager.py +24 -10
- lfx/services/mcp_composer/service.py +1029 -162
- lfx/services/session.py +5 -0
- lfx/services/settings/auth.py +18 -11
- lfx/services/settings/base.py +56 -30
- lfx/services/settings/constants.py +8 -0
- lfx/services/storage/local.py +108 -46
- lfx/services/storage/service.py +171 -29
- lfx/template/field/base.py +3 -0
- lfx/utils/image.py +29 -11
- lfx/utils/ssrf_protection.py +384 -0
- lfx/utils/validate_cloud.py +26 -0
- {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/METADATA +38 -22
- {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/RECORD +189 -160
- {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.dist-info}/WHEEL +1 -1
- lfx/components/agents/altk_agent.py +0 -366
- lfx/components/agents/cuga_agent.py +0 -1013
- lfx/components/docling/docling_remote_vlm.py +0 -284
- lfx/components/logic/run_flow.py +0 -71
- lfx/components/models/embedding_model.py +0 -195
- lfx/components/models/language_model.py +0 -144
- lfx/components/processing/dataframe_to_toolset.py +0 -259
- /lfx/components/{data → data_source}/mock_data.py +0 -0
- /lfx/components/{knowledge_bases → files_and_knowledge}/ingestion.py +0 -0
- /lfx/components/{logic → flow_controls}/data_conditional_router.py +0 -0
- /lfx/components/{logic → flow_controls}/flow_tool.py +0 -0
- /lfx/components/{logic → flow_controls}/listen.py +0 -0
- /lfx/components/{logic → flow_controls}/notify.py +0 -0
- /lfx/components/{logic → flow_controls}/pass_message.py +0 -0
- /lfx/components/{logic → flow_controls}/sub_flow.py +0 -0
- /lfx/components/{processing → models_and_agents}/prompt.py +0 -0
- /lfx/components/{helpers → processing}/create_list.py +0 -0
- /lfx/components/{helpers → processing}/output_parser.py +0 -0
- /lfx/components/{helpers → processing}/store_message.py +0 -0
- /lfx/components/{helpers → utilities}/id_generator.py +0 -0
- {lfx_nightly-0.2.0.dev0.dist-info → lfx_nightly-0.2.0.dev41.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,
|
|
@@ -328,6 +334,8 @@ class ComposioBaseComponent(Component):
|
|
|
328
334
|
return Message(text=str(result))
|
|
329
335
|
|
|
330
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)
|
|
331
339
|
result = self.execute_action()
|
|
332
340
|
|
|
333
341
|
if isinstance(result, dict):
|
|
@@ -371,6 +379,8 @@ class ComposioBaseComponent(Component):
|
|
|
371
379
|
|
|
372
380
|
def _build_wrapper(self) -> Composio:
|
|
373
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)
|
|
374
384
|
try:
|
|
375
385
|
if not self.api_key:
|
|
376
386
|
msg = "Composio API Key is required"
|
|
@@ -470,11 +480,17 @@ class ComposioBaseComponent(Component):
|
|
|
470
480
|
if parameters_schema is None:
|
|
471
481
|
logger.warning(f"Parameters schema is None for action key: {action_key}")
|
|
472
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
|
+
|
|
473
487
|
self._action_schemas[action_key] = tool_dict
|
|
474
488
|
self._actions_data[action_key] = {
|
|
475
489
|
"display_name": display_name,
|
|
476
490
|
"action_fields": [],
|
|
477
491
|
"file_upload_fields": set(),
|
|
492
|
+
"version": version,
|
|
493
|
+
"available_versions": available_versions,
|
|
478
494
|
}
|
|
479
495
|
continue
|
|
480
496
|
|
|
@@ -488,11 +504,17 @@ class ComposioBaseComponent(Component):
|
|
|
488
504
|
parameters_schema = parameters_schema.__dict__
|
|
489
505
|
else:
|
|
490
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
|
+
|
|
491
511
|
self._action_schemas[action_key] = tool_dict
|
|
492
512
|
self._actions_data[action_key] = {
|
|
493
513
|
"display_name": display_name,
|
|
494
514
|
"action_fields": [],
|
|
495
515
|
"file_upload_fields": set(),
|
|
516
|
+
"version": version,
|
|
517
|
+
"available_versions": available_versions,
|
|
496
518
|
}
|
|
497
519
|
continue
|
|
498
520
|
|
|
@@ -531,22 +553,34 @@ class ComposioBaseComponent(Component):
|
|
|
531
553
|
elif field_name in original_descriptions:
|
|
532
554
|
field_schema["description"] = original_descriptions[field_name]
|
|
533
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
|
+
|
|
534
560
|
self._action_schemas[action_key] = tool_dict
|
|
535
561
|
self._actions_data[action_key] = {
|
|
536
562
|
"display_name": display_name,
|
|
537
563
|
"action_fields": [],
|
|
538
564
|
"file_upload_fields": set(),
|
|
565
|
+
"version": version,
|
|
566
|
+
"available_versions": available_versions,
|
|
539
567
|
}
|
|
540
568
|
continue
|
|
541
569
|
|
|
542
570
|
if flat_schema is None:
|
|
543
571
|
logger.warning(f"Flat schema is None for action key: {action_key}")
|
|
544
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
|
+
|
|
545
577
|
self._action_schemas[action_key] = tool_dict
|
|
546
578
|
self._actions_data[action_key] = {
|
|
547
579
|
"display_name": display_name,
|
|
548
580
|
"action_fields": [],
|
|
549
581
|
"file_upload_fields": set(),
|
|
582
|
+
"version": version,
|
|
583
|
+
"available_versions": available_versions,
|
|
550
584
|
}
|
|
551
585
|
continue
|
|
552
586
|
|
|
@@ -619,20 +653,32 @@ class ComposioBaseComponent(Component):
|
|
|
619
653
|
clean_field_name = p_name.replace("[0]", "")
|
|
620
654
|
self._bool_variables.add(clean_field_name)
|
|
621
655
|
|
|
656
|
+
# Extract version information from the tool
|
|
657
|
+
version = tool_dict.get("version")
|
|
658
|
+
available_versions = tool_dict.get("available_versions", [])
|
|
659
|
+
|
|
622
660
|
self._action_schemas[action_key] = tool_dict
|
|
623
661
|
self._actions_data[action_key] = {
|
|
624
662
|
"display_name": display_name,
|
|
625
663
|
"action_fields": action_fields,
|
|
626
664
|
"file_upload_fields": file_upload_fields,
|
|
665
|
+
"version": version,
|
|
666
|
+
"available_versions": available_versions,
|
|
627
667
|
}
|
|
628
668
|
|
|
629
669
|
except (KeyError, TypeError, ValueError) as flatten_error:
|
|
630
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
|
+
|
|
631
675
|
self._action_schemas[action_key] = tool_dict
|
|
632
676
|
self._actions_data[action_key] = {
|
|
633
677
|
"display_name": display_name,
|
|
634
678
|
"action_fields": [],
|
|
635
679
|
"file_upload_fields": set(),
|
|
680
|
+
"version": version,
|
|
681
|
+
"available_versions": available_versions,
|
|
636
682
|
}
|
|
637
683
|
continue
|
|
638
684
|
|
|
@@ -687,6 +733,9 @@ class ComposioBaseComponent(Component):
|
|
|
687
733
|
parameters_schema = parameters_schema.copy() # Don't modify the original
|
|
688
734
|
parameters_schema["required"] = []
|
|
689
735
|
|
|
736
|
+
# Also get top-level required fields from original schema
|
|
737
|
+
original_required = set(parameters_schema.get("required", []))
|
|
738
|
+
|
|
690
739
|
try:
|
|
691
740
|
# Preserve original descriptions before flattening to restore if lost
|
|
692
741
|
original_descriptions = {}
|
|
@@ -910,6 +959,8 @@ class ComposioBaseComponent(Component):
|
|
|
910
959
|
if any(getattr(i, "name", None) == top_name for i in processed_inputs):
|
|
911
960
|
continue
|
|
912
961
|
top_schema = props_dict.get(top_name, {})
|
|
962
|
+
# For MultilineInput fields (complex JSON objects/arrays)
|
|
963
|
+
is_required = top_name in original_required
|
|
913
964
|
processed_inputs.append(
|
|
914
965
|
MultilineInput(
|
|
915
966
|
name=top_name,
|
|
@@ -917,7 +968,7 @@ class ComposioBaseComponent(Component):
|
|
|
917
968
|
info=(
|
|
918
969
|
top_schema.get("description") or "Provide JSON for this parameter (object or array)."
|
|
919
970
|
),
|
|
920
|
-
required=
|
|
971
|
+
required=is_required, # Setting original schema
|
|
921
972
|
)
|
|
922
973
|
)
|
|
923
974
|
|
|
@@ -1007,24 +1058,14 @@ class ComposioBaseComponent(Component):
|
|
|
1007
1058
|
return auth_config.id
|
|
1008
1059
|
|
|
1009
1060
|
def _initiate_connection(self, app_name: str) -> tuple[str, str]:
|
|
1010
|
-
"""Initiate
|
|
1061
|
+
"""Initiate connection using link method and return (redirect_url, connection_id)."""
|
|
1011
1062
|
try:
|
|
1012
1063
|
composio = self._build_wrapper()
|
|
1013
1064
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
auth_config_id = self.create_new_auth_config(app_name)
|
|
1017
|
-
else:
|
|
1018
|
-
auth_config_id = None
|
|
1019
|
-
for auth_config in auth_configs.items:
|
|
1020
|
-
if auth_config.auth_scheme == "OAUTH2":
|
|
1021
|
-
auth_config_id = auth_config.id
|
|
1022
|
-
|
|
1023
|
-
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)
|
|
1024
1067
|
|
|
1025
|
-
connection_request = composio.connected_accounts.
|
|
1026
|
-
user_id=self.entity_id, auth_config_id=auth_config_id
|
|
1027
|
-
)
|
|
1068
|
+
connection_request = composio.connected_accounts.link(user_id=self.entity_id, auth_config_id=auth_config_id)
|
|
1028
1069
|
|
|
1029
1070
|
redirect_url = getattr(connection_request, "redirect_url", None)
|
|
1030
1071
|
connection_id = getattr(connection_request, "id", None)
|
|
@@ -1037,12 +1078,12 @@ class ComposioBaseComponent(Component):
|
|
|
1037
1078
|
msg = "No connection ID received from Composio"
|
|
1038
1079
|
raise ValueError(msg)
|
|
1039
1080
|
|
|
1040
|
-
logger.info(f"
|
|
1081
|
+
logger.info(f"Connection initiated for {app_name}: {redirect_url} (ID: {connection_id})")
|
|
1041
1082
|
return redirect_url, connection_id # noqa: TRY300
|
|
1042
1083
|
|
|
1043
1084
|
except (ValueError, ConnectionError, TypeError, AttributeError) as e:
|
|
1044
1085
|
logger.error(f"Error initiating connection for {app_name}: {e}")
|
|
1045
|
-
msg = f"Failed to initiate
|
|
1086
|
+
msg = f"Failed to initiate connection: {e}"
|
|
1046
1087
|
raise ValueError(msg) from e
|
|
1047
1088
|
|
|
1048
1089
|
def _check_connection_status_by_id(self, connection_id: str) -> str | None:
|
|
@@ -1359,20 +1400,14 @@ class ComposioBaseComponent(Component):
|
|
|
1359
1400
|
desc = field.get("description")
|
|
1360
1401
|
self._add_text_field(build_config, name, disp, desc, required=required, default_value=default_val)
|
|
1361
1402
|
|
|
1362
|
-
#
|
|
1403
|
+
# Only process AuthConfigCreation fields (for custom OAuth2, etc.)
|
|
1404
|
+
# Connection initiation fields are now handled on Composio page via link method
|
|
1363
1405
|
creation = fields.get("auth_config_creation") or fields.get("authConfigCreation") or {}
|
|
1364
1406
|
# Process required fields
|
|
1365
1407
|
process_fields(creation.get("required", []), required=True)
|
|
1366
1408
|
# Process optional fields (excluding those with defaults and bearer_token)
|
|
1367
1409
|
process_fields(creation.get("optional", []), required=False)
|
|
1368
1410
|
|
|
1369
|
-
# b) ConnectedAccountInitiation fields (for API_KEY, etc.)
|
|
1370
|
-
initiation = fields.get("connected_account_initiation") or fields.get("connectedAccountInitiation") or {}
|
|
1371
|
-
# Process required fields
|
|
1372
|
-
process_fields(initiation.get("required", []), required=True)
|
|
1373
|
-
# Process optional fields (excluding those with defaults)
|
|
1374
|
-
process_fields(initiation.get("optional", []), required=False)
|
|
1375
|
-
|
|
1376
1411
|
def _collect_all_auth_field_names(self, schema: dict[str, Any] | None) -> set[str]:
|
|
1377
1412
|
names: set[str] = set()
|
|
1378
1413
|
if not schema:
|
|
@@ -1482,10 +1517,15 @@ class ComposioBaseComponent(Component):
|
|
|
1482
1517
|
selected_mode = (build_config.get("auth_mode") or {}).get("value")
|
|
1483
1518
|
managed = (schema or {}).get("composio_managed_auth_schemes") or []
|
|
1484
1519
|
# Don't render custom fields if "Composio_Managed" is selected
|
|
1485
|
-
|
|
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]:
|
|
1486
1523
|
self._clear_auth_dynamic_fields(build_config)
|
|
1487
1524
|
self._render_custom_auth_fields(build_config, schema or {}, selected_mode)
|
|
1488
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)
|
|
1489
1529
|
except (TypeError, ValueError, AttributeError):
|
|
1490
1530
|
pass
|
|
1491
1531
|
|
|
@@ -1632,6 +1672,9 @@ class ComposioBaseComponent(Component):
|
|
|
1632
1672
|
if mode == "Composio_Managed":
|
|
1633
1673
|
# Composio_Managed → no extra fields needed
|
|
1634
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
|
|
1635
1678
|
elif isinstance(managed, list) and mode in managed:
|
|
1636
1679
|
# This is a specific managed auth scheme (e.g., OAUTH2) but user can still choose custom
|
|
1637
1680
|
# So we should render custom fields for this mode
|
|
@@ -1696,6 +1739,13 @@ class ComposioBaseComponent(Component):
|
|
|
1696
1739
|
|
|
1697
1740
|
# Create new connection ONLY if we truly have no usable connection yet
|
|
1698
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
|
+
|
|
1699
1749
|
try:
|
|
1700
1750
|
# Determine auth mode
|
|
1701
1751
|
schema = self._get_toolkit_schema()
|
|
@@ -1718,7 +1768,7 @@ class ComposioBaseComponent(Component):
|
|
|
1718
1768
|
build_config["auth_link"]["auth_tooltip"] = "Select Auth Mode"
|
|
1719
1769
|
return self.update_input_types(build_config)
|
|
1720
1770
|
# Custom modes: create auth config and/or initiate with config
|
|
1721
|
-
#
|
|
1771
|
+
# Only validate auth_config_creation fields for OAUTH2
|
|
1722
1772
|
required_missing = []
|
|
1723
1773
|
if mode == "OAUTH2":
|
|
1724
1774
|
req_names_pre = self._get_schema_field_names(
|
|
@@ -1732,30 +1782,6 @@ class ComposioBaseComponent(Component):
|
|
|
1732
1782
|
val = build_config[fname].get("value")
|
|
1733
1783
|
if val in (None, ""):
|
|
1734
1784
|
required_missing.append(fname)
|
|
1735
|
-
elif mode == "API_KEY":
|
|
1736
|
-
req_names_pre = self._get_schema_field_names(
|
|
1737
|
-
schema,
|
|
1738
|
-
"API_KEY",
|
|
1739
|
-
"connected_account_initiation",
|
|
1740
|
-
"required",
|
|
1741
|
-
)
|
|
1742
|
-
for fname in req_names_pre:
|
|
1743
|
-
if fname in build_config:
|
|
1744
|
-
val = build_config[fname].get("value")
|
|
1745
|
-
if val in (None, ""):
|
|
1746
|
-
required_missing.append(fname)
|
|
1747
|
-
else:
|
|
1748
|
-
req_names_pre = self._get_schema_field_names(
|
|
1749
|
-
schema,
|
|
1750
|
-
mode,
|
|
1751
|
-
"connected_account_initiation",
|
|
1752
|
-
"required",
|
|
1753
|
-
)
|
|
1754
|
-
for fname in req_names_pre:
|
|
1755
|
-
if fname in build_config:
|
|
1756
|
-
val = build_config[fname].get("value")
|
|
1757
|
-
if val in (None, ""):
|
|
1758
|
-
required_missing.append(fname)
|
|
1759
1785
|
if required_missing:
|
|
1760
1786
|
# Surface errors on each missing field
|
|
1761
1787
|
for fname in required_missing:
|
|
@@ -1779,24 +1805,18 @@ class ComposioBaseComponent(Component):
|
|
|
1779
1805
|
# If an auth_config was already created via the button, use it and include initiation fields
|
|
1780
1806
|
stored_ac_id = (build_config.get("auth_link") or {}).get("auth_config_id")
|
|
1781
1807
|
if stored_ac_id:
|
|
1782
|
-
#
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
if fname in build_config:
|
|
1793
|
-
v = build_config[fname].get("value")
|
|
1794
|
-
if v not in (None, ""):
|
|
1795
|
-
val_payload[fname] = v
|
|
1796
|
-
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(
|
|
1797
1818
|
user_id=self.entity_id,
|
|
1798
1819
|
auth_config_id=stored_ac_id,
|
|
1799
|
-
config={"auth_scheme": "OAUTH2", "val": val_payload} if val_payload else None,
|
|
1800
1820
|
)
|
|
1801
1821
|
redirect_url = getattr(redirect, "redirect_url", None)
|
|
1802
1822
|
connection_id = getattr(redirect, "id", None)
|
|
@@ -1807,6 +1827,9 @@ class ComposioBaseComponent(Component):
|
|
|
1807
1827
|
# Clear action blocker text on successful initiation
|
|
1808
1828
|
build_config["action_button"]["helper_text"] = ""
|
|
1809
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)
|
|
1810
1833
|
return self.update_input_types(build_config)
|
|
1811
1834
|
# Otherwise, create custom OAuth2 auth config using schema-declared required fields
|
|
1812
1835
|
credentials = {}
|
|
@@ -1827,6 +1850,14 @@ class ComposioBaseComponent(Component):
|
|
|
1827
1850
|
else:
|
|
1828
1851
|
missing.append(fname)
|
|
1829
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
|
+
|
|
1830
1861
|
ac = composio.auth_configs.create(
|
|
1831
1862
|
toolkit=toolkit_slug,
|
|
1832
1863
|
options={
|
|
@@ -1836,30 +1867,8 @@ class ComposioBaseComponent(Component):
|
|
|
1836
1867
|
},
|
|
1837
1868
|
)
|
|
1838
1869
|
auth_config_id = getattr(ac, "id", None)
|
|
1839
|
-
#
|
|
1840
|
-
|
|
1841
|
-
schema,
|
|
1842
|
-
"OAUTH2",
|
|
1843
|
-
"connected_account_initiation",
|
|
1844
|
-
"required",
|
|
1845
|
-
)
|
|
1846
|
-
if init_req:
|
|
1847
|
-
self._clear_auth_dynamic_fields(build_config)
|
|
1848
|
-
for name in init_req:
|
|
1849
|
-
self._add_text_field(
|
|
1850
|
-
build_config,
|
|
1851
|
-
name=name,
|
|
1852
|
-
display_name=name.replace("_", " ").title(),
|
|
1853
|
-
info="Provide connection parameter",
|
|
1854
|
-
required=True,
|
|
1855
|
-
)
|
|
1856
|
-
build_config.setdefault("auth_link", {})
|
|
1857
|
-
build_config["auth_link"]["auth_config_id"] = auth_config_id
|
|
1858
|
-
build_config["auth_link"]["value"] = "connect"
|
|
1859
|
-
build_config["auth_link"]["auth_tooltip"] = "Connect"
|
|
1860
|
-
return self.update_input_types(build_config)
|
|
1861
|
-
# Otherwise initiate immediately
|
|
1862
|
-
redirect = composio.connected_accounts.initiate(
|
|
1870
|
+
# Use link method directly - no need to check for connection initiation fields
|
|
1871
|
+
redirect = composio.connected_accounts.link(
|
|
1863
1872
|
user_id=self.entity_id,
|
|
1864
1873
|
auth_config_id=auth_config_id,
|
|
1865
1874
|
)
|
|
@@ -1876,141 +1885,64 @@ class ComposioBaseComponent(Component):
|
|
|
1876
1885
|
build_config["action_button"]["helper_text_metadata"] = {}
|
|
1877
1886
|
return self.update_input_types(build_config)
|
|
1878
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
|
+
|
|
1879
1896
|
ac = composio.auth_configs.create(
|
|
1880
1897
|
toolkit=toolkit_slug,
|
|
1881
1898
|
options={"type": "use_custom_auth", "auth_scheme": "API_KEY", "credentials": {}},
|
|
1882
1899
|
)
|
|
1883
1900
|
auth_config_id = getattr(ac, "id", None)
|
|
1884
|
-
#
|
|
1885
|
-
|
|
1886
|
-
missing = []
|
|
1887
|
-
# Collect required names from schema
|
|
1888
|
-
req_names = self._get_schema_field_names(
|
|
1889
|
-
schema,
|
|
1890
|
-
"API_KEY",
|
|
1891
|
-
"connected_account_initiation",
|
|
1892
|
-
"required",
|
|
1893
|
-
)
|
|
1894
|
-
# Merge rendered dynamic fields and schema-required names
|
|
1895
|
-
candidate_names = set(self._auth_dynamic_fields) | req_names
|
|
1896
|
-
for fname in candidate_names:
|
|
1897
|
-
if fname in build_config:
|
|
1898
|
-
val = build_config[fname].get("value")
|
|
1899
|
-
if val not in (None, ""):
|
|
1900
|
-
val_payload[fname] = val
|
|
1901
|
-
else:
|
|
1902
|
-
missing.append(fname)
|
|
1903
|
-
initiation = composio.connected_accounts.initiate(
|
|
1901
|
+
# Use link method - user will enter API key on Composio page
|
|
1902
|
+
initiation = composio.connected_accounts.link(
|
|
1904
1903
|
user_id=self.entity_id,
|
|
1905
1904
|
auth_config_id=auth_config_id,
|
|
1906
|
-
config={"auth_scheme": "API_KEY", "val": val_payload},
|
|
1907
1905
|
)
|
|
1908
1906
|
connection_id = getattr(initiation, "id", None)
|
|
1909
1907
|
redirect_url = getattr(initiation, "redirect_url", None)
|
|
1910
|
-
#
|
|
1908
|
+
# API_KEY now also returns redirect URL with new link method
|
|
1911
1909
|
if redirect_url:
|
|
1912
1910
|
build_config["auth_link"]["value"] = redirect_url
|
|
1913
1911
|
build_config["auth_link"]["auth_tooltip"] = "Disconnect"
|
|
1914
|
-
|
|
1915
|
-
# No redirect for API_KEY; mark as connected
|
|
1916
|
-
build_config["auth_link"]["value"] = "validated"
|
|
1917
|
-
build_config["auth_link"]["auth_tooltip"] = "Disconnect"
|
|
1918
|
-
# In both cases, hide auth fields immediately after successful initiation
|
|
1912
|
+
# Hide auth fields immediately after successful initiation
|
|
1919
1913
|
schema = self._get_toolkit_schema()
|
|
1920
1914
|
self._clear_auth_fields_from_schema(build_config, schema)
|
|
1921
1915
|
build_config["action_button"]["helper_text"] = ""
|
|
1922
1916
|
build_config["action_button"]["helper_text_metadata"] = {}
|
|
1923
1917
|
|
|
1924
|
-
# Convert auth_mode to pill for connected state
|
|
1925
|
-
if not redirect_url and mode: # API_KEY or similar direct connection
|
|
1926
|
-
build_config["auth_link"]["connection_id"] = connection_id
|
|
1927
|
-
build_config.setdefault("auth_mode", {})
|
|
1928
|
-
build_config["auth_mode"]["value"] = mode
|
|
1929
|
-
build_config["auth_mode"]["options"] = [mode]
|
|
1930
|
-
build_config["auth_mode"]["show"] = False
|
|
1931
|
-
try:
|
|
1932
|
-
pill = TabInput(
|
|
1933
|
-
name="auth_mode",
|
|
1934
|
-
display_name="Auth Mode",
|
|
1935
|
-
options=[mode],
|
|
1936
|
-
value=mode,
|
|
1937
|
-
).to_dict()
|
|
1938
|
-
pill["show"] = True
|
|
1939
|
-
build_config["auth_mode"] = pill
|
|
1940
|
-
except (TypeError, ValueError, AttributeError):
|
|
1941
|
-
build_config["auth_mode"] = {
|
|
1942
|
-
"name": "auth_mode",
|
|
1943
|
-
"display_name": "Auth Mode",
|
|
1944
|
-
"type": "tab",
|
|
1945
|
-
"options": [mode],
|
|
1946
|
-
"value": mode,
|
|
1947
|
-
"show": True,
|
|
1948
|
-
}
|
|
1949
|
-
|
|
1950
1918
|
return self.update_input_types(build_config)
|
|
1951
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
|
+
|
|
1952
1926
|
ac = composio.auth_configs.create(
|
|
1953
1927
|
toolkit=toolkit_slug,
|
|
1954
1928
|
options={"type": "use_custom_auth", "auth_scheme": mode, "credentials": {}},
|
|
1955
1929
|
)
|
|
1956
1930
|
auth_config_id = getattr(ac, "id", None)
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
schema,
|
|
1960
|
-
mode,
|
|
1961
|
-
"connected_account_initiation",
|
|
1962
|
-
"required",
|
|
1963
|
-
)
|
|
1964
|
-
candidate_names = set(self._auth_dynamic_fields) | req_names
|
|
1965
|
-
for fname in candidate_names:
|
|
1966
|
-
if fname in build_config:
|
|
1967
|
-
val = build_config[fname].get("value")
|
|
1968
|
-
if val not in (None, ""):
|
|
1969
|
-
val_payload[fname] = val
|
|
1970
|
-
initiation = composio.connected_accounts.initiate(
|
|
1931
|
+
# Use link method - user will enter required fields on Composio page
|
|
1932
|
+
initiation = composio.connected_accounts.link(
|
|
1971
1933
|
user_id=self.entity_id,
|
|
1972
1934
|
auth_config_id=auth_config_id,
|
|
1973
|
-
config={"auth_scheme": mode, "val": val_payload},
|
|
1974
1935
|
)
|
|
1975
1936
|
connection_id = getattr(initiation, "id", None)
|
|
1976
1937
|
redirect_url = getattr(initiation, "redirect_url", None)
|
|
1977
|
-
# Do not store connection_id on initiation; only when ACTIVE
|
|
1978
1938
|
if redirect_url:
|
|
1979
1939
|
build_config["auth_link"]["value"] = redirect_url
|
|
1980
1940
|
build_config["auth_link"]["auth_tooltip"] = "Disconnect"
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
# Clear auth fields when connected
|
|
1987
|
-
schema = self._get_toolkit_schema()
|
|
1988
|
-
self._clear_auth_fields_from_schema(build_config, schema)
|
|
1989
|
-
|
|
1990
|
-
# Convert auth_mode to pill for connected state
|
|
1991
|
-
if mode:
|
|
1992
|
-
build_config.setdefault("auth_mode", {})
|
|
1993
|
-
build_config["auth_mode"]["value"] = mode
|
|
1994
|
-
build_config["auth_mode"]["options"] = [mode]
|
|
1995
|
-
build_config["auth_mode"]["show"] = False
|
|
1996
|
-
try:
|
|
1997
|
-
pill = TabInput(
|
|
1998
|
-
name="auth_mode",
|
|
1999
|
-
display_name="Auth Mode",
|
|
2000
|
-
options=[mode],
|
|
2001
|
-
value=mode,
|
|
2002
|
-
).to_dict()
|
|
2003
|
-
pill["show"] = True
|
|
2004
|
-
build_config["auth_mode"] = pill
|
|
2005
|
-
except (TypeError, ValueError, AttributeError):
|
|
2006
|
-
build_config["auth_mode"] = {
|
|
2007
|
-
"name": "auth_mode",
|
|
2008
|
-
"display_name": "Auth Mode",
|
|
2009
|
-
"type": "tab",
|
|
2010
|
-
"options": [mode],
|
|
2011
|
-
"value": mode,
|
|
2012
|
-
"show": True,
|
|
2013
|
-
}
|
|
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"] = {}
|
|
2014
1946
|
return self.update_input_types(build_config)
|
|
2015
1947
|
except (ValueError, ConnectionError, TypeError) as e:
|
|
2016
1948
|
logger.error(f"Error creating connection: {e}")
|
|
@@ -2144,7 +2076,12 @@ class ComposioBaseComponent(Component):
|
|
|
2144
2076
|
schema = self._get_toolkit_schema()
|
|
2145
2077
|
mode = (build_config.get("auth_mode") or {}).get("value")
|
|
2146
2078
|
managed = (schema or {}).get("composio_managed_auth_schemes") or []
|
|
2147
|
-
|
|
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
|
+
):
|
|
2148
2085
|
self._render_custom_auth_fields(build_config, schema or {}, mode)
|
|
2149
2086
|
# Already reordered in _render_custom_auth_fields
|
|
2150
2087
|
except (TypeError, ValueError, AttributeError):
|
|
@@ -2189,6 +2126,12 @@ class ComposioBaseComponent(Component):
|
|
|
2189
2126
|
# Handle auth config button click
|
|
2190
2127
|
if field_name == "create_auth_config" and field_value == "create":
|
|
2191
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
|
+
|
|
2192
2135
|
composio = self._build_wrapper()
|
|
2193
2136
|
toolkit_slug = self.app_name.lower()
|
|
2194
2137
|
schema = self._get_toolkit_schema() or {}
|
|
@@ -2209,29 +2152,8 @@ class ComposioBaseComponent(Component):
|
|
|
2209
2152
|
auth_config_id = getattr(ac, "id", None)
|
|
2210
2153
|
build_config.setdefault("auth_link", {})
|
|
2211
2154
|
if auth_config_id:
|
|
2212
|
-
#
|
|
2213
|
-
|
|
2214
|
-
schema, "OAUTH2", "connected_account_initiation", "required"
|
|
2215
|
-
)
|
|
2216
|
-
if initiation_required:
|
|
2217
|
-
# Populate those fields dynamically for the user to fill
|
|
2218
|
-
self._clear_auth_dynamic_fields(build_config)
|
|
2219
|
-
for name in initiation_required:
|
|
2220
|
-
# Render as text inputs to collect connection fields
|
|
2221
|
-
self._add_text_field(
|
|
2222
|
-
build_config,
|
|
2223
|
-
name=name,
|
|
2224
|
-
display_name=name.replace("_", " ").title(),
|
|
2225
|
-
info="Provide connection parameter",
|
|
2226
|
-
required=True,
|
|
2227
|
-
)
|
|
2228
|
-
# Store the new auth_config_id so pressing Connect will use it
|
|
2229
|
-
build_config["auth_link"]["auth_config_id"] = auth_config_id
|
|
2230
|
-
build_config["auth_link"]["value"] = "connect"
|
|
2231
|
-
build_config["auth_link"]["auth_tooltip"] = "Connect"
|
|
2232
|
-
return self.update_input_types(build_config)
|
|
2233
|
-
# If no initiation fields required, initiate immediately
|
|
2234
|
-
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(
|
|
2235
2157
|
user_id=self.entity_id, auth_config_id=auth_config_id
|
|
2236
2158
|
)
|
|
2237
2159
|
redirect_url = getattr(connection_request, "redirect_url", None)
|
|
@@ -2438,6 +2360,8 @@ class ComposioBaseComponent(Component):
|
|
|
2438
2360
|
|
|
2439
2361
|
def execute_action(self):
|
|
2440
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)
|
|
2441
2365
|
composio = self._build_wrapper()
|
|
2442
2366
|
self._populate_actions_data()
|
|
2443
2367
|
self._build_action_maps()
|
|
@@ -2507,12 +2431,23 @@ class ComposioBaseComponent(Component):
|
|
|
2507
2431
|
|
|
2508
2432
|
arguments[final_field_name] = value
|
|
2509
2433
|
|
|
2510
|
-
#
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
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)
|
|
2516
2451
|
|
|
2517
2452
|
if isinstance(result, dict) and "successful" in result:
|
|
2518
2453
|
if result["successful"]:
|