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.
- langflow/alembic/versions/1cb603706752_modify_uniqueness_constraint_on_file_.py +279 -0
- langflow/api/v1/endpoints.py +1 -1
- langflow/base/composio/composio_base.py +1092 -126
- langflow/components/agents/mcp_component.py +21 -4
- langflow/components/composio/__init__.py +24 -0
- langflow/components/composio/composio_api.py +116 -136
- langflow/components/composio/dropbox_compnent.py +11 -0
- langflow/components/composio/github_composio.py +1 -639
- langflow/components/composio/gmail_composio.py +26 -394
- langflow/components/composio/googlecalendar_composio.py +2 -778
- langflow/components/composio/googlemeet_composio.py +11 -0
- langflow/components/composio/googletasks_composio.py +8 -0
- langflow/components/composio/linear_composio.py +11 -0
- langflow/components/composio/outlook_composio.py +1 -755
- langflow/components/composio/reddit_composio.py +11 -0
- langflow/components/composio/slack_composio.py +1 -576
- langflow/components/composio/slackbot_composio.py +11 -0
- langflow/components/composio/supabase_composio.py +11 -0
- langflow/components/composio/todoist_composio.py +11 -0
- langflow/components/composio/youtube_composio.py +11 -0
- langflow/components/data/kb_ingest.py +15 -16
- langflow/components/processing/save_file.py +31 -4
- langflow/custom/utils.py +30 -7
- langflow/frontend/assets/{SlackIcon-Bikuxo8x.js → SlackIcon-B260Qg_R.js} +1 -1
- langflow/frontend/assets/{Wikipedia-B6aCFf5-.js → Wikipedia-BB2mbgyd.js} +1 -1
- langflow/frontend/assets/{Wolfram-CekL_M-a.js → Wolfram-DytXC9hF.js} +1 -1
- langflow/frontend/assets/{index-D1RgjMON.js → index-3TJWUdmx.js} +1 -1
- langflow/frontend/assets/{index-B4xLpgbM.js → index-3qMh9x6K.js} +1 -1
- langflow/frontend/assets/{index-DEuXrfXH.js → index-3uOAA_XX.js} +1 -1
- langflow/frontend/assets/{index-DTJX3yQa.js → index-4eRtaV45.js} +1 -1
- langflow/frontend/assets/index-7xXgqu09.js +1 -0
- langflow/frontend/assets/{index-BRNhftot.js → index-AY5Dm2mG.js} +1 -1
- langflow/frontend/assets/{index-4Tl3Nxdo.js → index-AlJ7td-D.js} +1 -1
- langflow/frontend/assets/{index-D2nHdRne.js → index-B-c82Fnu.js} +1 -1
- langflow/frontend/assets/{index-C3RZz8WE.js → index-B2ggrBuR.js} +1 -1
- langflow/frontend/assets/{index-in188l0A.js → index-B536IPXH.js} +1 -1
- langflow/frontend/assets/{index-CP0tFKwN.js → index-B5ed-sAv.js} +1 -1
- langflow/frontend/assets/{index-CAzSTGAM.js → index-B8TlNgn-.js} +1 -1
- langflow/frontend/assets/{index-09CVJwsY.js → index-B8y58M9b.js} +1 -1
- langflow/frontend/assets/{index-B9uOBe6Y.js → index-B9Mo3ndZ.js} +1 -1
- langflow/frontend/assets/{index-DAJafn16.js → index-BCK-ZyIh.js} +1 -1
- langflow/frontend/assets/{index-Cy-ZEfWh.js → index-BEDxAk3N.js} +1 -1
- langflow/frontend/assets/{index-DbmqjLy6.js → index-BEKoRwsX.js} +1 -1
- langflow/frontend/assets/{index-BcqeL_f4.js → index-BIkqesA-.js} +1 -1
- langflow/frontend/assets/{index-7x3wNZ-4.js → index-BJrY2Fiu.js} +1 -1
- langflow/frontend/assets/{index-Iamzh9ZT.js → index-BKvKC-12.js} +1 -1
- langflow/frontend/assets/{index-COqjpsdy.js → index-BLROcaSz.js} +1 -1
- langflow/frontend/assets/{index-BRwkzs92.js → index-BNbWMmAV.js} +1 -1
- langflow/frontend/assets/{index-C_UkF-RJ.js → index-BOEf7-ty.js} +1 -1
- langflow/frontend/assets/index-BOYTBrh9.js +1 -0
- langflow/frontend/assets/{index-DDcpxWU4.js → index-BQB-iDYl.js} +1 -1
- langflow/frontend/assets/{index-Crq_yhkG.js → index-BRWNIt9F.js} +1 -1
- langflow/frontend/assets/{index-DmaQAn3K.js → index-BVHvIhT5.js} +1 -1
- langflow/frontend/assets/{index-Cs_jt3dj.js → index-BVtf6m9S.js} +1 -1
- langflow/frontend/assets/{index-T2jJOG85.js → index-BWq9GTzt.js} +1 -1
- langflow/frontend/assets/{index-Dz0r9Idb.js → index-BXMhmvTj.js} +1 -1
- langflow/frontend/assets/{index-eJwu5YEi.js → index-Ba3RTMXI.js} +1 -1
- langflow/frontend/assets/{index-xVx59Op-.js → index-Baka5dKE.js} +1 -1
- langflow/frontend/assets/{index-DnusMCK1.js → index-BbsND1Qg.js} +1 -1
- langflow/frontend/assets/index-BcgB3rXH.js +1 -0
- langflow/frontend/assets/{index-CmiRgF_-.js → index-BdIWbCEL.js} +1 -1
- langflow/frontend/assets/{index-BllNr21U.js → index-BdYgKk1d.js} +1 -1
- langflow/frontend/assets/{index-BIKbxmIh.js → index-BeNby7qF.js} +1 -1
- langflow/frontend/assets/{index-CUe1ivTn.js → index-BejHxU5W.js} +1 -1
- langflow/frontend/assets/{index-CVphnxXi.js → index-Bisa4IQF.js} +1 -1
- langflow/frontend/assets/{index-Cr2oy5K2.js → index-BjENqyKe.js} +1 -1
- langflow/frontend/assets/{index-CEn_71Wk.js → index-BlBl2tvQ.js} +1 -1
- langflow/frontend/assets/{index-DOb9c2bf.js → index-BnLT29qW.js} +1 -1
- langflow/frontend/assets/{index-BRizlHaN.js → index-BqUeOc7Y.js} +1 -1
- langflow/frontend/assets/{index-D7nFs6oq.js → index-BsBWP-Dh.js} +1 -1
- langflow/frontend/assets/{index-BlRTHXW5.js → index-BtJ2o21k.js} +1 -1
- langflow/frontend/assets/{index-AOX7bbjJ.js → index-BxWXWRmZ.js} +1 -1
- langflow/frontend/assets/{index-B20KmxhS.js → index-BxkZkBgQ.js} +1 -1
- langflow/frontend/assets/{index-DoFlaGDx.js → index-Bxml6wXu.js} +1 -1
- langflow/frontend/assets/{index-B9KRIJFi.js → index-ByFXr9Iq.js} +1 -1
- langflow/frontend/assets/{index-CY6LUi4V.js → index-C2Xd7UkR.js} +1 -1
- langflow/frontend/assets/index-C76aBV_h.js +1 -0
- langflow/frontend/assets/{index-9gkURvG2.js → index-C7V5U9yH.js} +1 -1
- langflow/frontend/assets/{index-BDmbsLY2.js → index-C7x9R_Yo.js} +1 -1
- langflow/frontend/assets/{index-DI0zAExi.js → index-C8KD3LPb.js} +1 -1
- langflow/frontend/assets/{index-DzDNhMMW.js → index-C9N80hP8.js} +1 -1
- langflow/frontend/assets/{index-6GWpsedd.js → index-CDFLVFB4.js} +1 -1
- langflow/frontend/assets/{index-pkOi9P45.js → index-CF4dtI6S.js} +1 -1
- langflow/frontend/assets/{index-CdwjD4IX.js → index-CG7cp0nD.js} +1 -1
- langflow/frontend/assets/{index-J0pvFqLk.js → index-CHFO5O4g.js} +1 -1
- langflow/frontend/assets/{index-5G402gB8.js → index-CJwYfDBz.js} +1 -1
- langflow/frontend/assets/{index-BzCjyHto.js → index-CMGZGIx_.js} +1 -1
- langflow/frontend/assets/{index-Bm7a2vMS.js → index-COL0eiWI.js} +1 -1
- langflow/frontend/assets/{index-JHCxbvlW.js → index-CWWo2zOA.js} +1 -1
- langflow/frontend/assets/{index-C7wDSVVH.js → index-C_1RBTul.js} +1 -1
- langflow/frontend/assets/{index-BIjUtp6d.js → index-Ccb5B8zG.js} +1 -1
- langflow/frontend/assets/{index-yIh6-LZT.js → index-Cd5zuUUK.js} +1 -1
- langflow/frontend/assets/{index-CPIdMJkX.js → index-CkQ-bJ4G.js} +1 -1
- langflow/frontend/assets/{index-TRyDa01A.js → index-CkSzjCqM.js} +1 -1
- langflow/frontend/assets/{index-CSRizl2S.js → index-CoUlHbtg.js} +1 -1
- langflow/frontend/assets/index-Cpgkb0Q3.js +1 -0
- langflow/frontend/assets/{index-Cp7Pmn03.js → index-CqDUqHfd.js} +1 -1
- langflow/frontend/assets/{index-CGVDXKtN.js → index-Ct9_T9ox.js} +1 -1
- langflow/frontend/assets/{index-BwlYjc56.js → index-CvQ0w8Pj.js} +1 -1
- langflow/frontend/assets/{index-DkJCCraf.js → index-CwIxqYlT.js} +1 -1
- langflow/frontend/assets/{index-Bgd7yLoW.js → index-Cx__T92e.js} +1 -1
- langflow/frontend/assets/{index-RveG4dl9.js → index-D-zkHcob.js} +1 -1
- langflow/frontend/assets/{index-DVV_etfW.js → index-D0HmkH0H.js} +1 -1
- langflow/frontend/assets/{index-CglSqvB5.js → index-D0s9f6Re.js} +1 -1
- langflow/frontend/assets/{index-J98sU-1p.js → index-D5PeCofu.js} +1 -1
- langflow/frontend/assets/{index-BJIsQS8D.js → index-D87Zw62M.js} +1 -1
- langflow/frontend/assets/{index-FYcoJPMP.js → index-D9eflZfP.js} +1 -1
- langflow/frontend/assets/{index-DJs6FoYC.js → index-DDNNv4C0.js} +1 -1
- langflow/frontend/assets/index-DHlEwAxb.js +1 -0
- langflow/frontend/assets/{index-DqDQk0Cu.js → index-DIqSyDVO.js} +1 -1
- langflow/frontend/assets/{index-DOI0ceS-.js → index-DK8vNpXK.js} +1 -1
- langflow/frontend/assets/{index-D29n5mus.js → index-DKEXZFUO.js} +1 -1
- langflow/frontend/assets/{index-dfaj9-hY.js → index-DPX6X_bw.js} +1 -1
- langflow/frontend/assets/{index-CgbINWS8.js → index-DS1EgA10.js} +1 -1
- langflow/frontend/assets/{index-C69gdJqw.js → index-DS9I4y48.js} +1 -1
- langflow/frontend/assets/{index-B2EmwqKj.js → index-DWkMJnbd.js} +1 -1
- langflow/frontend/assets/{index-CIYzjH2y.js → index-DWr_zPkx.js} +1 -1
- langflow/frontend/assets/{index-D-HTZ68O.js → index-DX7XsAcx.js} +1 -1
- langflow/frontend/assets/{index-Cq30cQcP.js → index-DZzbmg3J.js} +1 -1
- langflow/frontend/assets/{index-BZCt_UnJ.js → index-DasrI03Y.js} +1 -1
- langflow/frontend/assets/index-DdzVmJHE.js +1 -0
- langflow/frontend/assets/{index-DmvjdU1N.js → index-DhzEUXfr.js} +1 -1
- langflow/frontend/assets/{index-B_ytx_iA.js → index-DpJiH-Rk.js} +1 -1
- langflow/frontend/assets/{index-Cyk3aCmP.js → index-DpQKtcXu.js} +1 -1
- langflow/frontend/assets/{index-DrvRK4_i.js → index-Dpz3oBf5.js} +1 -1
- langflow/frontend/assets/{index-DF0oWRdd.js → index-DqSH4x-R.js} +1 -1
- langflow/frontend/assets/{index-DX_InNVT.js → index-DtJyCbzF.js} +1 -1
- langflow/frontend/assets/{index-B4AtFbkN.js → index-Du9aJK7m.js} +1 -1
- langflow/frontend/assets/{index-qXcoVIRo.js → index-DuAeoC-H.js} +1 -1
- langflow/frontend/assets/{index-D7Vx6mgS.js → index-DxIs8VSp.js} +1 -1
- langflow/frontend/assets/{index-U7J1YiWE.js → index-DyJDHm2D.js} +1 -1
- langflow/frontend/assets/{index-1MEYR1La.js → index-DzeIsaBm.js} +1 -1
- langflow/frontend/assets/{index-Cbwk3f-p.js → index-DztLFiip.js} +1 -1
- langflow/frontend/assets/{index-C_2G2ZqJ.js → index-GODbXlHC.js} +1 -1
- langflow/frontend/assets/{index-2vQdFIK_.js → index-G_U_kPAd.js} +1 -1
- langflow/frontend/assets/{index-DS4F_Phe.js → index-IFGgPiye.js} +1 -1
- langflow/frontend/assets/{index-5hW8VleF.js → index-LrMzDsq9.js} +1 -1
- langflow/frontend/assets/{index-L7FKc9QN.js → index-R7q8cAek.js} +1 -1
- langflow/frontend/assets/{index-BRE8A4Q_.js → index-Uq2ij_SS.js} +1 -1
- langflow/frontend/assets/{index-Bn4HAVDG.js → index-VHmUHUUU.js} +1 -1
- langflow/frontend/assets/{index-VO-pk-Hg.js → index-VZnN0P6C.js} +1 -1
- langflow/frontend/assets/{index-Dy7ehgeV.js → index-VcXZzovW.js} +1 -1
- langflow/frontend/assets/{index-DNS4La1f.js → index-Ym6gz0T6.js} +1 -1
- langflow/frontend/assets/{index-UI2ws3qp.js → index-ci4XHjbJ.js} +176 -176
- langflow/frontend/assets/{index-DlMAYATX.js → index-dkS0ek2S.js} +1 -1
- langflow/frontend/assets/{index-Dc0p1Oxl.js → index-hOkEW3JP.js} +1 -1
- langflow/frontend/assets/{index-KnS52ylc.js → index-js8ceOaP.js} +1 -1
- langflow/frontend/assets/{index-DtCsjX48.js → index-lKEJpUsF.js} +1 -1
- langflow/frontend/assets/{index-BO4fl1uU.js → index-mBjJYD9q.js} +1 -1
- langflow/frontend/assets/{index-C_K6Tof7.js → index-r1LZg-PY.js} +1 -1
- langflow/frontend/assets/index-rcdQpNcU.js +1 -0
- langflow/frontend/assets/{index-_3qag0I4.js → index-sS6XLk3j.js} +1 -1
- langflow/frontend/assets/{index-C6P0vvSP.js → index-tOy_uloT.js} +1 -1
- langflow/frontend/assets/lazyIconImports-Bh1TFfvH.js +2 -0
- langflow/frontend/assets/{use-post-add-user-Bt6vZvvT.js → use-post-add-user-HN0rRnhv.js} +1 -1
- langflow/frontend/index.html +1 -1
- langflow/initial_setup/starter_projects/Knowledge Ingestion.json +2 -2
- langflow/initial_setup/starter_projects/News Aggregator.json +19 -2
- langflow/initial_setup/starter_projects/Nvidia Remix.json +19 -2
- langflow/interface/initialize/loading.py +3 -1
- langflow/main.py +19 -2
- langflow/services/database/models/file/model.py +4 -2
- langflow/services/database/service.py +3 -1
- langflow/services/telemetry/schema.py +7 -0
- langflow/services/telemetry/service.py +25 -0
- langflow/services/tracing/service.py +14 -4
- {langflow_base_nightly-0.5.0.dev33.dist-info → langflow_base_nightly-0.5.0.dev35.dist-info}/METADATA +1 -1
- {langflow_base_nightly-0.5.0.dev33.dist-info → langflow_base_nightly-0.5.0.dev35.dist-info}/RECORD +170 -152
- langflow/frontend/assets/lazyIconImports-kvf_Kak2.js +0 -2
- {langflow_base_nightly-0.5.0.dev33.dist-info → langflow_base_nightly-0.5.0.dev35.dist-info}/WHEEL +0 -0
- {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
|
-
|
|
159
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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":
|
|
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
|
-
#
|
|
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(),
|
|
164
|
-
"icon": app,
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
239
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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) ->
|
|
272
|
-
"""Build the Composio
|
|
251
|
+
def _build_wrapper(self) -> Composio:
|
|
252
|
+
"""Build the Composio wrapper using new SDK.
|
|
273
253
|
|
|
274
254
|
Returns:
|
|
275
|
-
|
|
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
|
|
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."""
|