langflow-base-nightly 0.5.0.dev33__py3-none-any.whl → 0.5.0.dev35__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 (171) hide show
  1. langflow/alembic/versions/1cb603706752_modify_uniqueness_constraint_on_file_.py +279 -0
  2. langflow/api/v1/endpoints.py +1 -1
  3. langflow/base/composio/composio_base.py +1092 -126
  4. langflow/components/agents/mcp_component.py +21 -4
  5. langflow/components/composio/__init__.py +24 -0
  6. langflow/components/composio/composio_api.py +116 -136
  7. langflow/components/composio/dropbox_compnent.py +11 -0
  8. langflow/components/composio/github_composio.py +1 -639
  9. langflow/components/composio/gmail_composio.py +26 -394
  10. langflow/components/composio/googlecalendar_composio.py +2 -778
  11. langflow/components/composio/googlemeet_composio.py +11 -0
  12. langflow/components/composio/googletasks_composio.py +8 -0
  13. langflow/components/composio/linear_composio.py +11 -0
  14. langflow/components/composio/outlook_composio.py +1 -755
  15. langflow/components/composio/reddit_composio.py +11 -0
  16. langflow/components/composio/slack_composio.py +1 -576
  17. langflow/components/composio/slackbot_composio.py +11 -0
  18. langflow/components/composio/supabase_composio.py +11 -0
  19. langflow/components/composio/todoist_composio.py +11 -0
  20. langflow/components/composio/youtube_composio.py +11 -0
  21. langflow/components/data/kb_ingest.py +15 -16
  22. langflow/components/processing/save_file.py +31 -4
  23. langflow/custom/utils.py +30 -7
  24. langflow/frontend/assets/{SlackIcon-Bikuxo8x.js → SlackIcon-B260Qg_R.js} +1 -1
  25. langflow/frontend/assets/{Wikipedia-B6aCFf5-.js → Wikipedia-BB2mbgyd.js} +1 -1
  26. langflow/frontend/assets/{Wolfram-CekL_M-a.js → Wolfram-DytXC9hF.js} +1 -1
  27. langflow/frontend/assets/{index-D1RgjMON.js → index-3TJWUdmx.js} +1 -1
  28. langflow/frontend/assets/{index-B4xLpgbM.js → index-3qMh9x6K.js} +1 -1
  29. langflow/frontend/assets/{index-DEuXrfXH.js → index-3uOAA_XX.js} +1 -1
  30. langflow/frontend/assets/{index-DTJX3yQa.js → index-4eRtaV45.js} +1 -1
  31. langflow/frontend/assets/index-7xXgqu09.js +1 -0
  32. langflow/frontend/assets/{index-BRNhftot.js → index-AY5Dm2mG.js} +1 -1
  33. langflow/frontend/assets/{index-4Tl3Nxdo.js → index-AlJ7td-D.js} +1 -1
  34. langflow/frontend/assets/{index-D2nHdRne.js → index-B-c82Fnu.js} +1 -1
  35. langflow/frontend/assets/{index-C3RZz8WE.js → index-B2ggrBuR.js} +1 -1
  36. langflow/frontend/assets/{index-in188l0A.js → index-B536IPXH.js} +1 -1
  37. langflow/frontend/assets/{index-CP0tFKwN.js → index-B5ed-sAv.js} +1 -1
  38. langflow/frontend/assets/{index-CAzSTGAM.js → index-B8TlNgn-.js} +1 -1
  39. langflow/frontend/assets/{index-09CVJwsY.js → index-B8y58M9b.js} +1 -1
  40. langflow/frontend/assets/{index-B9uOBe6Y.js → index-B9Mo3ndZ.js} +1 -1
  41. langflow/frontend/assets/{index-DAJafn16.js → index-BCK-ZyIh.js} +1 -1
  42. langflow/frontend/assets/{index-Cy-ZEfWh.js → index-BEDxAk3N.js} +1 -1
  43. langflow/frontend/assets/{index-DbmqjLy6.js → index-BEKoRwsX.js} +1 -1
  44. langflow/frontend/assets/{index-BcqeL_f4.js → index-BIkqesA-.js} +1 -1
  45. langflow/frontend/assets/{index-7x3wNZ-4.js → index-BJrY2Fiu.js} +1 -1
  46. langflow/frontend/assets/{index-Iamzh9ZT.js → index-BKvKC-12.js} +1 -1
  47. langflow/frontend/assets/{index-COqjpsdy.js → index-BLROcaSz.js} +1 -1
  48. langflow/frontend/assets/{index-BRwkzs92.js → index-BNbWMmAV.js} +1 -1
  49. langflow/frontend/assets/{index-C_UkF-RJ.js → index-BOEf7-ty.js} +1 -1
  50. langflow/frontend/assets/index-BOYTBrh9.js +1 -0
  51. langflow/frontend/assets/{index-DDcpxWU4.js → index-BQB-iDYl.js} +1 -1
  52. langflow/frontend/assets/{index-Crq_yhkG.js → index-BRWNIt9F.js} +1 -1
  53. langflow/frontend/assets/{index-DmaQAn3K.js → index-BVHvIhT5.js} +1 -1
  54. langflow/frontend/assets/{index-Cs_jt3dj.js → index-BVtf6m9S.js} +1 -1
  55. langflow/frontend/assets/{index-T2jJOG85.js → index-BWq9GTzt.js} +1 -1
  56. langflow/frontend/assets/{index-Dz0r9Idb.js → index-BXMhmvTj.js} +1 -1
  57. langflow/frontend/assets/{index-eJwu5YEi.js → index-Ba3RTMXI.js} +1 -1
  58. langflow/frontend/assets/{index-xVx59Op-.js → index-Baka5dKE.js} +1 -1
  59. langflow/frontend/assets/{index-DnusMCK1.js → index-BbsND1Qg.js} +1 -1
  60. langflow/frontend/assets/index-BcgB3rXH.js +1 -0
  61. langflow/frontend/assets/{index-CmiRgF_-.js → index-BdIWbCEL.js} +1 -1
  62. langflow/frontend/assets/{index-BllNr21U.js → index-BdYgKk1d.js} +1 -1
  63. langflow/frontend/assets/{index-BIKbxmIh.js → index-BeNby7qF.js} +1 -1
  64. langflow/frontend/assets/{index-CUe1ivTn.js → index-BejHxU5W.js} +1 -1
  65. langflow/frontend/assets/{index-CVphnxXi.js → index-Bisa4IQF.js} +1 -1
  66. langflow/frontend/assets/{index-Cr2oy5K2.js → index-BjENqyKe.js} +1 -1
  67. langflow/frontend/assets/{index-CEn_71Wk.js → index-BlBl2tvQ.js} +1 -1
  68. langflow/frontend/assets/{index-DOb9c2bf.js → index-BnLT29qW.js} +1 -1
  69. langflow/frontend/assets/{index-BRizlHaN.js → index-BqUeOc7Y.js} +1 -1
  70. langflow/frontend/assets/{index-D7nFs6oq.js → index-BsBWP-Dh.js} +1 -1
  71. langflow/frontend/assets/{index-BlRTHXW5.js → index-BtJ2o21k.js} +1 -1
  72. langflow/frontend/assets/{index-AOX7bbjJ.js → index-BxWXWRmZ.js} +1 -1
  73. langflow/frontend/assets/{index-B20KmxhS.js → index-BxkZkBgQ.js} +1 -1
  74. langflow/frontend/assets/{index-DoFlaGDx.js → index-Bxml6wXu.js} +1 -1
  75. langflow/frontend/assets/{index-B9KRIJFi.js → index-ByFXr9Iq.js} +1 -1
  76. langflow/frontend/assets/{index-CY6LUi4V.js → index-C2Xd7UkR.js} +1 -1
  77. langflow/frontend/assets/index-C76aBV_h.js +1 -0
  78. langflow/frontend/assets/{index-9gkURvG2.js → index-C7V5U9yH.js} +1 -1
  79. langflow/frontend/assets/{index-BDmbsLY2.js → index-C7x9R_Yo.js} +1 -1
  80. langflow/frontend/assets/{index-DI0zAExi.js → index-C8KD3LPb.js} +1 -1
  81. langflow/frontend/assets/{index-DzDNhMMW.js → index-C9N80hP8.js} +1 -1
  82. langflow/frontend/assets/{index-6GWpsedd.js → index-CDFLVFB4.js} +1 -1
  83. langflow/frontend/assets/{index-pkOi9P45.js → index-CF4dtI6S.js} +1 -1
  84. langflow/frontend/assets/{index-CdwjD4IX.js → index-CG7cp0nD.js} +1 -1
  85. langflow/frontend/assets/{index-J0pvFqLk.js → index-CHFO5O4g.js} +1 -1
  86. langflow/frontend/assets/{index-5G402gB8.js → index-CJwYfDBz.js} +1 -1
  87. langflow/frontend/assets/{index-BzCjyHto.js → index-CMGZGIx_.js} +1 -1
  88. langflow/frontend/assets/{index-Bm7a2vMS.js → index-COL0eiWI.js} +1 -1
  89. langflow/frontend/assets/{index-JHCxbvlW.js → index-CWWo2zOA.js} +1 -1
  90. langflow/frontend/assets/{index-C7wDSVVH.js → index-C_1RBTul.js} +1 -1
  91. langflow/frontend/assets/{index-BIjUtp6d.js → index-Ccb5B8zG.js} +1 -1
  92. langflow/frontend/assets/{index-yIh6-LZT.js → index-Cd5zuUUK.js} +1 -1
  93. langflow/frontend/assets/{index-CPIdMJkX.js → index-CkQ-bJ4G.js} +1 -1
  94. langflow/frontend/assets/{index-TRyDa01A.js → index-CkSzjCqM.js} +1 -1
  95. langflow/frontend/assets/{index-CSRizl2S.js → index-CoUlHbtg.js} +1 -1
  96. langflow/frontend/assets/index-Cpgkb0Q3.js +1 -0
  97. langflow/frontend/assets/{index-Cp7Pmn03.js → index-CqDUqHfd.js} +1 -1
  98. langflow/frontend/assets/{index-CGVDXKtN.js → index-Ct9_T9ox.js} +1 -1
  99. langflow/frontend/assets/{index-BwlYjc56.js → index-CvQ0w8Pj.js} +1 -1
  100. langflow/frontend/assets/{index-DkJCCraf.js → index-CwIxqYlT.js} +1 -1
  101. langflow/frontend/assets/{index-Bgd7yLoW.js → index-Cx__T92e.js} +1 -1
  102. langflow/frontend/assets/{index-RveG4dl9.js → index-D-zkHcob.js} +1 -1
  103. langflow/frontend/assets/{index-DVV_etfW.js → index-D0HmkH0H.js} +1 -1
  104. langflow/frontend/assets/{index-CglSqvB5.js → index-D0s9f6Re.js} +1 -1
  105. langflow/frontend/assets/{index-J98sU-1p.js → index-D5PeCofu.js} +1 -1
  106. langflow/frontend/assets/{index-BJIsQS8D.js → index-D87Zw62M.js} +1 -1
  107. langflow/frontend/assets/{index-FYcoJPMP.js → index-D9eflZfP.js} +1 -1
  108. langflow/frontend/assets/{index-DJs6FoYC.js → index-DDNNv4C0.js} +1 -1
  109. langflow/frontend/assets/index-DHlEwAxb.js +1 -0
  110. langflow/frontend/assets/{index-DqDQk0Cu.js → index-DIqSyDVO.js} +1 -1
  111. langflow/frontend/assets/{index-DOI0ceS-.js → index-DK8vNpXK.js} +1 -1
  112. langflow/frontend/assets/{index-D29n5mus.js → index-DKEXZFUO.js} +1 -1
  113. langflow/frontend/assets/{index-dfaj9-hY.js → index-DPX6X_bw.js} +1 -1
  114. langflow/frontend/assets/{index-CgbINWS8.js → index-DS1EgA10.js} +1 -1
  115. langflow/frontend/assets/{index-C69gdJqw.js → index-DS9I4y48.js} +1 -1
  116. langflow/frontend/assets/{index-B2EmwqKj.js → index-DWkMJnbd.js} +1 -1
  117. langflow/frontend/assets/{index-CIYzjH2y.js → index-DWr_zPkx.js} +1 -1
  118. langflow/frontend/assets/{index-D-HTZ68O.js → index-DX7XsAcx.js} +1 -1
  119. langflow/frontend/assets/{index-Cq30cQcP.js → index-DZzbmg3J.js} +1 -1
  120. langflow/frontend/assets/{index-BZCt_UnJ.js → index-DasrI03Y.js} +1 -1
  121. langflow/frontend/assets/index-DdzVmJHE.js +1 -0
  122. langflow/frontend/assets/{index-DmvjdU1N.js → index-DhzEUXfr.js} +1 -1
  123. langflow/frontend/assets/{index-B_ytx_iA.js → index-DpJiH-Rk.js} +1 -1
  124. langflow/frontend/assets/{index-Cyk3aCmP.js → index-DpQKtcXu.js} +1 -1
  125. langflow/frontend/assets/{index-DrvRK4_i.js → index-Dpz3oBf5.js} +1 -1
  126. langflow/frontend/assets/{index-DF0oWRdd.js → index-DqSH4x-R.js} +1 -1
  127. langflow/frontend/assets/{index-DX_InNVT.js → index-DtJyCbzF.js} +1 -1
  128. langflow/frontend/assets/{index-B4AtFbkN.js → index-Du9aJK7m.js} +1 -1
  129. langflow/frontend/assets/{index-qXcoVIRo.js → index-DuAeoC-H.js} +1 -1
  130. langflow/frontend/assets/{index-D7Vx6mgS.js → index-DxIs8VSp.js} +1 -1
  131. langflow/frontend/assets/{index-U7J1YiWE.js → index-DyJDHm2D.js} +1 -1
  132. langflow/frontend/assets/{index-1MEYR1La.js → index-DzeIsaBm.js} +1 -1
  133. langflow/frontend/assets/{index-Cbwk3f-p.js → index-DztLFiip.js} +1 -1
  134. langflow/frontend/assets/{index-C_2G2ZqJ.js → index-GODbXlHC.js} +1 -1
  135. langflow/frontend/assets/{index-2vQdFIK_.js → index-G_U_kPAd.js} +1 -1
  136. langflow/frontend/assets/{index-DS4F_Phe.js → index-IFGgPiye.js} +1 -1
  137. langflow/frontend/assets/{index-5hW8VleF.js → index-LrMzDsq9.js} +1 -1
  138. langflow/frontend/assets/{index-L7FKc9QN.js → index-R7q8cAek.js} +1 -1
  139. langflow/frontend/assets/{index-BRE8A4Q_.js → index-Uq2ij_SS.js} +1 -1
  140. langflow/frontend/assets/{index-Bn4HAVDG.js → index-VHmUHUUU.js} +1 -1
  141. langflow/frontend/assets/{index-VO-pk-Hg.js → index-VZnN0P6C.js} +1 -1
  142. langflow/frontend/assets/{index-Dy7ehgeV.js → index-VcXZzovW.js} +1 -1
  143. langflow/frontend/assets/{index-DNS4La1f.js → index-Ym6gz0T6.js} +1 -1
  144. langflow/frontend/assets/{index-UI2ws3qp.js → index-ci4XHjbJ.js} +176 -176
  145. langflow/frontend/assets/{index-DlMAYATX.js → index-dkS0ek2S.js} +1 -1
  146. langflow/frontend/assets/{index-Dc0p1Oxl.js → index-hOkEW3JP.js} +1 -1
  147. langflow/frontend/assets/{index-KnS52ylc.js → index-js8ceOaP.js} +1 -1
  148. langflow/frontend/assets/{index-DtCsjX48.js → index-lKEJpUsF.js} +1 -1
  149. langflow/frontend/assets/{index-BO4fl1uU.js → index-mBjJYD9q.js} +1 -1
  150. langflow/frontend/assets/{index-C_K6Tof7.js → index-r1LZg-PY.js} +1 -1
  151. langflow/frontend/assets/index-rcdQpNcU.js +1 -0
  152. langflow/frontend/assets/{index-_3qag0I4.js → index-sS6XLk3j.js} +1 -1
  153. langflow/frontend/assets/{index-C6P0vvSP.js → index-tOy_uloT.js} +1 -1
  154. langflow/frontend/assets/lazyIconImports-Bh1TFfvH.js +2 -0
  155. langflow/frontend/assets/{use-post-add-user-Bt6vZvvT.js → use-post-add-user-HN0rRnhv.js} +1 -1
  156. langflow/frontend/index.html +1 -1
  157. langflow/initial_setup/starter_projects/Knowledge Ingestion.json +2 -2
  158. langflow/initial_setup/starter_projects/News Aggregator.json +19 -2
  159. langflow/initial_setup/starter_projects/Nvidia Remix.json +19 -2
  160. langflow/interface/initialize/loading.py +3 -1
  161. langflow/main.py +19 -2
  162. langflow/services/database/models/file/model.py +4 -2
  163. langflow/services/database/service.py +3 -1
  164. langflow/services/telemetry/schema.py +7 -0
  165. langflow/services/telemetry/service.py +25 -0
  166. langflow/services/tracing/service.py +14 -4
  167. {langflow_base_nightly-0.5.0.dev33.dist-info → langflow_base_nightly-0.5.0.dev35.dist-info}/METADATA +1 -1
  168. {langflow_base_nightly-0.5.0.dev33.dist-info → langflow_base_nightly-0.5.0.dev35.dist-info}/RECORD +170 -152
  169. langflow/frontend/assets/lazyIconImports-kvf_Kak2.js +0 -2
  170. {langflow_base_nightly-0.5.0.dev33.dist-info → langflow_base_nightly-0.5.0.dev35.dist-info}/WHEEL +0 -0
  171. {langflow_base_nightly-0.5.0.dev33.dist-info → langflow_base_nightly-0.5.0.dev35.dist-info}/entry_points.txt +0 -0
@@ -16,14 +16,14 @@ from langflow.base.mcp.util import (
16
16
  )
17
17
  from langflow.custom.custom_component.component_with_cache import ComponentWithCache
18
18
  from langflow.inputs.inputs import InputTypes # noqa: TC001
19
- from langflow.io import DropdownInput, McpInput, MessageTextInput, Output
19
+ from langflow.io import DropdownInput, McpInput, MessageTextInput, Output, SecretStrInput
20
20
  from langflow.io.schema import flatten_schema, schema_to_langflow_inputs
21
21
  from langflow.logging import logger
22
22
  from langflow.schema.dataframe import DataFrame
23
23
  from langflow.schema.message import Message
24
- from langflow.services.auth.utils import create_user_longterm_token
25
24
 
26
25
  # Import get_server from the backend API
26
+ from langflow.services.auth.utils import create_user_longterm_token, get_current_user
27
27
  from langflow.services.database.models.user.crud import get_user_by_id
28
28
  from langflow.services.deps import get_session, get_settings_service, get_storage_service
29
29
 
@@ -96,6 +96,13 @@ class MCPToolsComponent(ComponentWithCache):
96
96
  show=False,
97
97
  tool_mode=False,
98
98
  ),
99
+ SecretStrInput(
100
+ name="api_key",
101
+ display_name="Langflow API Key",
102
+ info="Langflow API key for authentication when fetching MCP servers and tools.",
103
+ required=False,
104
+ advanced=True,
105
+ ),
99
106
  ]
100
107
 
101
108
  outputs = [
@@ -155,8 +162,18 @@ class MCPToolsComponent(ComponentWithCache):
155
162
 
156
163
  try:
157
164
  async for db in get_session():
158
- user_id, _ = await create_user_longterm_token(db)
159
- current_user = await get_user_by_id(db, user_id)
165
+ # TODO: In 1.6, this may need to be removed or adjusted
166
+ # Try to get the super user token, if possible
167
+ if self.api_key:
168
+ current_user = await get_current_user(
169
+ token=None,
170
+ query_param=self.api_key,
171
+ header_param=None,
172
+ db=db,
173
+ )
174
+ else:
175
+ user_id, _ = await create_user_longterm_token(db)
176
+ current_user = await get_user_by_id(db, user_id)
160
177
 
161
178
  # Try to get server config from DB/API
162
179
  server_config = await get_server(
@@ -9,16 +9,32 @@ if TYPE_CHECKING:
9
9
  from .github_composio import ComposioGitHubAPIComponent
10
10
  from .gmail_composio import ComposioGmailAPIComponent
11
11
  from .googlecalendar_composio import ComposioGoogleCalendarAPIComponent
12
+ from .googlemeet_composio import ComposioGooglemeetAPIComponent
13
+ from .googletasks_composio import ComposioGoogleTasksAPIComponent
14
+ from .linear_composio import ComposioLinearAPIComponent
12
15
  from .outlook_composio import ComposioOutlookAPIComponent
16
+ from .reddit_composio import ComposioRedditAPIComponent
13
17
  from .slack_composio import ComposioSlackAPIComponent
18
+ from .slackbot_composio import ComposioSlackbotAPIComponent
19
+ from .supabase_composio import ComposioSupabaseAPIComponent
20
+ from .todoist_composio import ComposioTodoistAPIComponent
21
+ from .youtube_composio import ComposioYoutubeAPIComponent
14
22
 
15
23
  _dynamic_imports = {
16
24
  "ComposioAPIComponent": "composio_api",
17
25
  "ComposioGitHubAPIComponent": "github_composio",
18
26
  "ComposioGmailAPIComponent": "gmail_composio",
19
27
  "ComposioGoogleCalendarAPIComponent": "googlecalendar_composio",
28
+ "ComposioGooglemeetAPIComponent": "googlemeet_composio",
20
29
  "ComposioOutlookAPIComponent": "outlook_composio",
21
30
  "ComposioSlackAPIComponent": "slack_composio",
31
+ "ComposioGoogleTasksAPIComponent": "googletasks_composio",
32
+ "ComposioLinearAPIComponent": "linear_composio",
33
+ "ComposioRedditAPIComponent": "reddit_composio",
34
+ "ComposioSlackbotAPIComponent": "slackbot_composio",
35
+ "ComposioSupabaseAPIComponent": "supabase_composio",
36
+ "ComposioTodoistAPIComponent": "todoist_composio",
37
+ "ComposioYoutubeAPIComponent": "youtube_composio",
22
38
  }
23
39
 
24
40
  __all__ = [
@@ -26,8 +42,16 @@ __all__ = [
26
42
  "ComposioGitHubAPIComponent",
27
43
  "ComposioGmailAPIComponent",
28
44
  "ComposioGoogleCalendarAPIComponent",
45
+ "ComposioGoogleTasksAPIComponent",
46
+ "ComposioGooglemeetAPIComponent",
47
+ "ComposioLinearAPIComponent",
29
48
  "ComposioOutlookAPIComponent",
49
+ "ComposioRedditAPIComponent",
30
50
  "ComposioSlackAPIComponent",
51
+ "ComposioSlackbotAPIComponent",
52
+ "ComposioSupabaseAPIComponent",
53
+ "ComposioTodoistAPIComponent",
54
+ "ComposioYoutubeAPIComponent",
31
55
  ]
32
56
 
33
57
 
@@ -2,10 +2,10 @@
2
2
  from collections.abc import Sequence
3
3
  from typing import Any
4
4
 
5
- from composio import Action, App
5
+ from composio import Composio
6
+ from composio_langchain import LangchainProvider
6
7
 
7
8
  # Third-party imports
8
- from composio_langchain import ComposioToolSet
9
9
  from langchain_core.tools import Tool
10
10
 
11
11
  # Local imports
@@ -69,27 +69,7 @@ class ComposioAPIComponent(LCToolComponent):
69
69
  Output(name="tools", display_name="Tools", method="build_tool"),
70
70
  ]
71
71
 
72
- def sanitize_action_name(self, action_name: str) -> str:
73
- # TODO: Maybe restore
74
- return action_name
75
-
76
- # We want to use title case, and replace underscores with spaces
77
- sanitized_name = action_name.replace("_", " ").title()
78
-
79
- # Now we want to remove everything from and including the first dot
80
- return sanitized_name.replace(self.tool_name.title() + " ", "")
81
-
82
- def desanitize_action_name(self, action_name: str) -> str:
83
- # TODO: Maybe restore
84
- return action_name
85
-
86
- # We want to reverse what we did above
87
- unsanitized_name = action_name.replace(" ", "_").upper()
88
-
89
- # Append the tool_name to it at the beginning, followed by a dot, in all CAPS
90
- return f"{self.tool_name.upper()}_{unsanitized_name}"
91
-
92
- def validate_tool(self, build_config: dict, field_value: Any, connected_app_names: list) -> dict:
72
+ def validate_tool(self, build_config: dict, field_value: Any, tool_name: str | None = None) -> dict:
93
73
  # Get the index of the selected tool in the list of options
94
74
  selected_tool_index = next(
95
75
  (
@@ -108,35 +88,40 @@ class ComposioAPIComponent(LCToolComponent):
108
88
  build_config["actions"]["helper_text"] = ""
109
89
  build_config["actions"]["helper_text_metadata"] = {"icon": "Check", "variant": "success"}
110
90
 
111
- # Get the list of actions available
112
- all_actions = list(Action.all())
113
- authenticated_actions = sorted(
114
- [
115
- action
116
- for action in all_actions
117
- if action.app.lower() in list(connected_app_names) and action.app.lower() == self.tool_name.lower()
118
- ],
119
- key=lambda x: x.name,
120
- )
91
+ try:
92
+ composio = self._build_wrapper()
93
+ current_tool = tool_name or getattr(self, "tool_name", None)
94
+ if not current_tool:
95
+ self.log("No tool name available for validate_tool")
96
+ return build_config
97
+
98
+ toolkit_slug = current_tool.lower()
99
+
100
+ tools = composio.tools.get(user_id=self.entity_id, toolkits=[toolkit_slug])
101
+
102
+ authenticated_actions = []
103
+ for tool in tools:
104
+ if hasattr(tool, "name"):
105
+ action_name = tool.name
106
+ display_name = action_name.replace("_", " ").title()
107
+ authenticated_actions.append({"name": action_name, "display_name": display_name})
108
+ except (ValueError, ConnectionError, AttributeError) as e:
109
+ self.log(f"Error getting actions for {current_tool or 'unknown tool'}: {e}")
110
+ authenticated_actions = []
121
111
 
122
- # Return the list of action names
123
112
  build_config["actions"]["options"] = [
124
113
  {
125
- "name": self.sanitize_action_name(action.name),
114
+ "name": action["name"],
126
115
  }
127
116
  for action in authenticated_actions
128
117
  ]
129
118
 
130
- # Lastly, we need to show the actions field
131
119
  build_config["actions"]["show"] = True
132
-
133
120
  return build_config
134
121
 
135
122
  def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:
136
- # If the list of tools is not available, always update it
137
123
  if field_name == "api_key" or (self.api_key and not build_config["tool_name"]["options"]):
138
124
  if field_name == "api_key" and not field_value:
139
- # Reset the list of tools
140
125
  build_config["tool_name"]["options"] = []
141
126
  build_config["tool_name"]["value"] = ""
142
127
 
@@ -147,113 +132,94 @@ class ComposioAPIComponent(LCToolComponent):
147
132
 
148
133
  return build_config
149
134
 
150
- # TODO: Re-enable dynamic tool list
151
- # Initialize the Composio ToolSet with your API key
152
- # toolset = ComposioToolSet(api_key=self.api_key)
153
-
154
- # Get the entity (e.g., "default" for your user)
155
- # entity = toolset.get_entity(self.entity_id)
156
-
157
- # Get all available apps
158
- # all_apps = entity.client.apps.get()
159
-
160
- # Build an object with name, icon, link
135
+ # Build the list of available tools
161
136
  build_config["tool_name"]["options"] = [
162
137
  {
163
- "name": app.title(), # TODO: Switch to app.name
164
- "icon": app, # TODO: Switch to app.name
138
+ "name": app.title(),
139
+ "icon": app,
165
140
  "link": (
166
141
  build_config["tool_name"]["options"][ind]["link"]
167
142
  if build_config["tool_name"]["options"]
168
143
  else ""
169
144
  ),
170
145
  }
171
- # for app in sorted(all_apps, key=lambda x: x.name)
172
146
  for ind, app in enumerate(enabled_tools)
173
147
  ]
174
148
 
175
149
  return build_config
176
150
 
177
- # Handle the click of the Tool Name connect button
178
151
  if field_name == "tool_name" and field_value:
179
- # Get the list of apps (tools) we have connected
180
- toolset = ComposioToolSet(api_key=self.api_key)
181
- connected_apps = [app for app in toolset.get_connected_accounts() if app.status == "ACTIVE"]
182
-
183
- # Get the unique list of appName from the connected apps
184
- connected_app_names = [app.appName.lower() for app in connected_apps]
185
-
186
- # Clear out the list of selected actions
187
- build_config["actions"]["show"] = True
188
- build_config["actions"]["options"] = []
189
- build_config["actions"]["value"] = ""
190
-
191
- # Clear out any helper text
192
- build_config["tool_name"]["helper_text"] = ""
193
- build_config["tool_name"]["helper_text_metadata"] = {}
194
-
195
- # If it's a dictionary, we need to do validation
196
- if isinstance(field_value, dict):
197
- # If the current field value is a dictionary, it means the user has selected a tool
198
- if "validate" not in field_value:
199
- return build_config
200
-
201
- # Check if the selected tool is connected
202
- check_app = field_value["validate"].lower()
203
-
204
- # If the tool selected is NOT what we are validating, return the build config
205
- if check_app != self.tool_name.lower():
206
- # Set the helper text and helper text metadata field of the actions now
207
- build_config["actions"]["helper_text"] = "Please connect before selecting actions."
208
- build_config["actions"]["helper_text_metadata"] = {
209
- "icon": "OctagonAlert",
210
- "variant": "destructive",
211
- }
212
-
213
- return build_config
214
-
215
- # Check if the tool is already validated
216
- if check_app not in connected_app_names:
217
- return build_config
218
-
219
- # Validate the selected tool
220
- return self.validate_tool(build_config, field_value, connected_app_names)
221
-
222
- # Check if the tool is already validated
223
- if field_value.lower() in connected_app_names:
224
- return self.validate_tool(build_config, field_value, connected_app_names)
225
-
226
- # Get the entity (e.g., "default" for your user)
227
- entity = toolset.get_entity(id=self.entity_id)
228
-
229
- # Set the metadata for the actions
230
- build_config["actions"]["helper_text_metadata"] = {"icon": "OctagonAlert", "variant": "destructive"}
231
-
232
- # Get the index of the selected tool in the list of options
233
- selected_tool_index = next(
234
- (ind for ind, tool in enumerate(build_config["tool_name"]["options"]) if tool["name"] == field_value),
235
- None,
152
+ composio = self._build_wrapper()
153
+
154
+ current_tool_name = (
155
+ field_value
156
+ if isinstance(field_value, str)
157
+ else field_value.get("validate")
158
+ if isinstance(field_value, dict) and "validate" in field_value
159
+ else getattr(self, "tool_name", None)
236
160
  )
237
161
 
238
- # Initiate a GitHub connection and get the redirect URL
239
- try:
240
- connection_request = entity.initiate_connection(app_name=getattr(App, field_value.upper()))
241
- except Exception as _: # noqa: BLE001
242
- # Indicate that there was an error connecting to the tool
243
- build_config["tool_name"]["options"][selected_tool_index]["link"] = "error"
244
- build_config["tool_name"]["helper_text"] = f"Error connecting to {field_value}"
245
- build_config["tool_name"]["helper_text_metadata"] = {
246
- "icon": "OctagonAlert",
247
- "variant": "destructive",
248
- }
249
-
162
+ if not current_tool_name:
163
+ self.log("No tool name available for connection check")
250
164
  return build_config
251
165
 
252
- # Print the direct HTTP link for authentication
253
- build_config["tool_name"]["options"][selected_tool_index]["link"] = connection_request.redirectUrl
254
-
255
- # Set the helper text and helper text metadata field of the actions now
256
- build_config["actions"]["helper_text"] = "Please connect before selecting actions."
166
+ try:
167
+ toolkit_slug = current_tool_name.lower()
168
+
169
+ connection_list = composio.connected_accounts.list(
170
+ user_ids=[self.entity_id], toolkit_slugs=[toolkit_slug]
171
+ )
172
+
173
+ # Check for active connections
174
+ has_active_connections = False
175
+ if (
176
+ connection_list
177
+ and hasattr(connection_list, "items")
178
+ and connection_list.items
179
+ and isinstance(connection_list.items, list)
180
+ and len(connection_list.items) > 0
181
+ ):
182
+ for connection in connection_list.items:
183
+ if getattr(connection, "status", None) == "ACTIVE":
184
+ has_active_connections = True
185
+ break
186
+
187
+ # Get the index of the selected tool in the list of options
188
+ selected_tool_index = next(
189
+ (
190
+ ind
191
+ for ind, tool in enumerate(build_config["tool_name"]["options"])
192
+ if tool["name"] == current_tool_name.title()
193
+ ),
194
+ None,
195
+ )
196
+
197
+ if has_active_connections:
198
+ # User has active connection
199
+ if selected_tool_index is not None:
200
+ build_config["tool_name"]["options"][selected_tool_index]["link"] = "validated"
201
+
202
+ # If it's a validation request, validate the tool
203
+ if (isinstance(field_value, dict) and "validate" in field_value) or isinstance(field_value, str):
204
+ return self.validate_tool(build_config, field_value, current_tool_name)
205
+ else:
206
+ # No active connection - create OAuth connection
207
+ try:
208
+ connection = composio.toolkits.authorize(user_id=self.entity_id, toolkit=toolkit_slug)
209
+ redirect_url = getattr(connection, "redirect_url", None)
210
+
211
+ if redirect_url and redirect_url.startswith(("http://", "https://")):
212
+ if selected_tool_index is not None:
213
+ build_config["tool_name"]["options"][selected_tool_index]["link"] = redirect_url
214
+ elif selected_tool_index is not None:
215
+ build_config["tool_name"]["options"][selected_tool_index]["link"] = "error"
216
+ except (ValueError, ConnectionError, AttributeError) as e:
217
+ self.log(f"Error creating OAuth connection: {e}")
218
+ if selected_tool_index is not None:
219
+ build_config["tool_name"]["options"][selected_tool_index]["link"] = "error"
220
+
221
+ except (ValueError, ConnectionError, AttributeError) as e:
222
+ self.log(f"Error checking connection status: {e}")
257
223
 
258
224
  return build_config
259
225
 
@@ -263,16 +229,30 @@ class ComposioAPIComponent(LCToolComponent):
263
229
  Returns:
264
230
  Sequence[Tool]: List of configured Composio tools.
265
231
  """
266
- composio_toolset = self._build_wrapper()
267
- return composio_toolset.get_tools(
268
- actions=[self.desanitize_action_name(action["name"]) for action in self.actions]
269
- )
232
+ composio = self._build_wrapper()
233
+ action_names = [action["name"] for action in self.actions]
234
+
235
+ # Get toolkits from action names
236
+ toolkits = set()
237
+ for action_name in action_names:
238
+ if "_" in action_name:
239
+ toolkit = action_name.split("_")[0].lower()
240
+ toolkits.add(toolkit)
241
+
242
+ if not toolkits:
243
+ return []
244
+
245
+ # Get all tools for the relevant toolkits
246
+ all_tools = composio.tools.get(user_id=self.entity_id, toolkits=list(toolkits))
247
+
248
+ # Filter to only the specific actions we want using list comprehension
249
+ return [tool for tool in all_tools if hasattr(tool, "name") and tool.name in action_names]
270
250
 
271
- def _build_wrapper(self) -> ComposioToolSet:
272
- """Build the Composio toolset wrapper.
251
+ def _build_wrapper(self) -> Composio:
252
+ """Build the Composio wrapper using new SDK.
273
253
 
274
254
  Returns:
275
- ComposioToolSet: The initialized toolset.
255
+ Composio: The initialized Composio client.
276
256
 
277
257
  Raises:
278
258
  ValueError: If the API key is not found or invalid.
@@ -281,7 +261,7 @@ class ComposioAPIComponent(LCToolComponent):
281
261
  if not self.api_key:
282
262
  msg = "Composio API Key is required"
283
263
  raise ValueError(msg)
284
- return ComposioToolSet(api_key=self.api_key, entity_id=self.entity_id)
264
+ return Composio(api_key=self.api_key, provider=LangchainProvider())
285
265
  except ValueError as e:
286
266
  self.log(f"Error building Composio wrapper: {e}")
287
267
  msg = "Please provide a valid Composio API Key in the component settings"
@@ -0,0 +1,11 @@
1
+ from langflow.base.composio.composio_base import ComposioBaseComponent
2
+
3
+
4
+ class ComposioDropboxAPIComponent(ComposioBaseComponent):
5
+ display_name: str = "Dropbox"
6
+ icon = "Dropbox"
7
+ documentation: str = "https://docs.composio.dev"
8
+ app_name = "dropbox"
9
+
10
+ def set_default_tools(self):
11
+ """Set the default tools for Dropbox component."""