feat: update Composio components (#8868)
Co-authored-by: Uday-sidagana <uday.sidgana@gmail.com> Co-authored-by: Uday Sidagana <udaysidagana@Mac.lan> Co-authored-by: Edwin Jose <edwin.jose@datastax.com> Co-authored-by: Uday Sidagana <129588963+Uday-sidagana@users.noreply.github.com>
|
|
@ -66,8 +66,8 @@ dependencies = [
|
|||
"yfinance==0.2.50",
|
||||
"wolframalpha==5.1.3",
|
||||
"astra-assistants[tools]~=2.2.12",
|
||||
"composio-langchain==0.7.15",
|
||||
"composio-core==0.7.15",
|
||||
"composio-langchain==0.8.5",
|
||||
"composio==0.8.5",
|
||||
"spider-client==0.1.24",
|
||||
"nltk==3.9.1",
|
||||
"lark==1.2.2",
|
||||
|
|
@ -329,4 +329,4 @@ ignore_missing_imports = true
|
|||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
build-backend = "hatchling.build"
|
||||
|
|
@ -724,7 +724,7 @@ async def custom_component_update(
|
|||
field_value=code_request.field_value,
|
||||
field_name=code_request.field,
|
||||
)
|
||||
if "code" not in updated_build_config:
|
||||
if "code" not in updated_build_config or not updated_build_config.get("code", {}).get("value"):
|
||||
updated_build_config = add_code_field_to_build_config(updated_build_config, code_request.code)
|
||||
component_node["template"] = updated_build_config
|
||||
|
||||
|
|
|
|||
|
|
@ -9,16 +9,32 @@ if TYPE_CHECKING:
|
|||
from .github_composio import ComposioGitHubAPIComponent
|
||||
from .gmail_composio import ComposioGmailAPIComponent
|
||||
from .googlecalendar_composio import ComposioGoogleCalendarAPIComponent
|
||||
from .googlemeet_composio import ComposioGooglemeetAPIComponent
|
||||
from .googletasks_composio import ComposioGoogleTasksAPIComponent
|
||||
from .linear_composio import ComposioLinearAPIComponent
|
||||
from .outlook_composio import ComposioOutlookAPIComponent
|
||||
from .reddit_composio import ComposioRedditAPIComponent
|
||||
from .slack_composio import ComposioSlackAPIComponent
|
||||
from .slackbot_composio import ComposioSlackbotAPIComponent
|
||||
from .supabase_composio import ComposioSupabaseAPIComponent
|
||||
from .todoist_composio import ComposioTodoistAPIComponent
|
||||
from .youtube_composio import ComposioYoutubeAPIComponent
|
||||
|
||||
_dynamic_imports = {
|
||||
"ComposioAPIComponent": "composio_api",
|
||||
"ComposioGitHubAPIComponent": "github_composio",
|
||||
"ComposioGmailAPIComponent": "gmail_composio",
|
||||
"ComposioGoogleCalendarAPIComponent": "googlecalendar_composio",
|
||||
"ComposioGooglemeetAPIComponent": "googlemeet_composio",
|
||||
"ComposioOutlookAPIComponent": "outlook_composio",
|
||||
"ComposioSlackAPIComponent": "slack_composio",
|
||||
"ComposioGoogleTasksAPIComponent": "googletasks_composio",
|
||||
"ComposioLinearAPIComponent": "linear_composio",
|
||||
"ComposioRedditAPIComponent": "reddit_composio",
|
||||
"ComposioSlackbotAPIComponent": "slackbot_composio",
|
||||
"ComposioSupabaseAPIComponent": "supabase_composio",
|
||||
"ComposioTodoistAPIComponent": "todoist_composio",
|
||||
"ComposioYoutubeAPIComponent": "youtube_composio",
|
||||
}
|
||||
|
||||
__all__ = [
|
||||
|
|
@ -26,8 +42,16 @@ __all__ = [
|
|||
"ComposioGitHubAPIComponent",
|
||||
"ComposioGmailAPIComponent",
|
||||
"ComposioGoogleCalendarAPIComponent",
|
||||
"ComposioGoogleTasksAPIComponent",
|
||||
"ComposioGooglemeetAPIComponent",
|
||||
"ComposioLinearAPIComponent",
|
||||
"ComposioOutlookAPIComponent",
|
||||
"ComposioRedditAPIComponent",
|
||||
"ComposioSlackAPIComponent",
|
||||
"ComposioSlackbotAPIComponent",
|
||||
"ComposioSupabaseAPIComponent",
|
||||
"ComposioTodoistAPIComponent",
|
||||
"ComposioYoutubeAPIComponent",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from composio import Action, App
|
||||
from composio import Composio
|
||||
from composio_langchain import LangchainProvider
|
||||
|
||||
# Third-party imports
|
||||
from composio_langchain import ComposioToolSet
|
||||
from langchain_core.tools import Tool
|
||||
|
||||
# Local imports
|
||||
|
|
@ -69,27 +69,7 @@ class ComposioAPIComponent(LCToolComponent):
|
|||
Output(name="tools", display_name="Tools", method="build_tool"),
|
||||
]
|
||||
|
||||
def sanitize_action_name(self, action_name: str) -> str:
|
||||
# TODO: Maybe restore
|
||||
return action_name
|
||||
|
||||
# We want to use title case, and replace underscores with spaces
|
||||
sanitized_name = action_name.replace("_", " ").title()
|
||||
|
||||
# Now we want to remove everything from and including the first dot
|
||||
return sanitized_name.replace(self.tool_name.title() + " ", "")
|
||||
|
||||
def desanitize_action_name(self, action_name: str) -> str:
|
||||
# TODO: Maybe restore
|
||||
return action_name
|
||||
|
||||
# We want to reverse what we did above
|
||||
unsanitized_name = action_name.replace(" ", "_").upper()
|
||||
|
||||
# Append the tool_name to it at the beginning, followed by a dot, in all CAPS
|
||||
return f"{self.tool_name.upper()}_{unsanitized_name}"
|
||||
|
||||
def validate_tool(self, build_config: dict, field_value: Any, connected_app_names: list) -> dict:
|
||||
def validate_tool(self, build_config: dict, field_value: Any, tool_name: str | None = None) -> dict:
|
||||
# Get the index of the selected tool in the list of options
|
||||
selected_tool_index = next(
|
||||
(
|
||||
|
|
@ -108,35 +88,40 @@ class ComposioAPIComponent(LCToolComponent):
|
|||
build_config["actions"]["helper_text"] = ""
|
||||
build_config["actions"]["helper_text_metadata"] = {"icon": "Check", "variant": "success"}
|
||||
|
||||
# Get the list of actions available
|
||||
all_actions = list(Action.all())
|
||||
authenticated_actions = sorted(
|
||||
[
|
||||
action
|
||||
for action in all_actions
|
||||
if action.app.lower() in list(connected_app_names) and action.app.lower() == self.tool_name.lower()
|
||||
],
|
||||
key=lambda x: x.name,
|
||||
)
|
||||
try:
|
||||
composio = self._build_wrapper()
|
||||
current_tool = tool_name or getattr(self, "tool_name", None)
|
||||
if not current_tool:
|
||||
self.log("No tool name available for validate_tool")
|
||||
return build_config
|
||||
|
||||
toolkit_slug = current_tool.lower()
|
||||
|
||||
tools = composio.tools.get(user_id=self.entity_id, toolkits=[toolkit_slug])
|
||||
|
||||
authenticated_actions = []
|
||||
for tool in tools:
|
||||
if hasattr(tool, "name"):
|
||||
action_name = tool.name
|
||||
display_name = action_name.replace("_", " ").title()
|
||||
authenticated_actions.append({"name": action_name, "display_name": display_name})
|
||||
except (ValueError, ConnectionError, AttributeError) as e:
|
||||
self.log(f"Error getting actions for {current_tool or 'unknown tool'}: {e}")
|
||||
authenticated_actions = []
|
||||
|
||||
# Return the list of action names
|
||||
build_config["actions"]["options"] = [
|
||||
{
|
||||
"name": self.sanitize_action_name(action.name),
|
||||
"name": action["name"],
|
||||
}
|
||||
for action in authenticated_actions
|
||||
]
|
||||
|
||||
# Lastly, we need to show the actions field
|
||||
build_config["actions"]["show"] = True
|
||||
|
||||
return build_config
|
||||
|
||||
def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:
|
||||
# If the list of tools is not available, always update it
|
||||
if field_name == "api_key" or (self.api_key and not build_config["tool_name"]["options"]):
|
||||
if field_name == "api_key" and not field_value:
|
||||
# Reset the list of tools
|
||||
build_config["tool_name"]["options"] = []
|
||||
build_config["tool_name"]["value"] = ""
|
||||
|
||||
|
|
@ -147,113 +132,94 @@ class ComposioAPIComponent(LCToolComponent):
|
|||
|
||||
return build_config
|
||||
|
||||
# TODO: Re-enable dynamic tool list
|
||||
# Initialize the Composio ToolSet with your API key
|
||||
# toolset = ComposioToolSet(api_key=self.api_key)
|
||||
|
||||
# Get the entity (e.g., "default" for your user)
|
||||
# entity = toolset.get_entity(self.entity_id)
|
||||
|
||||
# Get all available apps
|
||||
# all_apps = entity.client.apps.get()
|
||||
|
||||
# Build an object with name, icon, link
|
||||
# Build the list of available tools
|
||||
build_config["tool_name"]["options"] = [
|
||||
{
|
||||
"name": app.title(), # TODO: Switch to app.name
|
||||
"icon": app, # TODO: Switch to app.name
|
||||
"name": app.title(),
|
||||
"icon": app,
|
||||
"link": (
|
||||
build_config["tool_name"]["options"][ind]["link"]
|
||||
if build_config["tool_name"]["options"]
|
||||
else ""
|
||||
),
|
||||
}
|
||||
# for app in sorted(all_apps, key=lambda x: x.name)
|
||||
for ind, app in enumerate(enabled_tools)
|
||||
]
|
||||
|
||||
return build_config
|
||||
|
||||
# Handle the click of the Tool Name connect button
|
||||
if field_name == "tool_name" and field_value:
|
||||
# Get the list of apps (tools) we have connected
|
||||
toolset = ComposioToolSet(api_key=self.api_key)
|
||||
connected_apps = [app for app in toolset.get_connected_accounts() if app.status == "ACTIVE"]
|
||||
composio = self._build_wrapper()
|
||||
|
||||
# Get the unique list of appName from the connected apps
|
||||
connected_app_names = [app.appName.lower() for app in connected_apps]
|
||||
|
||||
# Clear out the list of selected actions
|
||||
build_config["actions"]["show"] = True
|
||||
build_config["actions"]["options"] = []
|
||||
build_config["actions"]["value"] = ""
|
||||
|
||||
# Clear out any helper text
|
||||
build_config["tool_name"]["helper_text"] = ""
|
||||
build_config["tool_name"]["helper_text_metadata"] = {}
|
||||
|
||||
# If it's a dictionary, we need to do validation
|
||||
if isinstance(field_value, dict):
|
||||
# If the current field value is a dictionary, it means the user has selected a tool
|
||||
if "validate" not in field_value:
|
||||
return build_config
|
||||
|
||||
# Check if the selected tool is connected
|
||||
check_app = field_value["validate"].lower()
|
||||
|
||||
# If the tool selected is NOT what we are validating, return the build config
|
||||
if check_app != self.tool_name.lower():
|
||||
# Set the helper text and helper text metadata field of the actions now
|
||||
build_config["actions"]["helper_text"] = "Please connect before selecting actions."
|
||||
build_config["actions"]["helper_text_metadata"] = {
|
||||
"icon": "OctagonAlert",
|
||||
"variant": "destructive",
|
||||
}
|
||||
|
||||
return build_config
|
||||
|
||||
# Check if the tool is already validated
|
||||
if check_app not in connected_app_names:
|
||||
return build_config
|
||||
|
||||
# Validate the selected tool
|
||||
return self.validate_tool(build_config, field_value, connected_app_names)
|
||||
|
||||
# Check if the tool is already validated
|
||||
if field_value.lower() in connected_app_names:
|
||||
return self.validate_tool(build_config, field_value, connected_app_names)
|
||||
|
||||
# Get the entity (e.g., "default" for your user)
|
||||
entity = toolset.get_entity(id=self.entity_id)
|
||||
|
||||
# Set the metadata for the actions
|
||||
build_config["actions"]["helper_text_metadata"] = {"icon": "OctagonAlert", "variant": "destructive"}
|
||||
|
||||
# Get the index of the selected tool in the list of options
|
||||
selected_tool_index = next(
|
||||
(ind for ind, tool in enumerate(build_config["tool_name"]["options"]) if tool["name"] == field_value),
|
||||
None,
|
||||
current_tool_name = (
|
||||
field_value
|
||||
if isinstance(field_value, str)
|
||||
else field_value.get("validate")
|
||||
if isinstance(field_value, dict) and "validate" in field_value
|
||||
else getattr(self, "tool_name", None)
|
||||
)
|
||||
|
||||
# Initiate a GitHub connection and get the redirect URL
|
||||
try:
|
||||
connection_request = entity.initiate_connection(app_name=getattr(App, field_value.upper()))
|
||||
except Exception as _: # noqa: BLE001
|
||||
# Indicate that there was an error connecting to the tool
|
||||
build_config["tool_name"]["options"][selected_tool_index]["link"] = "error"
|
||||
build_config["tool_name"]["helper_text"] = f"Error connecting to {field_value}"
|
||||
build_config["tool_name"]["helper_text_metadata"] = {
|
||||
"icon": "OctagonAlert",
|
||||
"variant": "destructive",
|
||||
}
|
||||
|
||||
if not current_tool_name:
|
||||
self.log("No tool name available for connection check")
|
||||
return build_config
|
||||
|
||||
# Print the direct HTTP link for authentication
|
||||
build_config["tool_name"]["options"][selected_tool_index]["link"] = connection_request.redirectUrl
|
||||
try:
|
||||
toolkit_slug = current_tool_name.lower()
|
||||
|
||||
# Set the helper text and helper text metadata field of the actions now
|
||||
build_config["actions"]["helper_text"] = "Please connect before selecting actions."
|
||||
connection_list = composio.connected_accounts.list(
|
||||
user_ids=[self.entity_id], toolkit_slugs=[toolkit_slug]
|
||||
)
|
||||
|
||||
# Check for active connections
|
||||
has_active_connections = False
|
||||
if (
|
||||
connection_list
|
||||
and hasattr(connection_list, "items")
|
||||
and connection_list.items
|
||||
and isinstance(connection_list.items, list)
|
||||
and len(connection_list.items) > 0
|
||||
):
|
||||
for connection in connection_list.items:
|
||||
if getattr(connection, "status", None) == "ACTIVE":
|
||||
has_active_connections = True
|
||||
break
|
||||
|
||||
# Get the index of the selected tool in the list of options
|
||||
selected_tool_index = next(
|
||||
(
|
||||
ind
|
||||
for ind, tool in enumerate(build_config["tool_name"]["options"])
|
||||
if tool["name"] == current_tool_name.title()
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if has_active_connections:
|
||||
# User has active connection
|
||||
if selected_tool_index is not None:
|
||||
build_config["tool_name"]["options"][selected_tool_index]["link"] = "validated"
|
||||
|
||||
# If it's a validation request, validate the tool
|
||||
if (isinstance(field_value, dict) and "validate" in field_value) or isinstance(field_value, str):
|
||||
return self.validate_tool(build_config, field_value, current_tool_name)
|
||||
else:
|
||||
# No active connection - create OAuth connection
|
||||
try:
|
||||
connection = composio.toolkits.authorize(user_id=self.entity_id, toolkit=toolkit_slug)
|
||||
redirect_url = getattr(connection, "redirect_url", None)
|
||||
|
||||
if redirect_url and redirect_url.startswith(("http://", "https://")):
|
||||
if selected_tool_index is not None:
|
||||
build_config["tool_name"]["options"][selected_tool_index]["link"] = redirect_url
|
||||
elif selected_tool_index is not None:
|
||||
build_config["tool_name"]["options"][selected_tool_index]["link"] = "error"
|
||||
except (ValueError, ConnectionError, AttributeError) as e:
|
||||
self.log(f"Error creating OAuth connection: {e}")
|
||||
if selected_tool_index is not None:
|
||||
build_config["tool_name"]["options"][selected_tool_index]["link"] = "error"
|
||||
|
||||
except (ValueError, ConnectionError, AttributeError) as e:
|
||||
self.log(f"Error checking connection status: {e}")
|
||||
|
||||
return build_config
|
||||
|
||||
|
|
@ -263,16 +229,30 @@ class ComposioAPIComponent(LCToolComponent):
|
|||
Returns:
|
||||
Sequence[Tool]: List of configured Composio tools.
|
||||
"""
|
||||
composio_toolset = self._build_wrapper()
|
||||
return composio_toolset.get_tools(
|
||||
actions=[self.desanitize_action_name(action["name"]) for action in self.actions]
|
||||
)
|
||||
composio = self._build_wrapper()
|
||||
action_names = [action["name"] for action in self.actions]
|
||||
|
||||
def _build_wrapper(self) -> ComposioToolSet:
|
||||
"""Build the Composio toolset wrapper.
|
||||
# Get toolkits from action names
|
||||
toolkits = set()
|
||||
for action_name in action_names:
|
||||
if "_" in action_name:
|
||||
toolkit = action_name.split("_")[0].lower()
|
||||
toolkits.add(toolkit)
|
||||
|
||||
if not toolkits:
|
||||
return []
|
||||
|
||||
# Get all tools for the relevant toolkits
|
||||
all_tools = composio.tools.get(user_id=self.entity_id, toolkits=list(toolkits))
|
||||
|
||||
# Filter to only the specific actions we want using list comprehension
|
||||
return [tool for tool in all_tools if hasattr(tool, "name") and tool.name in action_names]
|
||||
|
||||
def _build_wrapper(self) -> Composio:
|
||||
"""Build the Composio wrapper using new SDK.
|
||||
|
||||
Returns:
|
||||
ComposioToolSet: The initialized toolset.
|
||||
Composio: The initialized Composio client.
|
||||
|
||||
Raises:
|
||||
ValueError: If the API key is not found or invalid.
|
||||
|
|
@ -281,7 +261,7 @@ class ComposioAPIComponent(LCToolComponent):
|
|||
if not self.api_key:
|
||||
msg = "Composio API Key is required"
|
||||
raise ValueError(msg)
|
||||
return ComposioToolSet(api_key=self.api_key, entity_id=self.entity_id)
|
||||
return Composio(api_key=self.api_key, provider=LangchainProvider())
|
||||
except ValueError as e:
|
||||
self.log(f"Error building Composio wrapper: {e}")
|
||||
msg = "Please provide a valid Composio API Key in the component settings"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
|
||||
class ComposioDropboxAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Dropbox"
|
||||
icon = "Dropbox"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "dropbox"
|
||||
|
||||
def set_default_tools(self):
|
||||
"""Set the default tools for Dropbox component."""
|
||||
|
|
@ -1,649 +1,11 @@
|
|||
import json
|
||||
from typing import Any
|
||||
|
||||
from composio import Action
|
||||
|
||||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
from langflow.inputs import (
|
||||
BoolInput,
|
||||
IntInput,
|
||||
MessageTextInput,
|
||||
)
|
||||
from langflow.logging import logger
|
||||
|
||||
|
||||
class ComposioGitHubAPIComponent(ComposioBaseComponent):
|
||||
"""GitHub API component for interacting with GitHub services."""
|
||||
|
||||
display_name: str = "GitHub"
|
||||
description: str = "GitHub API"
|
||||
icon = "Github"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "github"
|
||||
|
||||
# GitHub-specific actions
|
||||
_actions_data: dict = {
|
||||
"GITHUB_CREATE_A_PULL_REQUEST": {
|
||||
"display_name": "Create A Pull Request",
|
||||
"action_fields": [
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_owner",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_repo",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_title",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_head",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_head_repo",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_base",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_body",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_maintainer_can_modify",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_draft",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_issue",
|
||||
],
|
||||
},
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER": {
|
||||
"display_name": "Star A Repository",
|
||||
"action_fields": [
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_owner",
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_repo",
|
||||
],
|
||||
},
|
||||
"GITHUB_LIST_COMMITS": {
|
||||
"display_name": "List Commits",
|
||||
"action_fields": [
|
||||
"GITHUB_LIST_COMMITS_owner",
|
||||
"GITHUB_LIST_COMMITS_repo",
|
||||
"GITHUB_LIST_COMMITS_sha",
|
||||
"GITHUB_LIST_COMMITS_path",
|
||||
"GITHUB_LIST_COMMITS_author",
|
||||
"GITHUB_LIST_COMMITS_committer",
|
||||
"GITHUB_LIST_COMMITS_since",
|
||||
"GITHUB_LIST_COMMITS_until",
|
||||
"GITHUB_LIST_COMMITS_per_page",
|
||||
"GITHUB_LIST_COMMITS_page",
|
||||
],
|
||||
},
|
||||
"GITHUB_GET_A_PULL_REQUEST": {
|
||||
"display_name": "Get A Pull Request",
|
||||
"action_fields": [
|
||||
"GITHUB_GET_A_PULL_REQUEST_owner",
|
||||
"GITHUB_GET_A_PULL_REQUEST_repo",
|
||||
"GITHUB_GET_A_PULL_REQUEST_pull_number",
|
||||
],
|
||||
},
|
||||
"GITHUB_CREATE_AN_ISSUE": {
|
||||
"display_name": "Create An Issue",
|
||||
"action_fields": [
|
||||
"GITHUB_CREATE_AN_ISSUE_owner",
|
||||
"GITHUB_CREATE_AN_ISSUE_repo",
|
||||
"GITHUB_CREATE_AN_ISSUE_title",
|
||||
"GITHUB_CREATE_AN_ISSUE_body",
|
||||
"GITHUB_CREATE_AN_ISSUE_assignee",
|
||||
"GITHUB_CREATE_AN_ISSUE_milestone",
|
||||
"GITHUB_CREATE_AN_ISSUE_labels",
|
||||
"GITHUB_CREATE_AN_ISSUE_assignees",
|
||||
],
|
||||
},
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES": {
|
||||
"display_name": "List Repository Issues",
|
||||
"action_fields": [
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_owner",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_repo",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_milestone",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_state",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_assignee",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_creator",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_mentioned",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_labels",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_sort",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_direction",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_since",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_per_page",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_page",
|
||||
],
|
||||
},
|
||||
"GITHUB_LIST_BRANCHES": {
|
||||
"display_name": "List Branches",
|
||||
"action_fields": [
|
||||
"GITHUB_LIST_BRANCHES_owner",
|
||||
"GITHUB_LIST_BRANCHES_repo",
|
||||
"GITHUB_LIST_BRANCHES_protected",
|
||||
"GITHUB_LIST_BRANCHES_per_page",
|
||||
"GITHUB_LIST_BRANCHES_page",
|
||||
],
|
||||
},
|
||||
"GITHUB_LIST_PULL_REQUESTS": {
|
||||
"display_name": "List Pull Requests",
|
||||
"action_fields": [
|
||||
"GITHUB_LIST_PULL_REQUESTS_owner",
|
||||
"GITHUB_LIST_PULL_REQUESTS_repo",
|
||||
"GITHUB_LIST_PULL_REQUESTS_state",
|
||||
"GITHUB_LIST_PULL_REQUESTS_head",
|
||||
"GITHUB_LIST_PULL_REQUESTS_base",
|
||||
"GITHUB_LIST_PULL_REQUESTS_sort",
|
||||
"GITHUB_LIST_PULL_REQUESTS_direction",
|
||||
"GITHUB_LIST_PULL_REQUESTS_per_page",
|
||||
"GITHUB_LIST_PULL_REQUESTS_page",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
_all_fields = {field for action_data in _actions_data.values() for field in action_data["action_fields"]}
|
||||
_bool_variables = {
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_maintainer_can_modify",
|
||||
"GITHUB_CREATE_A_PULL_REQUEST_draft",
|
||||
"GITHUB_LIST_BRANCHES_protected",
|
||||
}
|
||||
|
||||
inputs = [
|
||||
*ComposioBaseComponent._base_inputs,
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_AN_ISSUE_owner",
|
||||
display_name="Owner",
|
||||
info="The account owner of the repository. The name is not case sensitive.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_AN_ISSUE_repo",
|
||||
display_name="Repo",
|
||||
info="The name of the repository. The name is not case sensitive. ",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_AN_ISSUE_title",
|
||||
display_name="Title",
|
||||
info="The title of the issue.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_AN_ISSUE_body",
|
||||
display_name="Body",
|
||||
info="The contents of the issue.",
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_AN_ISSUE_assignee",
|
||||
display_name="Assignee",
|
||||
info="Login for the user that this issue should be assigned to. _NOTE: Only users with push access can set the assignee for new issues. The assignee is silently dropped otherwise. **This field is deprecated.**_ ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_AN_ISSUE_milestone",
|
||||
display_name="Milestone",
|
||||
info="Milestone",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_AN_ISSUE_labels",
|
||||
display_name="Labels",
|
||||
info="Labels to associate with this issue. _NOTE: Only users with push access can set labels for new issues. Labels are silently dropped otherwise._ ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_AN_ISSUE_assignees",
|
||||
display_name="Assignees",
|
||||
info="Logins for Users to assign to this issue. _NOTE: Only users with push access can set assignees for new issues. Assignees are silently dropped otherwise._ ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_PULL_REQUESTS_owner",
|
||||
display_name="Owner",
|
||||
info="The account owner of the repository. The name is not case sensitive.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_PULL_REQUESTS_repo",
|
||||
display_name="Repo",
|
||||
info="The name of the repository. The name is not case sensitive. ",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_PULL_REQUESTS_state",
|
||||
display_name="State",
|
||||
info="Either `open`, `closed`, or `all` to filter by state.",
|
||||
show=False,
|
||||
value="open",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_PULL_REQUESTS_head",
|
||||
display_name="Head",
|
||||
info="Filter pulls by head user or head organization and branch name in the format of `user:ref-name` or `organization:ref-name`. For example: `github:new-script-format` or `octocat:test-branch`. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_PULL_REQUESTS_base",
|
||||
display_name="Base",
|
||||
info="Filter pulls by base branch name. Example: `gh-pages`.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_PULL_REQUESTS_sort",
|
||||
display_name="Sort",
|
||||
info="What to sort results by. `popularity` will sort by the number of comments. `long-running` will sort by date created and will limit the results to pull requests that have been open for more than a month and have had activity within the past month. ", # noqa: E501
|
||||
show=False,
|
||||
value="created",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_PULL_REQUESTS_direction",
|
||||
display_name="Direction",
|
||||
info="The direction of the sort. Default: `desc` when sort is `created` or sort is not specified, otherwise `asc`. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_LIST_PULL_REQUESTS_per_page",
|
||||
display_name="Per Page",
|
||||
info="The number of results per page (max 100)",
|
||||
show=False,
|
||||
value=1,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_LIST_PULL_REQUESTS_page",
|
||||
display_name="Page",
|
||||
info="The page number of the results to fetch",
|
||||
show=False,
|
||||
value=1,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_owner",
|
||||
display_name="Owner",
|
||||
info="The account owner of the repository. The name is not case sensitive.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_repo",
|
||||
display_name="Repo",
|
||||
info="The name of the repository. The name is not case sensitive. ",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_title",
|
||||
display_name="Title",
|
||||
info="The title of the new pull request. Required unless `issue` is specified.",
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_head",
|
||||
display_name="Head",
|
||||
info="The name of the branch where your changes are implemented. For cross-repository pull requests in the same network, namespace `head` with a user like this: `username:branch`. ", # noqa: E501
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_head_repo",
|
||||
display_name="Head Repo",
|
||||
info="The name of the repository where the changes in the pull request were made. This field is required for cross-repository pull requests if both repositories are owned by the same organization. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_base",
|
||||
display_name="Base",
|
||||
info="The name of the branch you want the changes pulled into. This should be an existing branch on the current repository. You cannot submit a pull request to one repository that requests a merge to a base of another repository. ", # noqa: E501
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_body",
|
||||
display_name="Body",
|
||||
info="The contents of the pull request.",
|
||||
show=False,
|
||||
),
|
||||
BoolInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_maintainer_can_modify",
|
||||
display_name="Maintainer Can Modify",
|
||||
info="Indicates whether maintainers can modify the pull request",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_draft",
|
||||
display_name="Draft",
|
||||
info="Indicates whether the pull request is a draft",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_CREATE_A_PULL_REQUEST_issue",
|
||||
display_name="Issue",
|
||||
info="An issue in the repository to convert to a pull request. The issue title, body, and comments will become the title, body, and comments on the new pull request. Required unless `title` is specified. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_owner",
|
||||
display_name="Owner",
|
||||
info="The account owner of the repository. The name is not case sensitive.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_repo",
|
||||
display_name="Repo",
|
||||
info="The name of the repository. The name is not case sensitive. ",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_milestone",
|
||||
display_name="Milestone",
|
||||
info="If an `integer` is passed, it should refer to a milestone by its `number` field. If the string `*` is passed, issues with any milestone are accepted. If the string `none` is passed, issues without milestones are returned. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_state",
|
||||
display_name="State",
|
||||
info="Indicates the state of the issues to return.",
|
||||
show=False,
|
||||
value="open",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_assignee",
|
||||
display_name="Assignee",
|
||||
info="Can be the name of a user. Pass in `none` for issues with no assigned user, and `*` for issues assigned to any user. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_creator",
|
||||
display_name="Creator",
|
||||
info="The user that created the issue.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_mentioned",
|
||||
display_name="Mentioned",
|
||||
info="A user that's mentioned in the issue.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_labels",
|
||||
display_name="Labels",
|
||||
info="A list of comma separated label names. Example: `bug,ui,@high`",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_sort",
|
||||
display_name="Sort",
|
||||
info="What to sort results by",
|
||||
show=False,
|
||||
value="created",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_direction",
|
||||
display_name="Direction",
|
||||
info="The direction to sort the results by",
|
||||
show=False,
|
||||
value="desc",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_since",
|
||||
display_name="Since",
|
||||
info="Only show results that were last updated after the given time. This is a timestamp in ISO 8601 (https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_per_page",
|
||||
display_name="Per Page",
|
||||
info="The number of results per page (max 100)",
|
||||
show=False,
|
||||
value=1,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_LIST_REPOSITORY_ISSUES_page",
|
||||
display_name="Page",
|
||||
info="The page number of the results to fetch",
|
||||
show=False,
|
||||
value=1,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_BRANCHES_owner",
|
||||
display_name="Owner",
|
||||
info="The account owner of the repository. The name is not case sensitive.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_BRANCHES_repo",
|
||||
display_name="Repo",
|
||||
info="The name of the repository. The name is not case sensitive. ",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GITHUB_LIST_BRANCHES_protected",
|
||||
display_name="Protected",
|
||||
info="Setting to `true` returns only protected branches. When set to `false`, only unprotected branches are returned. Omitting this parameter returns all branches", # noqa: E501
|
||||
show=False,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_LIST_BRANCHES_per_page",
|
||||
display_name="Per Page",
|
||||
info="The number of results per page (max 100)",
|
||||
show=False,
|
||||
value=30,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_LIST_BRANCHES_page",
|
||||
display_name="Page",
|
||||
info="The page number of the results to fetch",
|
||||
show=False,
|
||||
value=1,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_owner",
|
||||
display_name="Owner",
|
||||
info="The account owner of the repository. The name is not case sensitive.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_repo",
|
||||
display_name="Repo",
|
||||
info="The name of the repository. The name is not case sensitive.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_GET_A_PULL_REQUEST_owner",
|
||||
display_name="Owner",
|
||||
info="The account owner of the repository. The name is not case sensitive.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_GET_A_PULL_REQUEST_repo",
|
||||
display_name="Repo",
|
||||
info="The name of the repository. The name is not case sensitive. ",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_GET_A_PULL_REQUEST_pull_number",
|
||||
display_name="Pull Number",
|
||||
info="The number that identifies the pull request.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_COMMITS_owner",
|
||||
display_name="Owner",
|
||||
info="The account owner of the repository. The name is not case sensitive.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_COMMITS_repo",
|
||||
display_name="Repo",
|
||||
info="The name of the repository. The name is not case sensitive. ",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_COMMITS_sha",
|
||||
display_name="SHA",
|
||||
info="SHA or branch to start listing commits from. Default: the repository's default branch (usually `main`). ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_COMMITS_path",
|
||||
display_name="Path",
|
||||
info="Only commits containing this file path will be returned.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_COMMITS_author",
|
||||
display_name="Author",
|
||||
info="GitHub username or email address to use to filter by commit author.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_COMMITS_committer",
|
||||
display_name="Committer",
|
||||
info="GitHub username or email address to use to filter by commit committer.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_COMMITS_since",
|
||||
display_name="Since",
|
||||
info="Only show results that were last updated after the given time. This is a timestamp in ISO 8601 (https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GITHUB_LIST_COMMITS_until",
|
||||
display_name="Until",
|
||||
info="Only commits before this date will be returned. This is a timestamp in ISO 8601 (https://en.wikipedia.org/wiki/ISO_8601) format: `YYYY-MM-DDTHH:MM:SSZ`. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_LIST_COMMITS_per_page",
|
||||
display_name="Per Page",
|
||||
info="The number of results per page (max 100)",
|
||||
show=False,
|
||||
value=1,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GITHUB_LIST_COMMITS_page",
|
||||
display_name="Page",
|
||||
info="The page number of the results to fetch",
|
||||
show=False,
|
||||
value=1,
|
||||
advanced=True,
|
||||
),
|
||||
]
|
||||
|
||||
def execute_action(self):
|
||||
"""Execute action and return response as Message."""
|
||||
toolset = self._build_wrapper()
|
||||
|
||||
try:
|
||||
self._build_action_maps()
|
||||
# Get the display name from the action list
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else self.action
|
||||
# Use the display_to_key_map to get the action key
|
||||
action_key = self._display_to_key_map.get(display_name)
|
||||
if not action_key:
|
||||
msg = f"Invalid action: {display_name}"
|
||||
raise ValueError(msg)
|
||||
|
||||
enum_name = getattr(Action, action_key)
|
||||
params = {}
|
||||
if action_key in self._actions_data:
|
||||
for field in self._actions_data[action_key]["action_fields"]:
|
||||
value = getattr(self, field)
|
||||
|
||||
if value is None or value == "":
|
||||
continue
|
||||
|
||||
if (
|
||||
field
|
||||
in [
|
||||
"GITHUB_CREATE_AN_ISSUE_labels",
|
||||
"GITHUB_CREATE_AN_ISSUE_assignees",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_labels",
|
||||
]
|
||||
and value
|
||||
):
|
||||
value = [item.strip() for item in value.split(",")]
|
||||
|
||||
if field in self._bool_variables:
|
||||
value = bool(value)
|
||||
|
||||
param_name = field.replace(action_key + "_", "")
|
||||
params[param_name] = value
|
||||
|
||||
result = toolset.execute_action(
|
||||
action=enum_name,
|
||||
params=params,
|
||||
)
|
||||
if not result.get("successful"):
|
||||
try:
|
||||
message_str = result.get("error", {})
|
||||
error_message = message_str.split("`")[1]
|
||||
error_msg_json = json.loads(error_message)
|
||||
except (IndexError, json.JSONDecodeError):
|
||||
return {"error": str(message_str)}
|
||||
return {
|
||||
"code": error_msg_json.get("status"),
|
||||
"message": error_msg_json.get("message"),
|
||||
"documentation_url": error_msg_json.get("documentation_url"),
|
||||
}
|
||||
|
||||
result_data = result.get("data", [])
|
||||
if (
|
||||
len(result_data) != 1
|
||||
and not self._actions_data.get(action_key, {}).get("result_field")
|
||||
and self._actions_data.get(action_key, {}).get("get_result_field")
|
||||
):
|
||||
msg = f"Expected a dict with a single key, got {len(result_data)} keys: {result_data.keys()}"
|
||||
raise ValueError(msg)
|
||||
if isinstance(result_data.get("details"), list):
|
||||
return result_data.get("details")
|
||||
return result_data # noqa: TRY300
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing action: {e}")
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else str(self.action)
|
||||
msg = f"Failed to execute {display_name}: {e!s}"
|
||||
raise ValueError(msg) from e
|
||||
|
||||
def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:
|
||||
return super().update_build_config(build_config, field_value, field_name)
|
||||
|
||||
def set_default_tools(self):
|
||||
self._default_tools = {
|
||||
self.sanitize_action_name("GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER").replace(" ", "-"),
|
||||
self.sanitize_action_name("GITHUB_CREATE_A_PULL_REQUEST").replace(" ", "-"),
|
||||
}
|
||||
"""Set the default tools for GitHub component."""
|
||||
|
|
|
|||
|
|
@ -1,406 +1,38 @@
|
|||
import json
|
||||
from typing import Any
|
||||
|
||||
from composio import Action
|
||||
|
||||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
from langflow.inputs.inputs import (
|
||||
BoolInput,
|
||||
FileInput,
|
||||
IntInput,
|
||||
MessageTextInput,
|
||||
)
|
||||
from langflow.logging import logger
|
||||
|
||||
|
||||
class ComposioGmailAPIComponent(ComposioBaseComponent):
|
||||
"""Gmail API component for interacting with Gmail services."""
|
||||
|
||||
display_name: str = "Gmail"
|
||||
name = "GmailAPI"
|
||||
icon = "Google"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "gmail"
|
||||
|
||||
# Gmail-specific actions
|
||||
_actions_data: dict = {
|
||||
"GMAIL_SEND_EMAIL": {
|
||||
"display_name": "Send Email",
|
||||
"action_fields": [
|
||||
"recipient_email",
|
||||
"subject",
|
||||
"body",
|
||||
"cc",
|
||||
"bcc",
|
||||
"is_html",
|
||||
"gmail_user_id",
|
||||
"attachment",
|
||||
],
|
||||
},
|
||||
"GMAIL_FETCH_EMAILS": {
|
||||
"display_name": "Fetch Emails",
|
||||
"action_fields": [
|
||||
"gmail_user_id",
|
||||
"max_results",
|
||||
"query",
|
||||
"page_token",
|
||||
"label_ids",
|
||||
"include_spam_trash",
|
||||
],
|
||||
"get_result_field": True,
|
||||
"result_field": "messages",
|
||||
},
|
||||
"GMAIL_GET_PROFILE": {
|
||||
"display_name": "Get User Profile",
|
||||
"action_fields": ["gmail_user_id"],
|
||||
},
|
||||
"GMAIL_FETCH_MESSAGE_BY_MESSAGE_ID": {
|
||||
"display_name": "Get Email By ID",
|
||||
"action_fields": ["message_id", "gmail_user_id", "format"],
|
||||
"get_result_field": False,
|
||||
},
|
||||
"GMAIL_CREATE_EMAIL_DRAFT": {
|
||||
"display_name": "Create Draft Email",
|
||||
"action_fields": [
|
||||
"recipient_email",
|
||||
"subject",
|
||||
"body",
|
||||
"cc",
|
||||
"bcc",
|
||||
"is_html",
|
||||
"attachment",
|
||||
"gmail_user_id",
|
||||
],
|
||||
},
|
||||
"GMAIL_FETCH_MESSAGE_BY_THREAD_ID": {
|
||||
"display_name": "Get Message By Thread ID",
|
||||
"action_fields": ["thread_id", "page_token", "gmail_user_id"],
|
||||
"get_result_field": False,
|
||||
},
|
||||
"GMAIL_LIST_THREADS": {
|
||||
"display_name": "List Email Threads",
|
||||
"action_fields": ["max_results", "query", "gmail_user_id", "page_token"],
|
||||
"get_result_field": True,
|
||||
"result_field": "threads",
|
||||
},
|
||||
"GMAIL_REPLY_TO_THREAD": {
|
||||
"display_name": "Reply To Thread",
|
||||
"action_fields": ["thread_id", "message_body", "recipient_email", "gmail_user_id", "cc", "bcc", "is_html"],
|
||||
},
|
||||
"GMAIL_LIST_LABELS": {
|
||||
"display_name": "List Email Labels",
|
||||
"action_fields": ["gmail_user_id"],
|
||||
"get_result_field": True,
|
||||
"result_field": "labels",
|
||||
},
|
||||
"GMAIL_CREATE_LABEL": {
|
||||
"display_name": "Create Email Label",
|
||||
"action_fields": ["label_name", "label_list_visibility", "message_list_visibility", "gmail_user_id"],
|
||||
},
|
||||
"GMAIL_GET_PEOPLE": {
|
||||
"display_name": "Get Contacts",
|
||||
"action_fields": ["resource_name", "person_fields"],
|
||||
"get_result_field": True,
|
||||
"result_field": "people_data",
|
||||
},
|
||||
"GMAIL_REMOVE_LABEL": {
|
||||
"display_name": "Delete Email Label",
|
||||
"action_fields": ["label_id", "gmail_user_id"],
|
||||
"get_result_field": False,
|
||||
},
|
||||
"GMAIL_GET_ATTACHMENT": {
|
||||
"display_name": "Get Attachment",
|
||||
"action_fields": ["message_id", "attachment_id", "file_name", "gmail_user_id"],
|
||||
},
|
||||
}
|
||||
_all_fields = {field for action_data in _actions_data.values() for field in action_data["action_fields"]}
|
||||
_bool_variables = {"is_html", "include_spam_trash"}
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.post_processors = {
|
||||
"GMAIL_SEND_EMAIL": self._process_send_email_response,
|
||||
"GMAIL_FETCH_EMAILS": self._process_fetch_emails_response,
|
||||
}
|
||||
|
||||
# Combine base inputs with Gmail-specific inputs
|
||||
inputs = [
|
||||
*ComposioBaseComponent._base_inputs,
|
||||
# Email composition fields
|
||||
MessageTextInput(
|
||||
name="recipient_email",
|
||||
display_name="Recipient Email",
|
||||
info="Email address of the recipient",
|
||||
show=False,
|
||||
required=True,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="subject",
|
||||
display_name="Subject",
|
||||
info="Subject of the email",
|
||||
show=False,
|
||||
required=True,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="body",
|
||||
display_name="Body",
|
||||
required=True,
|
||||
info="Content of the email",
|
||||
show=False,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="cc",
|
||||
display_name="CC",
|
||||
info="Email addresses to CC (Carbon Copy) in the email, separated by commas",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="bcc",
|
||||
display_name="BCC",
|
||||
info="Email addresses to BCC (Blind Carbon Copy) in the email, separated by commas",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="is_html",
|
||||
display_name="Is HTML",
|
||||
info="Specify whether the email body contains HTML content (true/false)",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
# Email retrieval and management fields
|
||||
MessageTextInput(
|
||||
name="gmail_user_id",
|
||||
display_name="User ID",
|
||||
info="The user's email address or 'me' for the authenticated user",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="max_results",
|
||||
display_name="Max Results",
|
||||
required=True,
|
||||
info="Maximum number of emails to be returned",
|
||||
show=False,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="message_id",
|
||||
display_name="Message ID",
|
||||
info="The ID of the specific email message",
|
||||
show=False,
|
||||
required=True,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="thread_id",
|
||||
display_name="Thread ID",
|
||||
info="The ID of the email thread",
|
||||
show=False,
|
||||
required=True,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="query",
|
||||
display_name="Query",
|
||||
info="Search query to filter emails (e.g., 'from:someone@email.com' or 'subject:hello')",
|
||||
show=False,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="message_body",
|
||||
display_name="Message Body",
|
||||
info="The body content of the message to be sent",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
# Label management fields
|
||||
MessageTextInput(
|
||||
name="label_name",
|
||||
display_name="Label Name",
|
||||
info="Name of the Gmail label to create, modify, or filter by",
|
||||
show=False,
|
||||
required=True,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="label_id",
|
||||
display_name="Label ID",
|
||||
info="The ID of the Gmail label",
|
||||
show=False,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="label_ids",
|
||||
display_name="Label Ids",
|
||||
info="Comma-separated list of label IDs to filter messages",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="label_list_visibility",
|
||||
display_name="Label List Visibility",
|
||||
info="The visibility of the label in the label list in the Gmail web interface",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="message_list_visibility",
|
||||
display_name="Message List Visibility",
|
||||
info="The visibility of the label in the message list in the Gmail web interface",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
# Pagination and filtering
|
||||
MessageTextInput(
|
||||
name="page_token",
|
||||
display_name="Page Token",
|
||||
info="Token for retrieving the next page of results",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="include_spam_trash",
|
||||
display_name="Include messages from Spam/Trash",
|
||||
info="Include messages from SPAM and TRASH in the results",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="format",
|
||||
display_name="Format",
|
||||
info="The format to return the message in. Possible values: minimal, full, raw, metadata",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
# Contact management fields
|
||||
MessageTextInput(
|
||||
name="resource_name",
|
||||
display_name="Resource Name",
|
||||
info="The resource name of the person to provide information about",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="person_fields",
|
||||
display_name="Person fields",
|
||||
info="Fields to return for the person. Multiple fields can be specified by separating them with commas",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
# Attachment handling
|
||||
MessageTextInput(
|
||||
name="attachment_id",
|
||||
display_name="Attachment ID",
|
||||
info="Id of the attachment",
|
||||
show=False,
|
||||
required=True,
|
||||
advanced=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="file_name",
|
||||
display_name="File name",
|
||||
info="File name of the attachment file",
|
||||
show=False,
|
||||
required=True,
|
||||
advanced=False,
|
||||
),
|
||||
FileInput(
|
||||
name="attachment",
|
||||
display_name="Add Attachment",
|
||||
file_types=[
|
||||
"csv",
|
||||
"txt",
|
||||
"doc",
|
||||
"docx",
|
||||
"xls",
|
||||
"xlsx",
|
||||
"pdf",
|
||||
"png",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"gif",
|
||||
"zip",
|
||||
"rar",
|
||||
"ppt",
|
||||
"pptx",
|
||||
],
|
||||
info="Add an attachment",
|
||||
show=False,
|
||||
),
|
||||
]
|
||||
def _process_send_email_response(self, raw_data):
|
||||
"""Post-processor for GMAIL_SEND_EMAIL action."""
|
||||
if isinstance(raw_data, dict):
|
||||
response_data = raw_data.get("response_data", raw_data)
|
||||
|
||||
def execute_action(self):
|
||||
"""Execute action and return response as Message."""
|
||||
toolset = self._build_wrapper()
|
||||
return {
|
||||
"message_id": response_data.get("id"),
|
||||
"thread_id": response_data.get("threadId"),
|
||||
"label_ids": response_data.get("labelIds", []),
|
||||
}
|
||||
return raw_data
|
||||
|
||||
try:
|
||||
self._build_action_maps()
|
||||
# Get the display name from the action list
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else self.action
|
||||
# Use the display_to_key_map to get the action key
|
||||
action_key = self._display_to_key_map.get(display_name)
|
||||
if not action_key:
|
||||
msg = f"Invalid action: {display_name}"
|
||||
raise ValueError(msg)
|
||||
|
||||
enum_name = getattr(Action, action_key)
|
||||
params = {}
|
||||
if action_key in self._actions_data:
|
||||
for field in self._actions_data[action_key]["action_fields"]:
|
||||
value = getattr(self, field)
|
||||
|
||||
if value is None or value == "":
|
||||
continue
|
||||
|
||||
if field in ["cc", "bcc", "label_ids"] and value:
|
||||
value = [item.strip() for item in value.split(",")]
|
||||
|
||||
if field in self._bool_variables:
|
||||
value = bool(value)
|
||||
|
||||
params[field] = value
|
||||
|
||||
if params.get("gmail_user_id"):
|
||||
params["user_id"] = params.pop("gmail_user_id")
|
||||
|
||||
result = toolset.execute_action(
|
||||
action=enum_name,
|
||||
params=params,
|
||||
)
|
||||
if not result.get("successful"):
|
||||
message_str = result.get("data", {}).get("message", "{}")
|
||||
try:
|
||||
error_data = json.loads(message_str).get("error", {})
|
||||
except json.JSONDecodeError:
|
||||
error_data = {"error": "Failed to get exact error details"}
|
||||
return {
|
||||
"code": error_data.get("code"),
|
||||
"message": error_data.get("message"),
|
||||
"errors": error_data.get("errors", []),
|
||||
"status": error_data.get("status"),
|
||||
}
|
||||
|
||||
result_data = result.get("data", {})
|
||||
actions_data = self._actions_data.get(action_key, {})
|
||||
# If 'get_result_field' is True and 'result_field' is specified, extract the data
|
||||
# using 'result_field'. Otherwise, fall back to the entire 'data' field in the response.
|
||||
if actions_data.get("get_result_field") and actions_data.get("result_field"):
|
||||
result_data = result_data.get(actions_data.get("result_field"), result.get("data", []))
|
||||
if len(result_data) != 1 and not actions_data.get("result_field") and actions_data.get("get_result_field"):
|
||||
msg = f"Expected a dict with a single key, got {len(result_data)} keys: {result_data.keys()}"
|
||||
raise ValueError(msg)
|
||||
return result_data # noqa: TRY300
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing action: {e}")
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else str(self.action)
|
||||
msg = f"Failed to execute {display_name}: {e!s}"
|
||||
raise ValueError(msg) from e
|
||||
|
||||
def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:
|
||||
return super().update_build_config(build_config, field_value, field_name)
|
||||
def _process_fetch_emails_response(self, raw_data):
|
||||
"""Post-processor for GMAIL_FETCH_EMAILS action."""
|
||||
if isinstance(raw_data, dict):
|
||||
messages = raw_data.get("messages", [])
|
||||
if messages:
|
||||
return messages
|
||||
return raw_data
|
||||
|
||||
def set_default_tools(self):
|
||||
self._default_tools = {
|
||||
"GMAIL_SEND_EMAIL",
|
||||
"GMAIL_FETCH_EMAILS",
|
||||
}
|
||||
"""Set the default tools for Gmail component."""
|
||||
|
|
|
|||
|
|
@ -1,787 +1,11 @@
|
|||
from typing import Any
|
||||
|
||||
from composio import Action
|
||||
|
||||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
from langflow.inputs import (
|
||||
BoolInput,
|
||||
IntInput,
|
||||
MessageTextInput,
|
||||
)
|
||||
from langflow.logging import logger
|
||||
|
||||
|
||||
class ComposioGoogleCalendarAPIComponent(ComposioBaseComponent):
|
||||
"""Google Calendar API component for interacting with Google Calendar services."""
|
||||
|
||||
display_name: str = "Google Calendar"
|
||||
description: str = "Google Calendar API"
|
||||
icon = "Googlecalendar"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "googlecalendar"
|
||||
|
||||
_actions_data: dict = {
|
||||
"GOOGLECALENDAR_UPDATE_EVENT": {
|
||||
"display_name": "Update Google Event",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_description",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_eventType",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_create_meeting_room",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_guestsCanSeeOtherGuests",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_guestsCanInviteOthers",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_location",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_summary",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_transparency",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_visibility",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_timezone",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_recurrence",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_guests_can_modify",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_attendees",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_send_updates",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_start_datetime",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_event_duration_hour",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_event_duration_minutes",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_calendar_id",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_event_id",
|
||||
],
|
||||
},
|
||||
"GOOGLECALENDAR_REMOVE_ATTENDEE": {
|
||||
"display_name": "Remove Attendee From Event",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_REMOVE_ATTENDEE_calendar_id",
|
||||
"GOOGLECALENDAR_REMOVE_ATTENDEE_event_id",
|
||||
"GOOGLECALENDAR_REMOVE_ATTENDEE_attendee_email",
|
||||
],
|
||||
},
|
||||
"GOOGLECALENDAR_GET_CURRENT_DATE_TIME": {
|
||||
"display_name": "Get Current Date And Time",
|
||||
"action_fields": ["GOOGLECALENDAR_GET_CURRENT_DATE_TIME_timezone"],
|
||||
},
|
||||
"GOOGLECALENDAR_QUICK_ADD": {
|
||||
"display_name": "Quick Add Event",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_QUICK_ADD_calendar_id",
|
||||
"GOOGLECALENDAR_QUICK_ADD_text",
|
||||
"GOOGLECALENDAR_QUICK_ADD_send_updates",
|
||||
],
|
||||
},
|
||||
"GOOGLECALENDAR_LIST_CALENDARS": {
|
||||
"display_name": "List Google Calendars",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_max_results",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_min_access_role",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_page_token",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_show_deleted",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_show_hidden",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_sync_token",
|
||||
],
|
||||
},
|
||||
"GOOGLECALENDAR_FIND_EVENT": {
|
||||
"display_name": "Find Event",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_FIND_EVENT_calendar_id",
|
||||
"GOOGLECALENDAR_FIND_EVENT_query",
|
||||
"GOOGLECALENDAR_FIND_EVENT_max_results",
|
||||
"GOOGLECALENDAR_FIND_EVENT_order_by",
|
||||
"GOOGLECALENDAR_FIND_EVENT_show_deleted",
|
||||
"GOOGLECALENDAR_FIND_EVENT_single_events",
|
||||
"GOOGLECALENDAR_FIND_EVENT_timeMax",
|
||||
"GOOGLECALENDAR_FIND_EVENT_timeMin",
|
||||
"GOOGLECALENDAR_FIND_EVENT_updated_min",
|
||||
"GOOGLECALENDAR_FIND_EVENT_event_types",
|
||||
"GOOGLECALENDAR_FIND_EVENT_page_token",
|
||||
],
|
||||
},
|
||||
"GOOGLECALENDAR_CREATE_EVENT": {
|
||||
"display_name": "Create Event",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_CREATE_EVENT_description",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_eventType",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_create_meeting_room",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_guestsCanSeeOtherGuests",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_guestsCanInviteOthers",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_location",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_summary",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_transparency",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_visibility",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_timezone",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_recurrence",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_guests_can_modify",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_attendees",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_send_updates",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_start_datetime",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_event_duration_hour",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_event_duration_minutes",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_calendar_id",
|
||||
],
|
||||
},
|
||||
"GOOGLECALENDAR_FIND_FREE_SLOTS": {
|
||||
"display_name": "Find Free Slots",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_FIND_FREE_SLOTS_time_min",
|
||||
"GOOGLECALENDAR_FIND_FREE_SLOTS_time_max",
|
||||
"GOOGLECALENDAR_FIND_FREE_SLOTS_timezone",
|
||||
"GOOGLECALENDAR_FIND_FREE_SLOTS_group_expansion_max",
|
||||
"GOOGLECALENDAR_FIND_FREE_SLOTS_calendar_expansion_max",
|
||||
"GOOGLECALENDAR_FIND_FREE_SLOTS_items",
|
||||
],
|
||||
},
|
||||
"GOOGLECALENDAR_PATCH_CALENDAR": {
|
||||
"display_name": "Patch Calendar",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_PATCH_CALENDAR_calendar_id",
|
||||
"GOOGLECALENDAR_PATCH_CALENDAR_description",
|
||||
"GOOGLECALENDAR_PATCH_CALENDAR_location",
|
||||
"GOOGLECALENDAR_PATCH_CALENDAR_summary",
|
||||
"GOOGLECALENDAR_PATCH_CALENDAR_timezone",
|
||||
],
|
||||
},
|
||||
"GOOGLECALENDAR_GET_CALENDAR": {
|
||||
"display_name": "Fetch Google Calendar",
|
||||
"action_fields": ["GOOGLECALENDAR_GET_CALENDAR_calendar_id"],
|
||||
},
|
||||
"GOOGLECALENDAR_DELETE_EVENT": {
|
||||
"display_name": "Delete Event",
|
||||
"action_fields": ["GOOGLECALENDAR_DELETE_EVENT_calendar_id", "GOOGLECALENDAR_DELETE_EVENT_event_id"],
|
||||
},
|
||||
"GOOGLECALENDAR_DUPLICATE_CALENDAR": {
|
||||
"display_name": "Duplicate Calendar",
|
||||
"action_fields": ["GOOGLECALENDAR_DUPLICATE_CALENDAR_summary"],
|
||||
},
|
||||
}
|
||||
|
||||
_list_variables = {
|
||||
"GOOGLECALENDAR_FIND_EVENT_event_types",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_recurrence",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_attendees",
|
||||
"GOOGLECALENDAR_FIND_FREE_SLOTS_items",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_recurrence",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_attendees",
|
||||
}
|
||||
|
||||
_all_fields = {field for action_data in _actions_data.values() for field in action_data["action_fields"]}
|
||||
_bool_variables = {
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_show_deleted",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_show_hidden",
|
||||
"GOOGLECALENDAR_FIND_EVENT_show_deleted",
|
||||
"GOOGLECALENDAR_FIND_EVENT_single_events",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_create_meeting_room",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_guestsCanSeeOtherGuests",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_guestsCanInviteOthers",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_guests_can_modify",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_send_updates",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_create_meeting_room",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_guestsCanSeeOtherGuests",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_guestsCanInviteOthers",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_guests_can_modify",
|
||||
"GOOGLECALENDAR_UPDATE_EVENT_send_updates",
|
||||
}
|
||||
|
||||
inputs = [
|
||||
*ComposioBaseComponent._base_inputs,
|
||||
IntInput(
|
||||
name="GOOGLECALENDAR_LIST_CALENDARS_max_results",
|
||||
display_name="Max Results",
|
||||
info="Maximum number of entries returned on one result page. The page size can never be larger than 250 entries.", # noqa: E501
|
||||
show=False,
|
||||
value=10,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_LIST_CALENDARS_min_access_role",
|
||||
display_name="Min Access Role",
|
||||
info="The minimum access role for the user in the returned entries. Accepted values are 'owner' & 'reader'",
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_LIST_CALENDARS_page_token",
|
||||
display_name="Page Token",
|
||||
info="Token specifying which result page to return.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_LIST_CALENDARS_show_deleted",
|
||||
display_name="Show Deleted",
|
||||
info="Whether to include deleted calendar list entries in the result.",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_LIST_CALENDARS_show_hidden",
|
||||
display_name="Show Hidden",
|
||||
info="Whether to show hidden entries.",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_LIST_CALENDARS_sync_token",
|
||||
display_name="Sync Token",
|
||||
info="Token obtained from the nextSyncToken field returned on the last page of results from the previous list request.", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_calendar_id",
|
||||
display_name="Calendar Id",
|
||||
info="Identifier of the Google Calendar. Use 'primary' for the currently logged in user's primary calendar.", # noqa: E501
|
||||
show=False,
|
||||
value="primary",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_query",
|
||||
display_name="Query",
|
||||
info="Search term to find events that match these terms in the event's summary, description, location, attendee's displayName, attendee's email, organizer's displayName, organizer's email, etc if needed.", # noqa: E501
|
||||
show=False,
|
||||
),
|
||||
IntInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_max_results",
|
||||
display_name="Max Results",
|
||||
info="Maximum number of events returned on one result page. The page size can never be larger than 2500 events. The default value is 10.", # noqa: E501
|
||||
show=False,
|
||||
value=10,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_order_by",
|
||||
display_name="Order By",
|
||||
info="The order of the events returned in the result. Acceptable values are 'startTime' and 'updated'.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_show_deleted",
|
||||
display_name="Show Deleted",
|
||||
info="Whether to include deleted events (with status equals 'cancelled') in the result.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_single_events",
|
||||
display_name="Single Events",
|
||||
info="Whether to expand recurring events into instances and only return single one-off events and instances of recurring events, but not the underlying recurring events themselves.", # noqa: E501
|
||||
show=False,
|
||||
value=True,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_timeMax",
|
||||
display_name="Timemax",
|
||||
info="Upper bound (exclusive) for an event's start time to filter by. Accepts multiple formats:, 1. ISO format with timezone (e.g., 2024-12-06T13:00:00Z), 2. Comma-separated format (e.g., 2024,12,06,13,00,00), 3. Simple datetime format (e.g., 2024-12-06 13:00:00)", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_timeMin",
|
||||
display_name="Timemin",
|
||||
info="Lower bound (exclusive) for an event's end time to filter by. Accepts multiple formats:, 1. ISO format with timezone (e.g., 2024-12-06T13:00:00Z), 2. Comma-separated format (e.g., 2024,12,06,13,00,00), 3. Simple datetime format (e.g., 2024-12-06 13:00:00)", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_updated_min",
|
||||
display_name="Updated Min",
|
||||
info="Lower bound for an event's last modification time to filter by. Accepts multiple formats:, 1. ISO format with timezone (e.g., 2024-12-06T13:00:00Z), 2. Comma-separated format (e.g., 2024,12,06,13,00,00), 3. Simple datetime format (e.g., 2024-12-06 13:00:00)", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_event_types",
|
||||
display_name="Event Types",
|
||||
info="List of event types to return. Possible values are: default, outOfOffice, focusTime, workingLocation.", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_EVENT_page_token",
|
||||
display_name="Page Token",
|
||||
info="Token specifying which result page to return. Optional.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_DUPLICATE_CALENDAR_summary",
|
||||
display_name="Summary/Title",
|
||||
info="Title of the calendar to be duplicated.",
|
||||
show=False,
|
||||
value="",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_REMOVE_ATTENDEE_calendar_id",
|
||||
display_name="Calendar Id",
|
||||
info="ID of the Google Calendar",
|
||||
show=False,
|
||||
value="primary",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_REMOVE_ATTENDEE_event_id",
|
||||
display_name="Event Id",
|
||||
info="ID of the event",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_REMOVE_ATTENDEE_attendee_email",
|
||||
display_name="Attendee Email",
|
||||
info="Email address of the attendee to be removed",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_GET_CALENDAR_calendar_id",
|
||||
display_name="Calendar Id",
|
||||
info="The ID of the Google Calendar that needs to be fetched. Default is 'primary'.",
|
||||
show=False,
|
||||
value="primary",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_description",
|
||||
display_name="Description",
|
||||
info="Description of the event. Can contain HTML. Optional.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_eventType",
|
||||
display_name="Event Type",
|
||||
info="Type of the event, immutable post-creation. Currently, only 'default'",
|
||||
show=False,
|
||||
value="default",
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_create_meeting_room",
|
||||
display_name="Create Meeting Room",
|
||||
info="If true, a Google Meet link is created and added to the event.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_guestsCanSeeOtherGuests",
|
||||
display_name="Guests Can See Other Guests",
|
||||
info="Whether attendees other than the organizer can see who the event's attendees are.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_guestsCanInviteOthers",
|
||||
display_name="Guests Can Invite Others",
|
||||
info="Whether attendees other than the organizer can invite others to the event.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_location",
|
||||
display_name="Location",
|
||||
info="Geographic location of the event as free-form text.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_summary",
|
||||
display_name="Summary/Title",
|
||||
info="Summary (title) of the event.",
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_transparency",
|
||||
display_name="Event Transparency",
|
||||
info="'opaque' (busy) or 'transparent' (available).",
|
||||
show=False,
|
||||
value="opaque",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_visibility",
|
||||
display_name="Event Visibility",
|
||||
info="Event visibility: 'default', 'public', 'private', or 'confidential'.",
|
||||
show=False,
|
||||
value="default",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_timezone",
|
||||
display_name="Timezone",
|
||||
info="IANA timezone name (e.g., 'America/New_York'). Required if datetime is naive. If datetime includes timezone info (Z or offset), this field is optional and defaults to UTC.", # noqa: E501
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_recurrence",
|
||||
display_name="Recurrence",
|
||||
info="List of RRULE, EXRULE, RDATE, EXDATE lines for recurring events.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_guests_can_modify",
|
||||
display_name="Guests Can Modify",
|
||||
info="If True, guests can modify the event.",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_attendees",
|
||||
display_name="Attendees",
|
||||
info="List of attendee emails (strings).",
|
||||
show=False,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_send_updates",
|
||||
display_name="Send Updates",
|
||||
info="Defaults to True. Whether to send updates to the attendees.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_start_datetime",
|
||||
display_name="Start Datetime",
|
||||
info="Naive date/time (YYYY-MM-DDTHH:MM:SS) with NO offsets or Z. e.g. '2025-01-16T13:00:00'",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_event_duration_hour",
|
||||
display_name="Event Duration Hour",
|
||||
info="Number of hours (0-24).",
|
||||
show=False,
|
||||
value=0,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_event_duration_minutes",
|
||||
display_name="Event Duration Minutes",
|
||||
info="Number of minutes (0-59).",
|
||||
show=False,
|
||||
value=30,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_CREATE_EVENT_calendar_id",
|
||||
display_name="Calendar Id",
|
||||
info="The ID of the Google Calendar. `primary` for interacting with the primary calendar.",
|
||||
show=False,
|
||||
value="primary",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_DELETE_EVENT_calendar_id",
|
||||
display_name="Calendar Id",
|
||||
info="ID of the Google Calendar",
|
||||
show=False,
|
||||
value="primary",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_DELETE_EVENT_event_id",
|
||||
display_name="Event Id",
|
||||
info="ID of the event to be deleted",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_FREE_SLOTS_time_min",
|
||||
display_name="Time Min",
|
||||
info="The start datetime of the interval for the query. Supports multiple formats:, 1. ISO format with timezone (e.g., 2024-12-06T13:00:00Z), 2. Comma-separated format (e.g., 2024,12,06,13,00,00), 3. Simple datetime format (e.g., 2024-12-06 13:00:00)", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_FREE_SLOTS_time_max",
|
||||
display_name="Time Max",
|
||||
info="The end datetime of the interval for the query. Supports multiple formats:, 1. ISO format with timezone (e.g., 2024-12-06T13:00:00Z), 2. Comma-separated format (e.g., 2024,12,06,13,00,00), 3. Simple datetime format (e.g., 2024-12-06 13:00:00)", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_FREE_SLOTS_timezone",
|
||||
display_name="Timezone",
|
||||
info="Time zone used in the response. Optional. The default is UTC.",
|
||||
show=False,
|
||||
value="UTC",
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GOOGLECALENDAR_FIND_FREE_SLOTS_group_expansion_max",
|
||||
display_name="Group Expansion Max",
|
||||
info="Maximal number of calendar identifiers to be provided for a single group. Optional. An error is returned for a group with more members than this value. Maximum value is 100.", # noqa: E501
|
||||
show=False,
|
||||
value=100,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GOOGLECALENDAR_FIND_FREE_SLOTS_calendar_expansion_max",
|
||||
display_name="Calendar Expansion Max",
|
||||
info="Maximal number of calendars for which FreeBusy information is to be provided. Optional. Maximum value is 50.", # noqa: E501
|
||||
show=False,
|
||||
value=50,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_FIND_FREE_SLOTS_items",
|
||||
display_name="Items",
|
||||
info="List of calendars ids for which to fetch",
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_QUICK_ADD_calendar_id",
|
||||
display_name="Calendar Id",
|
||||
info="Calendar identifier. To list calendars to retrieve calendar IDs use relevant tools. To access the primary calendar of the currently logged in user, use the 'primary' keyword.", # noqa: E501
|
||||
show=False,
|
||||
value="primary",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_QUICK_ADD_text",
|
||||
display_name="Text",
|
||||
info="The text describing the event to be created.",
|
||||
show=False,
|
||||
value="",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_QUICK_ADD_send_updates",
|
||||
display_name="Send Updates",
|
||||
info="Guests who should receive notifications about the creation of the new event. Accepted fields include 'all', 'none', 'externalOnly'", # noqa: E501
|
||||
show=False,
|
||||
value="none",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_PATCH_CALENDAR_calendar_id",
|
||||
display_name="Calendar Id",
|
||||
info="The ID of the Google Calendar that needs to be updated.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_PATCH_CALENDAR_description",
|
||||
display_name="Description",
|
||||
info="Description of the calendar. Optional.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_PATCH_CALENDAR_location",
|
||||
display_name="Location",
|
||||
info="Geographic location of the calendar as free-form text.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_PATCH_CALENDAR_summary",
|
||||
display_name="Title/Summary",
|
||||
info="Title of the calendar. This field is required and cannot be left blank as per the Google Calendar API requirements.", # noqa: E501
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_PATCH_CALENDAR_timezone",
|
||||
display_name="Timezone",
|
||||
info="The time zone of the calendar. (Formatted as an IANA Time Zone Database name, e.g. 'Europe/Zurich').",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GOOGLECALENDAR_GET_CURRENT_DATE_TIME_timezone",
|
||||
display_name="Timezone",
|
||||
info="The timezone offset from UTC to retrieve current date and time, like for location of UTC+6, you give 6, for UTC -9, your give -9.", # noqa: E501
|
||||
show=False,
|
||||
value=0,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_description",
|
||||
display_name="Description",
|
||||
info="Description of the event. Can contain HTML. Optional.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_eventType",
|
||||
display_name="EventType",
|
||||
info="Type of the event, immutable post-creation. Currently, only 'default' and 'workingLocation' can be created.", # noqa: E501
|
||||
show=False,
|
||||
value="default",
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_create_meeting_room",
|
||||
display_name="Create Meeting Room",
|
||||
info="If true, a Google Meet link is created and added to the event.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_guestsCanSeeOtherGuests",
|
||||
display_name="Guests Can See Other Guests",
|
||||
info="Whether attendees other than the organizer can see who the event's attendees are.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_guestsCanInviteOthers",
|
||||
display_name="Guests Can Invite Others",
|
||||
info="Whether attendees other than the organizer can invite others to the event.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_location",
|
||||
display_name="Location",
|
||||
info="Geographic location of the event as free-form text.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_summary",
|
||||
display_name="Summary/Title",
|
||||
info="Summary (title) of the event.",
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_transparency",
|
||||
display_name="Event Transparency",
|
||||
info="'opaque' (busy) or 'transparent' (available).",
|
||||
show=False,
|
||||
value="opaque",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_visibility",
|
||||
display_name="Event Visibility",
|
||||
info="Event visibility: 'default', 'public', 'private', or 'confidential'.",
|
||||
show=False,
|
||||
value="default",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_timezone",
|
||||
display_name="Timezone",
|
||||
info="IANA timezone name (e.g., 'America/New_York'). Required if datetime is naive. If datetime includes timezone info (Z or offset), this field is optional and defaults to UTC.", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_recurrence",
|
||||
display_name="Recurrence",
|
||||
info="List of RRULE, EXRULE, RDATE, EXDATE lines for recurring events.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_guests_can_modify",
|
||||
display_name="Guests Can Modify",
|
||||
info="If True, guests can modify the event.",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_attendees",
|
||||
display_name="Attendees",
|
||||
info="List of attendee emails (strings).",
|
||||
show=False,
|
||||
),
|
||||
BoolInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_send_updates",
|
||||
display_name="Send Updates",
|
||||
info="Defaults to True. Whether to send updates to the attendees.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_start_datetime",
|
||||
display_name="Start Datetime",
|
||||
info="Naive date/time (YYYY-MM-DDTHH:MM:SS) with NO offsets or Z. e.g. '2025-01-16T13:00:00'",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_event_duration_hour",
|
||||
display_name="Event Duration Hour",
|
||||
info="Number of hours (0-24).",
|
||||
show=False,
|
||||
value=0,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_event_duration_minutes",
|
||||
display_name="Event Duration Minutes",
|
||||
info="Number of minutes (0-59).",
|
||||
show=False,
|
||||
value=30,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_calendar_id",
|
||||
display_name="Calendar Id",
|
||||
info="ID of the Google Calendar",
|
||||
show=False,
|
||||
value="primary",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="GOOGLECALENDAR_UPDATE_EVENT_event_id",
|
||||
display_name="Event Id",
|
||||
info="ID of the event to be updated",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
]
|
||||
|
||||
def execute_action(self):
|
||||
"""Execute action and return response as Message."""
|
||||
toolset = self._build_wrapper()
|
||||
|
||||
try:
|
||||
self._build_action_maps()
|
||||
# Get the display name from the action list
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else self.action
|
||||
# Use the display_to_key_map to get the action key
|
||||
action_key = self._display_to_key_map.get(display_name)
|
||||
if not action_key:
|
||||
msg = f"Invalid action: {display_name}"
|
||||
raise ValueError(msg)
|
||||
|
||||
enum_name = getattr(Action, action_key)
|
||||
params = {}
|
||||
if action_key in self._actions_data:
|
||||
for field in self._actions_data[action_key]["action_fields"]:
|
||||
value = getattr(self, field)
|
||||
|
||||
if value is None or value == "":
|
||||
continue
|
||||
|
||||
if field in self._list_variables and value:
|
||||
value = [item.strip() for item in value.split(",")]
|
||||
|
||||
if field in self._bool_variables:
|
||||
value = bool(value)
|
||||
|
||||
param_name = field.replace(action_key + "_", "")
|
||||
params[param_name] = value
|
||||
|
||||
result = toolset.execute_action(
|
||||
action=enum_name,
|
||||
params=params,
|
||||
)
|
||||
if not result.get("successful"):
|
||||
message_str = result.get("error", {})
|
||||
return {"error": message_str}
|
||||
|
||||
result_data = result.get("data", [])
|
||||
if (
|
||||
len(result_data) != 1
|
||||
and not self._actions_data.get(action_key, {}).get("result_field")
|
||||
and self._actions_data.get(action_key, {}).get("get_result_field")
|
||||
):
|
||||
msg = f"Expected a dict with a single key, got {len(result_data)} keys: {result_data.keys()}"
|
||||
raise ValueError(msg)
|
||||
if action_key == "GOOGLECALENDAR_GET_CURRENT_DATE_TIME":
|
||||
return result_data
|
||||
return result_data[next(iter(result_data))]
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing action: {e}")
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else str(self.action)
|
||||
msg = f"Failed to execute {display_name}: {e!s}"
|
||||
raise ValueError(msg) from e
|
||||
|
||||
def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:
|
||||
return super().update_build_config(build_config, field_value, field_name)
|
||||
def set_default_tools(self):
|
||||
"""Set the default tools for Google Calendar component."""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
|
||||
class ComposioGooglemeetAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Google Meet"
|
||||
icon = "Googlemeet"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "googlemeet"
|
||||
|
||||
def set_default_tools(self):
|
||||
"""Set the default tools for Google Calendar component."""
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from base.langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
|
||||
class ComposioGoogleTasksAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Google Tasks"
|
||||
icon = "GoogleTasks"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "googletasks"
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
|
||||
class ComposioLinearAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Linear"
|
||||
icon = "Linear"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "linear"
|
||||
|
||||
def set_default_tools(self):
|
||||
"""Set the default tools for Linear component."""
|
||||
|
|
@ -1,765 +1,11 @@
|
|||
import json
|
||||
from typing import Any
|
||||
|
||||
from composio import Action
|
||||
|
||||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
from langflow.inputs import BoolInput, FileInput, IntInput, MessageTextInput
|
||||
from langflow.logging import logger
|
||||
|
||||
|
||||
class ComposioOutlookAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Outlook"
|
||||
description: str = "Outlook API"
|
||||
icon = "Outlook"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "outlook"
|
||||
|
||||
_actions_data: dict = {
|
||||
"OUTLOOK_OUTLOOK_REPLY_EMAIL": {
|
||||
"display_name": "Reply To Email",
|
||||
"action_fields": [
|
||||
"OUTLOOK_OUTLOOK_REPLY_EMAIL_user_id",
|
||||
"OUTLOOK_OUTLOOK_REPLY_EMAIL_message_id",
|
||||
"OUTLOOK_OUTLOOK_REPLY_EMAIL_comment",
|
||||
"OUTLOOK_OUTLOOK_REPLY_EMAIL_cc_emails",
|
||||
"OUTLOOK_OUTLOOK_REPLY_EMAIL_bcc_emails",
|
||||
],
|
||||
"get_result_field": False,
|
||||
},
|
||||
"OUTLOOK_OUTLOOK_GET_PROFILE": {
|
||||
"display_name": "Get Profile",
|
||||
"action_fields": ["OUTLOOK_OUTLOOK_GET_PROFILE_user_id"],
|
||||
"get_result_field": True,
|
||||
"result_field": "response_data",
|
||||
},
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL": {
|
||||
"display_name": "Send Email",
|
||||
"action_fields": [
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_user_id",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_subject",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_body",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_to_email",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_to_name",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_cc_emails",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_bcc_emails",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_is_html",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_save_to_sent_items",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_attachment",
|
||||
],
|
||||
"get_result_field": False,
|
||||
},
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES": {
|
||||
"display_name": "List Messages",
|
||||
"action_fields": [
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_user_id",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_folder",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_top",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_skip",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_is_read",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_importance",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_gt",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_startswith",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_endswith",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_contains",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_ge",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_lt",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_le",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_from_address",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_has_attachments",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_body_preview_contains",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_sent_date_time_gt",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_sent_date_time_lt",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_categories",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_select",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_orderby",
|
||||
],
|
||||
"get_result_field": True,
|
||||
"result_field": "value",
|
||||
},
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS": {
|
||||
"display_name": "List Events",
|
||||
"action_fields": [
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS_user_id",
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS_top",
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS_skip",
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS_filter",
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS_select",
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS_orderby",
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS_timezone",
|
||||
],
|
||||
"get_result_field": True,
|
||||
"result_field": "value",
|
||||
},
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT": {
|
||||
"display_name": "Create Calendar Event",
|
||||
"action_fields": [
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_user_id",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_subject",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_body",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_is_html",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_start_datetime",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_end_datetime",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_time_zone",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_is_online_meeting",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_online_meeting_provider",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_attendees_info",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_location",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_show_as",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_categories",
|
||||
],
|
||||
"get_result_field": True,
|
||||
"result_field": "response_data",
|
||||
},
|
||||
"OUTLOOK_OUTLOOK_GET_EVENT": {
|
||||
"display_name": "Get Calendar Event",
|
||||
"action_fields": ["OUTLOOK_OUTLOOK_GET_EVENT_user_id", "OUTLOOK_OUTLOOK_GET_EVENT_event_id"],
|
||||
"get_result_field": True,
|
||||
"result_field": "response_data",
|
||||
},
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT": {
|
||||
"display_name": "Create Email Draft",
|
||||
"action_fields": [
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_subject",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_body",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_to_recipients",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_cc_recipients",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_bcc_recipients",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_is_html",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_attachment",
|
||||
],
|
||||
"get_result_field": True,
|
||||
"result_field": "response_data",
|
||||
},
|
||||
}
|
||||
|
||||
_all_fields = {field for action_data in _actions_data.values() for field in action_data["action_fields"]}
|
||||
|
||||
_bool_variables = {
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_is_html",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_save_to_sent_items",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_is_html",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_is_html",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_is_online_meeting",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_is_read",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_has_attachments",
|
||||
}
|
||||
|
||||
_list_variables = {
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS_select",
|
||||
"OUTLOOK_OUTLOOK_LIST_EVENTS_orderby",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_cc_emails",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_bcc_emails",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_to_recipients",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_cc_recipients",
|
||||
"OUTLOOK_OUTLOOK_CREATE_DRAFT_bcc_recipients",
|
||||
"OUTLOOK_OUTLOOK_REPLY_EMAIL_cc_emails",
|
||||
"OUTLOOK_OUTLOOK_REPLY_EMAIL_bcc_emails",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_attendees_info",
|
||||
"OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_categories",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_categories",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_select",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_orderby",
|
||||
}
|
||||
|
||||
inputs = [
|
||||
*ComposioBaseComponent._base_inputs,
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_EVENTS_user_id",
|
||||
display_name="User Id",
|
||||
info="The target user's email address or 'me' for the authenticated user.",
|
||||
show=False,
|
||||
value="me",
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_EVENTS_top",
|
||||
display_name="Max Results",
|
||||
info="The maximum number of events to return per request.",
|
||||
show=False,
|
||||
value=10,
|
||||
),
|
||||
IntInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_EVENTS_skip",
|
||||
display_name="Skip",
|
||||
info="The number of events to skip before starting to collect results.",
|
||||
show=False,
|
||||
value=0,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_EVENTS_filter",
|
||||
display_name="Filter",
|
||||
info="OData query string to filter results. Example: start/dateTime ge '2024-01-01T00:00:00'",
|
||||
show=False,
|
||||
value="",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_EVENTS_select",
|
||||
display_name="Select",
|
||||
info="List of properties to include in the response comma separated.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_EVENTS_orderby",
|
||||
display_name="Orderby",
|
||||
info="Properties to sort results by comma separated.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_EVENTS_timezone",
|
||||
display_name="Timezone",
|
||||
info="The timezone for event start and end times in the response.",
|
||||
show=False,
|
||||
value="UTC",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_user_id",
|
||||
display_name="User Id",
|
||||
info="The user's email address or 'me' for the authenticated user.",
|
||||
show=False,
|
||||
value="me",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_subject",
|
||||
display_name="Subject",
|
||||
info="Subject of the email",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_body",
|
||||
display_name="Body",
|
||||
info="Body content of the email. Can be plain text or HTML based on is_html flag.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_to_email",
|
||||
display_name="Recipient Email",
|
||||
info="Recipient email address",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_to_name",
|
||||
display_name="To Name",
|
||||
info="Recipient display name",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_cc_emails",
|
||||
display_name="CC",
|
||||
info="List of CC recipient email addresses comma separated",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_bcc_emails",
|
||||
display_name="BCC",
|
||||
info="List of BCC recipient email addresses comma separated",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_is_html",
|
||||
display_name="Is HTML",
|
||||
info="Set to True if the body content is HTML formatted",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_save_to_sent_items",
|
||||
display_name="Save To Sent Items",
|
||||
info="Whether to save the sent email to Sent Items folder.",
|
||||
show=False,
|
||||
value=True,
|
||||
advanced=True,
|
||||
),
|
||||
FileInput(
|
||||
name="OUTLOOK_OUTLOOK_SEND_EMAIL_attachment",
|
||||
display_name="Attachment",
|
||||
file_types=[
|
||||
"csv",
|
||||
"txt",
|
||||
"doc",
|
||||
"docx",
|
||||
"xls",
|
||||
"xlsx",
|
||||
"pdf",
|
||||
"png",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"gif",
|
||||
"zip",
|
||||
"rar",
|
||||
"ppt",
|
||||
"pptx",
|
||||
],
|
||||
info="Add an attachment",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CREATE_DRAFT_subject",
|
||||
display_name="Subject",
|
||||
info="Subject of the email",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CREATE_DRAFT_body",
|
||||
display_name="Body",
|
||||
info="Body content of the email. Can be plain text or HTML based on is_html flag",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CREATE_DRAFT_to_recipients",
|
||||
display_name="Recipient Email",
|
||||
info="List of recipient email addresses comma separated",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CREATE_DRAFT_cc_recipients",
|
||||
display_name="Cc Recipients",
|
||||
info="List of CC recipient email addresses",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CREATE_DRAFT_bcc_recipients",
|
||||
display_name="BCC",
|
||||
info="List of BCC recipient email addresses comma separated",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="OUTLOOK_OUTLOOK_CREATE_DRAFT_is_html",
|
||||
display_name="Is HTML",
|
||||
info="Set to True if the body content is HTML formatted",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
FileInput(
|
||||
name="OUTLOOK_OUTLOOK_CREATE_DRAFT_attachment",
|
||||
display_name="Attachment",
|
||||
file_types=[
|
||||
"csv",
|
||||
"txt",
|
||||
"doc",
|
||||
"docx",
|
||||
"xls",
|
||||
"xlsx",
|
||||
"pdf",
|
||||
"png",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"gif",
|
||||
"zip",
|
||||
"rar",
|
||||
"ppt",
|
||||
"pptx",
|
||||
],
|
||||
info="Add an attachment",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_GET_PROFILE_user_id",
|
||||
display_name="User Id",
|
||||
info="The user's email address or 'me' for the authenticated user.",
|
||||
show=False,
|
||||
value="me",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_REPLY_EMAIL_user_id",
|
||||
display_name="User Id",
|
||||
info="The user's email address or 'me' for the authenticated user.",
|
||||
show=False,
|
||||
value="me",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_REPLY_EMAIL_message_id",
|
||||
display_name="Message Id",
|
||||
info="The ID of the message to reply to. Can be obtained from OUTLOOK_LIST_MESSAGES action.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_REPLY_EMAIL_comment",
|
||||
display_name="Comment",
|
||||
info="Comment to include in the reply. Must be plain text.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_REPLY_EMAIL_cc_emails",
|
||||
display_name="CC",
|
||||
info="List of CC recipient email addresses comma separated",
|
||||
show=False,
|
||||
value=[],
|
||||
is_list=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_REPLY_EMAIL_bcc_emails",
|
||||
display_name="BCC",
|
||||
info="List of BCC recipient email addresses comma separated",
|
||||
show=False,
|
||||
value=[],
|
||||
is_list=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_user_id",
|
||||
display_name="User Id",
|
||||
info="The user's email address or 'me' for the authenticated user.",
|
||||
show=False,
|
||||
value="me",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_subject",
|
||||
display_name="Subject",
|
||||
info="Subject of the event. Example: 'Team Meeting'.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_body",
|
||||
display_name="Body",
|
||||
info="Body content of the event. Can be plain text or HTML.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_is_html",
|
||||
display_name="Is Html",
|
||||
info="Set to True if the body content should be interpreted as HTML.",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_start_datetime",
|
||||
display_name="Start Datetime",
|
||||
info="Start date/time (ISO 8601). Example: '2025-01-03T10:00:00Z'.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_end_datetime",
|
||||
display_name="End Datetime",
|
||||
info="End date/time (ISO 8601). Example: '2025-01-03T11:00:00Z'.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_time_zone",
|
||||
display_name="Time Zone",
|
||||
info="Time zone (e.g., 'UTC' or 'America/Los_Angeles').",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_is_online_meeting",
|
||||
display_name="Is Online Meeting",
|
||||
info="Set to True to make this an online meeting and generate a Teams URL.",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_online_meeting_provider",
|
||||
display_name="Online Meeting Provider",
|
||||
info="The online meeting service provider. Currently only supports 'teamsForBusiness'.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_attendees_info",
|
||||
display_name="Attendees",
|
||||
info="A list of attendee information. Only email is required for each attendee., Example: [{ 'email': 'team@example.com', 'name': 'Team', 'type': 'required' }, { 'email': 'other@example.com', 'type': 'optional' }, { 'email': 'other2@example.com' }]", # noqa: E501
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_location",
|
||||
display_name="Location",
|
||||
info="Location of the event (e.g., 'Conference Room').",
|
||||
show=False,
|
||||
value="",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_show_as",
|
||||
display_name="Show As",
|
||||
info="Status of the event: 'free', 'tentative', 'busy', or 'oof'.",
|
||||
show=False,
|
||||
value="busy",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_CALENDAR_CREATE_EVENT_categories",
|
||||
display_name="Categories",
|
||||
info="List of categories associated with the event comma separated.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_GET_EVENT_user_id",
|
||||
display_name="User Id",
|
||||
info="The user's email address or 'me' for the authenticated user.",
|
||||
show=False,
|
||||
value="me",
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_GET_EVENT_event_id",
|
||||
display_name="Event Id",
|
||||
info="The ID of the calendar event to retrieve.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_user_id",
|
||||
display_name="User Id",
|
||||
info="The target user's email address or 'me' for the authenticated user. For delegated access scenarios, this should be the email of the shared mailbox or delegated user.", # noqa: E501
|
||||
show=False,
|
||||
value="me",
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_folder",
|
||||
display_name="Folder",
|
||||
info="",
|
||||
show=False,
|
||||
value="inbox",
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_top",
|
||||
display_name="Max Results",
|
||||
info="The maximum number of messages to return per request. Must be a positive integer between 1 and 1000.",
|
||||
show=False,
|
||||
value=10,
|
||||
),
|
||||
IntInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_skip",
|
||||
display_name="Skip",
|
||||
info="The number of messages to skip before starting to collect results. Use for paginated responses.",
|
||||
show=False,
|
||||
value=0,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_is_read",
|
||||
display_name="Is Read",
|
||||
info="Filter messages by read status. If set to False, only unread messages will be returned.",
|
||||
show=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_importance",
|
||||
display_name="Importance",
|
||||
info="Filter messages by importance. For example, 'high', 'normal', or 'low'.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_subject",
|
||||
display_name="Subject",
|
||||
info="Filter messages by subject (exact match).",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_gt",
|
||||
display_name="Received Date Time Gt",
|
||||
info="Filter messages with a receivedDateTime greater than the specified value. Example: '2023-01-01T00:00:00Z'.", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_startswith",
|
||||
display_name="Subject Startswith",
|
||||
info="Filter messages where the subject starts with the specified string.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_endswith",
|
||||
display_name="Subject Endswith",
|
||||
info="Filter messages where the subject ends with the specified string.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_contains",
|
||||
display_name="Subject Contains",
|
||||
info="Filter messages where the subject contains the specified substring.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_ge",
|
||||
display_name="Received Date Time Ge",
|
||||
info="Filter messages with a receivedDateTime greater than or equal to the specified value.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_lt",
|
||||
display_name="Received Date Time Lt",
|
||||
info="Filter messages with a receivedDateTime less than the specified value.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_le",
|
||||
display_name="Received Date Time Le",
|
||||
info="Filter messages with a receivedDateTime less than or equal to the specified value.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_from_address",
|
||||
display_name="From Address",
|
||||
info="Filter messages by the sender's email address. Uses equality check on from/emailAddress/address.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_has_attachments",
|
||||
display_name="Has Attachments",
|
||||
info="Filter messages by whether they have attachments.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_body_preview_contains",
|
||||
display_name="Body Preview Contains",
|
||||
info="Filter messages where the bodyPreview contains the specified substring.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_sent_date_time_gt",
|
||||
display_name="Sent Date Time Gt",
|
||||
info="Filter messages with a sentDateTime greater than the specified value.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_sent_date_time_lt",
|
||||
display_name="Sent Date Time Lt",
|
||||
info="Filter messages with a sentDateTime less than the specified value.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_categories",
|
||||
display_name="Categories",
|
||||
info="Filter messages by categories. Matches if the message contains any of the specified categories.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_select",
|
||||
display_name="Select",
|
||||
info="A list of properties to include in the response comma separated. Common properties: 'subject', 'from', 'toRecipients', 'receivedDateTime'.", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="OUTLOOK_OUTLOOK_LIST_MESSAGES_orderby",
|
||||
display_name="Orderby",
|
||||
info="Specify properties to sort results by. For example, 'receivedDateTime desc' for newest messages first.", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
]
|
||||
|
||||
def execute_action(self):
|
||||
"""Execute action and return response as Message."""
|
||||
toolset = self._build_wrapper()
|
||||
|
||||
try:
|
||||
self._build_action_maps()
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else self.action
|
||||
action_key = self._display_to_key_map.get(display_name)
|
||||
if not action_key:
|
||||
msg = f"Invalid action: {display_name}"
|
||||
raise ValueError(msg)
|
||||
|
||||
enum_name = getattr(Action, action_key)
|
||||
params = {}
|
||||
if action_key in self._actions_data:
|
||||
for field in self._actions_data[action_key]["action_fields"]:
|
||||
value = getattr(self, field)
|
||||
|
||||
if value is None or value == "":
|
||||
continue
|
||||
|
||||
if field in self._list_variables and value:
|
||||
value = [item.strip() for item in value.split(",")]
|
||||
|
||||
if field in self._bool_variables:
|
||||
value = bool(value)
|
||||
|
||||
param_name = field.replace(action_key + "_", "")
|
||||
|
||||
params[param_name] = value
|
||||
|
||||
result = toolset.execute_action(
|
||||
action=enum_name,
|
||||
params=params,
|
||||
)
|
||||
if not result.get("successful"):
|
||||
error_data = result.get("data", {})
|
||||
error_message = error_data.get("message", str(result.get("error", "Unknown Error")))
|
||||
|
||||
if isinstance(error_message, str):
|
||||
try:
|
||||
error_obj = json.loads(error_message).get("error", {})
|
||||
error_obj["status_code"] = error_data.get("status_code", 400)
|
||||
return error_obj # noqa: TRY300
|
||||
except json.JSONDecodeError:
|
||||
return {"error": error_message, "status_code": error_data.get("status_code", 400)}
|
||||
|
||||
return error_message
|
||||
|
||||
result_data = result.get("data", {})
|
||||
actions_data = self._actions_data.get(action_key, {})
|
||||
if actions_data.get("get_result_field") and actions_data.get("result_field"):
|
||||
response_data = result_data.get("response_data", {})
|
||||
if response_data and actions_data.get("result_field") in response_data:
|
||||
result_data = response_data.get(actions_data.get("result_field"), result.get("data", []))
|
||||
else:
|
||||
result_data = result_data.get(actions_data.get("result_field"), result.get("data", []))
|
||||
if len(result_data) != 1 and not actions_data.get("result_field") and actions_data.get("get_result_field"):
|
||||
msg = f"Expected a dict with a single key, got {len(result_data)} keys: {result_data.keys()}"
|
||||
raise ValueError(msg)
|
||||
return result_data # noqa: TRY300
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing action: {e}")
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else str(self.action)
|
||||
msg = f"Failed to execute {display_name}: {e!s}"
|
||||
raise ValueError(msg) from e
|
||||
|
||||
def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:
|
||||
return super().update_build_config(build_config, field_value, field_name)
|
||||
|
||||
def set_default_tools(self):
|
||||
self._default_tools = {
|
||||
self.sanitize_action_name("OUTLOOK_OUTLOOK_SEND_EMAIL").replace(" ", "-"),
|
||||
self.sanitize_action_name("OUTLOOK_OUTLOOK_LIST_MESSAGES").replace(" ", "-"),
|
||||
}
|
||||
"""Set the default tools for Gmail component."""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
|
||||
class ComposioRedditAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Reddit"
|
||||
icon = "Reddit"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "reddit"
|
||||
|
||||
def set_default_tools(self):
|
||||
"""Set the default tools for Reddit component."""
|
||||
|
|
@ -1,586 +1,11 @@
|
|||
from typing import Any
|
||||
|
||||
from composio import Action
|
||||
|
||||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
from langflow.inputs import (
|
||||
BoolInput,
|
||||
IntInput,
|
||||
MessageTextInput,
|
||||
)
|
||||
from langflow.logging import logger
|
||||
|
||||
|
||||
class ComposioSlackAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Slack"
|
||||
description: str = "Slack API"
|
||||
icon = "Slack"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "slack"
|
||||
|
||||
_actions_data: dict = {
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION": {
|
||||
"display_name": "List Users",
|
||||
"action_fields": [
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_limit",
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_cursor",
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_include_locale",
|
||||
],
|
||||
},
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS": {
|
||||
"display_name": "List Channels",
|
||||
"action_fields": [
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS_exclude_archived",
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS_types",
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS_limit",
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS_cursor",
|
||||
],
|
||||
},
|
||||
"SLACK_UPDATES_A_SLACK_MESSAGE": {
|
||||
"display_name": "Update Slack Chat Message",
|
||||
"action_fields": [
|
||||
"SLACK_UPDATES_A_SLACK_MESSAGE_as_user",
|
||||
"SLACK_UPDATES_A_SLACK_MESSAGE_attachments",
|
||||
"SLACK_UPDATES_A_SLACK_MESSAGE_blocks",
|
||||
"SLACK_UPDATES_A_SLACK_MESSAGE_channel",
|
||||
"SLACK_UPDATES_A_SLACK_MESSAGE_link_names",
|
||||
"SLACK_UPDATES_A_SLACK_MESSAGE_parse",
|
||||
"SLACK_UPDATES_A_SLACK_MESSAGE_text",
|
||||
"SLACK_UPDATES_A_SLACK_MESSAGE_ts",
|
||||
],
|
||||
},
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL": {
|
||||
"display_name": "Post Message To Channel",
|
||||
"action_fields": [
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_as_user",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_attachments",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_blocks",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_channel",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_icon_emoji",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_icon_url",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_link_names",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_mrkdwn",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_parse",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_reply_broadcast",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_text",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_thread_ts",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_links",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_media",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_username",
|
||||
],
|
||||
},
|
||||
"SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY": {
|
||||
"display_name": "Search Messages Endpoint",
|
||||
"action_fields": [
|
||||
"SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_count",
|
||||
"SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_highlight",
|
||||
"SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_page",
|
||||
"SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_query",
|
||||
"SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_sort",
|
||||
"SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_sort_dir",
|
||||
],
|
||||
},
|
||||
"SLACK_FETCH_CONVERSATION_HISTORY": {
|
||||
"display_name": "Retrieve conversation history",
|
||||
"action_fields": [
|
||||
"SLACK_FETCH_CONVERSATION_HISTORY_channel",
|
||||
"SLACK_FETCH_CONVERSATION_HISTORY_latest",
|
||||
"SLACK_FETCH_CONVERSATION_HISTORY_oldest",
|
||||
"SLACK_FETCH_CONVERSATION_HISTORY_inclusive",
|
||||
"SLACK_FETCH_CONVERSATION_HISTORY_limit",
|
||||
"SLACK_FETCH_CONVERSATION_HISTORY_cursor",
|
||||
],
|
||||
},
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME": {
|
||||
"display_name": "Schedule Message In Chat",
|
||||
"action_fields": [
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_as_user",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_attachments",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_blocks",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_channel",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_link_names",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_parse",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_post_at",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_reply_broadcast",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_text",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_thread_ts",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_unfurl_links",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_unfurl_media",
|
||||
],
|
||||
},
|
||||
"SLACK_CREATE_A_REMINDER": {
|
||||
"display_name": "Add Reminder For User",
|
||||
"action_fields": [
|
||||
"SLACK_CREATE_A_REMINDER_text",
|
||||
"SLACK_CREATE_A_REMINDER_time",
|
||||
"SLACK_CREATE_A_REMINDER_user",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
_all_fields = {field for action_data in _actions_data.values() for field in action_data["action_fields"]}
|
||||
_bool_variables = {
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_include_locale",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_as_user",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_link_names",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_mrkdwn",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_reply_broadcast",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_links",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_media",
|
||||
"SLACK_FETCH_CONVERSATION_HISTORY_inclusive",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_as_user",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_link_names",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_reply_broadcast",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_unfurl_links",
|
||||
"SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_unfurl_media",
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS_exclude_archived",
|
||||
"SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_highlight",
|
||||
}
|
||||
|
||||
inputs = [
|
||||
*ComposioBaseComponent._base_inputs,
|
||||
IntInput(
|
||||
name="SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_limit",
|
||||
display_name="Limit",
|
||||
info="The maximum number of items to return. Fewer than the requested number of items may be returned, even if the end of the users list hasn't been reached. Providing no `limit` value will result in Slack attempting to deliver you the entire result set. If the collection is too large you may experience `limit_required` or HTTP 500 errors. ", # noqa: E501
|
||||
show=False,
|
||||
value=1,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_cursor",
|
||||
display_name="Cursor",
|
||||
info="Paginate through collections of data by setting the `cursor` parameter to a `next_cursor` attribute returned by a previous request's `response_metadata`. Default value fetches the first `page` of the collection", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_include_locale",
|
||||
display_name="Include Locale",
|
||||
info="Set this to `true` to receive the locale for users. Defaults to `false`",
|
||||
show=False,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_as_user",
|
||||
display_name="As User",
|
||||
info="Pass true to post the message as the authed user, instead of as a bot. Defaults to false",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_attachments",
|
||||
display_name="Attachments",
|
||||
info="A JSON-based array of structured attachments, presented as a URL-encoded string. ",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_blocks",
|
||||
display_name="Blocks",
|
||||
info="A JSON-based array of structured blocks, presented as a URL-encoded string. ",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_channel",
|
||||
display_name="Channel",
|
||||
info="Channel, private group, or IM channel to send message to. Can be an encoded ID, or a name ",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_icon_emoji",
|
||||
display_name="Icon Emoji",
|
||||
info="Emoji to use as the icon for this message. Overrides `icon_url`. Must be used in conjunction with `as_user` set to `false`, otherwise ignored", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_icon_url",
|
||||
display_name="Icon Url",
|
||||
info="URL to an image to use as the icon for this message. Must be used in conjunction with `as_user` set to false, otherwise ignored", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_link_names",
|
||||
display_name="Link Names",
|
||||
info="Find and link channel names and usernames.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_mrkdwn",
|
||||
display_name="Mrkdwn",
|
||||
info="Disable Slack markup parsing by setting to `false`. Enabled by default.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_parse",
|
||||
display_name="Parse",
|
||||
info="Change how messages are treated. Defaults to `none` ",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_reply_broadcast",
|
||||
display_name="Reply Broadcast",
|
||||
info="Used in conjunction with `thread_ts` and indicates whether reply should be made visible to everyone in the channel or conversation. Defaults to `false`. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_text",
|
||||
display_name="Text",
|
||||
info="How this field works and whether it is required depends on other fields you use in your API call",
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_thread_ts",
|
||||
display_name="Thread Ts",
|
||||
info="Provide another message's `ts` value to make this message a reply. Avoid using a reply's `ts` value; use its parent instead. ", # noqa: E501
|
||||
show=False,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_links",
|
||||
display_name="Unfurl Links",
|
||||
info="Pass true to enable unfurling of primarily text-based content.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_media",
|
||||
display_name="Unfurl Media",
|
||||
info="Pass false to disable unfurling of media content.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_username",
|
||||
display_name="Username",
|
||||
info="Set your bot's user name. Must be used in conjunction with `as_user` set to false, otherwise ignored",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_UPDATES_A_SLACK_MESSAGE_as_user",
|
||||
display_name="As User",
|
||||
info="Pass true to update the message as the authed user",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_UPDATES_A_SLACK_MESSAGE_attachments",
|
||||
display_name="Attachments",
|
||||
info="A JSON-based array of structured attachments, presented as a URL-encoded string. This field is required when not presenting `text`. If you don't include this field, the message's previous `attachments` will be retained. To remove previous `attachments`, include an empty array for this field. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_UPDATES_A_SLACK_MESSAGE_blocks",
|
||||
display_name="Blocks",
|
||||
info="A JSON-based array of structured blocks, presented as a URL-encoded string. If you don't include this field, the message's previous `blocks` will be retained. To remove previous `blocks`, include an empty array for this field. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_UPDATES_A_SLACK_MESSAGE_channel",
|
||||
display_name="Channel ID",
|
||||
info="Channel ID containing the message to be updated.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_UPDATES_A_SLACK_MESSAGE_link_names",
|
||||
display_name="Link Names",
|
||||
info="Find and link channel names and usernames. Defaults to `none`. If you do not specify a value for this field, the original value set for the message will be overwritten with the default, `none`. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_UPDATES_A_SLACK_MESSAGE_parse",
|
||||
display_name="Parse",
|
||||
info="Change how messages are treated. Defaults to `client`, unlike `chat.postMessage`. Accepts either `none` or `full`. If you do not specify a value for this field, the original value set for the message will be overwritten with the default, `client`. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_UPDATES_A_SLACK_MESSAGE_text",
|
||||
display_name="Text",
|
||||
info="New text for the message, using the default formatting rules. It's not required when presenting `blocks` or `attachments`. ", # noqa: E501
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_UPDATES_A_SLACK_MESSAGE_ts",
|
||||
display_name="Ts",
|
||||
info="Timestamp of the message to be updated.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_FETCH_CONVERSATION_HISTORY_channel",
|
||||
display_name="Channel ID",
|
||||
info="Channel ID to fetch history for.",
|
||||
show=False,
|
||||
),
|
||||
IntInput(
|
||||
name="SLACK_FETCH_CONVERSATION_HISTORY_latest",
|
||||
display_name="Latest",
|
||||
info="End of time range of messages to include in results.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="SLACK_FETCH_CONVERSATION_HISTORY_oldest",
|
||||
display_name="Oldest",
|
||||
info="Start of time range of messages to include in results.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_FETCH_CONVERSATION_HISTORY_inclusive",
|
||||
display_name="Inclusive",
|
||||
info="Include messages with latest or oldest timestamp in results only when either timestamp is specified. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="SLACK_FETCH_CONVERSATION_HISTORY_limit",
|
||||
display_name="Limit",
|
||||
info="The maximum number of items to return. Fewer than the requested number of items may be returned, even if the end of the users list hasn't been reached. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_FETCH_CONVERSATION_HISTORY_cursor",
|
||||
display_name="Cursor",
|
||||
info="Paginate through collections of data by setting the `cursor` parameter to a `next_cursor` attribute returned by a previous request's `response_metadata`. Default value fetches the first 'page' of the collection. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_as_user",
|
||||
display_name="As User",
|
||||
info="Pass true to post the message as the authed user, instead of as a bot. Defaults to false",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_attachments",
|
||||
display_name="Attachments",
|
||||
info="A JSON-based array of structured attachments, presented as a URL-encoded string. ",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_blocks",
|
||||
display_name="Blocks",
|
||||
info="A JSON-based array of structured blocks, presented as a URL-encoded string. ",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_channel",
|
||||
display_name="Channel",
|
||||
info="Channel, private group, or DM channel to send message to. Can be an encoded ID, or a name",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_link_names",
|
||||
display_name="Link Names",
|
||||
info="Find and link channel names and usernames.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_parse",
|
||||
display_name="Parse",
|
||||
info="Change how messages are treated. Defaults to `none`",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_post_at",
|
||||
display_name="Post At",
|
||||
info="Unix EPOCH timestamp of time in future to send the message.",
|
||||
show=False,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_reply_broadcast",
|
||||
display_name="Reply Broadcast",
|
||||
info="Used in conjunction with `thread_ts` and indicates whether reply should be made visible to everyone in the channel or conversation. Defaults to `false`. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_text",
|
||||
display_name="Text",
|
||||
info="How this field works and whether it is required depends on other fields you use in your API call",
|
||||
show=False,
|
||||
),
|
||||
IntInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_thread_ts",
|
||||
display_name="Thread Ts",
|
||||
info="Provide another message's `ts` value to make this message a reply. Avoid using a reply's `ts` value; use its parent instead. ", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_unfurl_links",
|
||||
display_name="Unfurl Links",
|
||||
info="Pass true to enable unfurling of primarily text-based content.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SCHEDULES_A_MESSAGE_TO_A_CHANNEL_AT_A_SPECIFIED_TIME_unfurl_media",
|
||||
display_name="Unfurl Media",
|
||||
info="Pass false to disable unfurling of media content.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS_exclude_archived",
|
||||
display_name="Exclude Archived",
|
||||
info="Set to `true` to exclude archived channels from the list",
|
||||
show=False,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS_types",
|
||||
display_name="Types",
|
||||
info="Mix and match channel types by providing a comma-separated list of any combination of `public_channel`, `private_channel`, `mpim`, `im` ", # noqa: E501
|
||||
show=False,
|
||||
),
|
||||
IntInput(
|
||||
name="SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS_limit",
|
||||
display_name="Limit",
|
||||
info="The maximum number of items to return. Fewer than the requested number of items may be returned, even if the end of the list hasn't been reached. Must be an integer no larger than 1000. ", # noqa: E501
|
||||
show=False,
|
||||
value=1,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_LIST_ALL_SLACK_TEAM_CHANNELS_WITH_VARIOUS_FILTERS_cursor",
|
||||
display_name="Cursor",
|
||||
info="Paginate through collections of data by setting the `cursor` parameter to a `next_cursor` attribute returned by a previous request's `response_metadata`. Default value fetches the first 'page' of the collection", # noqa: E501
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_count",
|
||||
display_name="Count",
|
||||
info="Pass the number of results you want per 'page'. Maximum of `100`.",
|
||||
show=False,
|
||||
value=1,
|
||||
advanced=True,
|
||||
),
|
||||
BoolInput(
|
||||
name="SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_highlight",
|
||||
display_name="Highlight",
|
||||
info="Pass a value of `true` to enable query highlight markers",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
IntInput(
|
||||
name="SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_page",
|
||||
display_name="Page",
|
||||
info="Page",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_query",
|
||||
display_name="Query",
|
||||
info="Search query.",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_sort",
|
||||
display_name="Sort",
|
||||
info="Return matches sorted by either `score` or `timestamp`.",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY_sort_dir",
|
||||
display_name="Sort Dir",
|
||||
info="Change sort direction to ascending (`asc`) or descending (`desc`).",
|
||||
show=False,
|
||||
advanced=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_CREATE_A_REMINDER_text",
|
||||
display_name="Text",
|
||||
info="The content of the reminder",
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_CREATE_A_REMINDER_time",
|
||||
display_name="Time",
|
||||
info="When this reminder should happen: the Unix timestamp (up to five years from now), the number of seconds until the reminder (if within 24 hours), or a natural language description (Ex. 'in 15 minutes,' or 'every Thursday') ", # noqa: E501
|
||||
show=False,
|
||||
required=True,
|
||||
),
|
||||
MessageTextInput(
|
||||
name="SLACK_CREATE_A_REMINDER_user",
|
||||
display_name="User",
|
||||
info="The user who will receive the reminder. If no user is specified, the reminder will go to user who created it. ", # noqa: E501
|
||||
show=False,
|
||||
),
|
||||
]
|
||||
|
||||
def execute_action(self):
|
||||
"""Execute action and return response as Message."""
|
||||
toolset = self._build_wrapper()
|
||||
|
||||
try:
|
||||
self._build_action_maps()
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else self.action
|
||||
action_key = self._display_to_key_map.get(display_name)
|
||||
if not action_key:
|
||||
msg = f"Invalid action: {display_name}"
|
||||
raise ValueError(msg)
|
||||
|
||||
enum_name = getattr(Action, action_key)
|
||||
params = {}
|
||||
if action_key in self._actions_data:
|
||||
for field in self._actions_data[action_key]["action_fields"]:
|
||||
value = getattr(self, field)
|
||||
|
||||
if value is None or value == "":
|
||||
continue
|
||||
|
||||
if field in self._bool_variables:
|
||||
value = bool(value)
|
||||
|
||||
param_name = field.replace(action_key + "_", "")
|
||||
|
||||
if param_name == "as_user":
|
||||
value = True
|
||||
|
||||
params[param_name] = value
|
||||
|
||||
result = toolset.execute_action(
|
||||
action=enum_name,
|
||||
params=params,
|
||||
)
|
||||
if not result.get("successful"):
|
||||
return {"error": result.get("error", "No response")}
|
||||
|
||||
return result.get("data", [])
|
||||
except Exception as e:
|
||||
logger.error(f"Error executing action: {e}")
|
||||
display_name = self.action[0]["name"] if isinstance(self.action, list) and self.action else str(self.action)
|
||||
msg = f"Failed to execute {display_name}: {e!s}"
|
||||
raise ValueError(msg) from e
|
||||
|
||||
def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:
|
||||
return super().update_build_config(build_config, field_value, field_name)
|
||||
|
||||
def set_default_tools(self):
|
||||
self._default_tools = {
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL",
|
||||
"SLACK_SEARCH_FOR_MESSAGES_WITH_QUERY",
|
||||
}
|
||||
"""Set the default tools for Slack component."""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
|
||||
class ComposioSlackbotAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Slackbot"
|
||||
icon = "Slack"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "slackbot"
|
||||
|
||||
def set_default_tools(self):
|
||||
"""Set the default tools for Slackbot component."""
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
|
||||
class ComposioSupabaseAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Supabase"
|
||||
icon = "Supabase"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "supabase"
|
||||
|
||||
def set_default_tools(self):
|
||||
"""Set the default tools for Supabase component."""
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
|
||||
class ComposioTodoistAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Todoist"
|
||||
icon = "Todoist"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "todoist"
|
||||
|
||||
def set_default_tools(self):
|
||||
"""Set the default tools for Todoist component."""
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
|
||||
class ComposioYoutubeAPIComponent(ComposioBaseComponent):
|
||||
display_name: str = "Youtube"
|
||||
icon = "Youtube"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
app_name = "youtube"
|
||||
|
||||
def set_default_tools(self):
|
||||
"""Set the default tools for Youtube component."""
|
||||
|
|
@ -1,132 +0,0 @@
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from langflow.base.composio.composio_base import ComposioBaseComponent
|
||||
|
||||
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
|
||||
|
||||
|
||||
class MockComposioToolSet:
|
||||
def __init__(self, api_key=None):
|
||||
self.api_key = api_key
|
||||
self.client = MagicMock()
|
||||
|
||||
def get_tools(self, *_):
|
||||
return []
|
||||
|
||||
def execute_action(self, *_, **__):
|
||||
return {"successful": True, "data": {"result": "mocked response"}}
|
||||
|
||||
|
||||
class TestComposioBase(ComponentTestBaseWithoutClient):
|
||||
@pytest.fixture
|
||||
def component_class(self):
|
||||
class TestComponent(ComposioBaseComponent):
|
||||
def execute_action(self):
|
||||
return []
|
||||
|
||||
return TestComponent
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_composio_toolset(self):
|
||||
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
|
||||
yield
|
||||
|
||||
@pytest.fixture
|
||||
def default_kwargs(self):
|
||||
return {
|
||||
"api_key": "",
|
||||
"entity_id": "default",
|
||||
"action": None,
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def file_names_mapping(self):
|
||||
# Component not yet released, mark all versions as non-existent
|
||||
return [
|
||||
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
]
|
||||
|
||||
def test_build_wrapper_no_api_key(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
with pytest.raises(ValueError, match="Please provide a valid Composio API Key in the component settings"):
|
||||
component._build_wrapper()
|
||||
|
||||
def test_build_wrapper_with_api_key(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
wrapper = component._build_wrapper()
|
||||
assert isinstance(wrapper, MockComposioToolSet)
|
||||
assert wrapper.api_key == "test_key"
|
||||
|
||||
def test_build_action_maps(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
# Test with empty actions data
|
||||
component._actions_data = {}
|
||||
component._build_action_maps()
|
||||
assert component._display_to_key_map == {}
|
||||
assert component._key_to_display_map == {}
|
||||
assert component._sanitized_names == {}
|
||||
|
||||
# Test with sample actions data
|
||||
component._actions_data = {
|
||||
"ACTION_1": {"display_name": "Action One"},
|
||||
"ACTION_2": {"display_name": "Action Two"},
|
||||
}
|
||||
component._build_action_maps()
|
||||
assert component._display_to_key_map == {
|
||||
"Action One": "ACTION_1",
|
||||
"Action Two": "ACTION_2",
|
||||
}
|
||||
assert component._key_to_display_map == {
|
||||
"ACTION_1": "Action One",
|
||||
"ACTION_2": "Action Two",
|
||||
}
|
||||
|
||||
def test_get_action_fields(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
component._actions_data = {
|
||||
"ACTION_1": {"action_fields": ["field1", "field2"]},
|
||||
"ACTION_2": {"action_fields": ["field3"]},
|
||||
}
|
||||
|
||||
# Test with valid action key
|
||||
fields = component._get_action_fields("ACTION_1")
|
||||
assert fields == {"field1", "field2"}
|
||||
|
||||
# Test with non-existent action key
|
||||
fields = component._get_action_fields("NON_EXISTENT")
|
||||
assert fields == set()
|
||||
|
||||
# Test with None action key
|
||||
fields = component._get_action_fields(None)
|
||||
assert fields == set()
|
||||
|
||||
def test_show_hide_fields(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
component._all_fields = {"field1", "field2"}
|
||||
component._bool_variables = {"field2"}
|
||||
component._actions_data = {
|
||||
"ACTION_1": {"display_name": "Action One", "action_fields": ["field1"]},
|
||||
}
|
||||
|
||||
build_config = {
|
||||
"field1": {"show": False, "value": "old_value"},
|
||||
"field2": {"show": False, "value": True},
|
||||
}
|
||||
|
||||
# Test with no field value
|
||||
component.show_hide_fields(build_config, None)
|
||||
assert not build_config["field1"]["show"]
|
||||
assert not build_config["field2"]["show"]
|
||||
assert build_config["field1"]["value"] == ""
|
||||
assert build_config["field2"]["value"] is False
|
||||
|
||||
# Test with valid action
|
||||
component.show_hide_fields(build_config, [{"name": "Action One"}])
|
||||
assert build_config["field1"]["show"] # Should be shown since it's in ACTION_1's fields
|
||||
assert not build_config["field2"]["show"] # Should remain hidden
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from composio import Action
|
||||
from langflow.components.composio.github_composio import ComposioGitHubAPIComponent
|
||||
from langflow.schema.dataframe import DataFrame
|
||||
|
||||
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
|
||||
|
||||
from .test_base import MockComposioToolSet
|
||||
|
||||
|
||||
class MockAction:
|
||||
GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER = "GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER"
|
||||
GITHUB_LIST_BRANCHES = "GITHUB_LIST_BRANCHES"
|
||||
GITHUB_LIST_REPOSITORY_ISSUES = "GITHUB_LIST_REPOSITORY_ISSUES"
|
||||
|
||||
|
||||
class TestGitHubComponent(ComponentTestBaseWithoutClient):
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_composio_toolset(self):
|
||||
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
|
||||
yield
|
||||
|
||||
@pytest.fixture
|
||||
def component_class(self):
|
||||
return ComposioGitHubAPIComponent
|
||||
|
||||
@pytest.fixture
|
||||
def default_kwargs(self):
|
||||
return {
|
||||
"api_key": "",
|
||||
"entity_id": "default",
|
||||
"action": None,
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def file_names_mapping(self):
|
||||
# Component not yet released, mark all versions as non-existent
|
||||
return [
|
||||
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
]
|
||||
|
||||
def test_init(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
assert component.display_name == "GitHub"
|
||||
assert component.app_name == "github"
|
||||
assert "GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER" in component._actions_data
|
||||
assert "GITHUB_LIST_BRANCHES" in component._actions_data
|
||||
|
||||
def test_execute_action_star_a_repo(self, component_class, default_kwargs, monkeypatch):
|
||||
# Mock Action enum
|
||||
monkeypatch.setattr(
|
||||
Action,
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER",
|
||||
MockAction.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER,
|
||||
)
|
||||
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Star A Repository"}]
|
||||
component.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_owner = "langflow-ai"
|
||||
component.GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_repo = "langflow"
|
||||
|
||||
# For this specific test, customize the _actions_data to not use get_result_field
|
||||
component._actions_data = {
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER": {
|
||||
"display_name": "Star A Repository",
|
||||
"action_fields": [
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_owner",
|
||||
"GITHUB_STAR_A_REPOSITORY_FOR_THE_AUTHENTICATED_USER_repo",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
# Execute action
|
||||
result = component.execute_action()
|
||||
assert result == {"result": "mocked response"}
|
||||
|
||||
def test_execute_action_list_branches(self, component_class, default_kwargs, monkeypatch):
|
||||
# Mock Action enum
|
||||
monkeypatch.setattr(Action, "GITHUB_LIST_BRANCHES", MockAction.GITHUB_LIST_BRANCHES)
|
||||
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "List Branches"}]
|
||||
component.GITHUB_LIST_BRANCHES_owner = "langflow-ai"
|
||||
component.GITHUB_LIST_BRANCHES_repo = "langflow"
|
||||
|
||||
# For this specific test, customize the _actions_data to not use get_result_field
|
||||
component._actions_data = {
|
||||
"GITHUB_LIST_BRANCHES": {
|
||||
"display_name": "List Branches",
|
||||
"action_fields": [
|
||||
"GITHUB_LIST_BRANCHES_owner",
|
||||
"GITHUB_LIST_BRANCHES_repo",
|
||||
"GITHUB_LIST_BRANCHES_protected",
|
||||
"GITHUB_LIST_BRANCHES_per_page",
|
||||
"GITHUB_LIST_BRANCHES_page",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
# Execute action
|
||||
result = component.execute_action()
|
||||
assert result == {"result": "mocked response"}
|
||||
|
||||
def test_execute_action_list_repo_issues(self, component_class, default_kwargs, monkeypatch):
|
||||
# Mock Action enum
|
||||
monkeypatch.setattr(Action, "GITHUB_LIST_REPOSITORY_ISSUES", MockAction.GITHUB_LIST_REPOSITORY_ISSUES)
|
||||
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "List Repository Issues"}]
|
||||
component.GITHUB_LIST_REPOSITORY_ISSUES_owner = "langflow-ai"
|
||||
component.GITHUB_LIST_REPOSITORY_ISSUES_repo = "langflow"
|
||||
|
||||
# For this specific test, customize the _actions_data to not use get_result_field
|
||||
component._actions_data = {
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES": {
|
||||
"display_name": "List Repository Issues",
|
||||
"action_fields": [
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_owner",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_repo",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_milestone",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_state",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_assignee",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_creator",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_mentioned",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_labels",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_sort",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_direction",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_since",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_per_page",
|
||||
"GITHUB_LIST_REPOSITORY_ISSUES_page",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
# Execute action
|
||||
result = component.execute_action()
|
||||
assert result == {"result": "mocked response"}
|
||||
|
||||
def test_execute_action_invalid_action(self, component_class, default_kwargs):
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Invalid Action"}]
|
||||
|
||||
# Execute action should raise ValueError
|
||||
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
|
||||
component.execute_action()
|
||||
|
||||
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
|
||||
# Mock Action enum
|
||||
monkeypatch.setattr(Action, "GITHUB_LIST_REPOSITORY_ISSUES", MockAction.GITHUB_LIST_REPOSITORY_ISSUES)
|
||||
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "List Repository Issues"}]
|
||||
component.max_results = 10
|
||||
|
||||
# Create mock email data that would be returned by execute_action
|
||||
mock_issues = [
|
||||
{
|
||||
"url": "url1",
|
||||
"repository_url": "repository_url1",
|
||||
"id": "id1",
|
||||
"title": "test issue",
|
||||
"state": "open",
|
||||
},
|
||||
{
|
||||
"url": "url2",
|
||||
"repository_url": "repository_url2",
|
||||
"id": "id2",
|
||||
"title": "test issue",
|
||||
"state": "open",
|
||||
},
|
||||
]
|
||||
|
||||
# Mock the execute_action method to return our mock data
|
||||
with patch.object(component, "execute_action", return_value=mock_issues):
|
||||
# Test as_dataframe method
|
||||
result = component.as_dataframe()
|
||||
|
||||
# Verify the result is a DataFrame
|
||||
assert isinstance(result, DataFrame)
|
||||
|
||||
# Verify the DataFrame is not empty
|
||||
assert not result.empty
|
||||
|
||||
# Check for expected content in the DataFrame string representation
|
||||
data_str = str(result)
|
||||
assert "test issue" in data_str
|
||||
|
||||
def test_update_build_config(self, component_class, default_kwargs):
|
||||
# Test that the GitHub component properly inherits and uses the base component's
|
||||
# update_build_config method
|
||||
component = component_class(**default_kwargs)
|
||||
build_config = {
|
||||
"auth_link": {"value": "", "auth_tooltip": ""},
|
||||
"action": {
|
||||
"options": [],
|
||||
"helper_text": "",
|
||||
"helper_text_metadata": {},
|
||||
},
|
||||
}
|
||||
|
||||
# Test with empty API key
|
||||
result = component.update_build_config(build_config, "", "api_key")
|
||||
assert result["auth_link"]["value"] == ""
|
||||
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
|
||||
assert result["action"]["options"] == []
|
||||
|
||||
# Test with valid API key
|
||||
component.api_key = "test_key"
|
||||
result = component.update_build_config(build_config, "test_key", "api_key")
|
||||
assert len(result["action"]["options"]) > 0 # Should have GitHub actions
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from composio import Action
|
||||
from langflow.components.composio.gmail_composio import ComposioGmailAPIComponent
|
||||
from langflow.schema.dataframe import DataFrame
|
||||
|
||||
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
|
||||
|
||||
from .test_base import MockComposioToolSet
|
||||
|
||||
|
||||
class MockAction:
|
||||
GMAIL_SEND_EMAIL = "GMAIL_SEND_EMAIL"
|
||||
GMAIL_FETCH_EMAILS = "GMAIL_FETCH_EMAILS"
|
||||
GMAIL_GET_PROFILE = "GMAIL_GET_PROFILE"
|
||||
|
||||
|
||||
class TestGmailComponent(ComponentTestBaseWithoutClient):
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_composio_toolset(self):
|
||||
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
|
||||
yield
|
||||
|
||||
@pytest.fixture
|
||||
def component_class(self):
|
||||
return ComposioGmailAPIComponent
|
||||
|
||||
@pytest.fixture
|
||||
def default_kwargs(self):
|
||||
return {
|
||||
"api_key": "",
|
||||
"entity_id": "default",
|
||||
"action": None,
|
||||
"recipient_email": "",
|
||||
"subject": "",
|
||||
"body": "",
|
||||
"is_html": False,
|
||||
"max_results": 10,
|
||||
"query": "",
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def file_names_mapping(self):
|
||||
# Component not yet released, mark all versions as non-existent
|
||||
return [
|
||||
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
]
|
||||
|
||||
def test_init(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
assert component.display_name == "Gmail"
|
||||
assert component.name == "GmailAPI"
|
||||
assert component.app_name == "gmail"
|
||||
assert "GMAIL_SEND_EMAIL" in component._actions_data
|
||||
assert "GMAIL_FETCH_EMAILS" in component._actions_data
|
||||
|
||||
def test_execute_action_send_email(self, component_class, default_kwargs, monkeypatch):
|
||||
# Mock Action enum
|
||||
monkeypatch.setattr(Action, "GMAIL_SEND_EMAIL", MockAction.GMAIL_SEND_EMAIL)
|
||||
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Send Email"}]
|
||||
component.recipient_email = "test@example.com"
|
||||
component.subject = "Test Subject"
|
||||
component.body = "Test Body"
|
||||
component.is_html = False
|
||||
|
||||
# For this specific test, customize the _actions_data to not use get_result_field
|
||||
component._actions_data = {
|
||||
"GMAIL_SEND_EMAIL": {
|
||||
"display_name": "Send Email",
|
||||
"action_fields": ["recipient_email", "subject", "body", "is_html"],
|
||||
"get_result_field": False,
|
||||
}
|
||||
}
|
||||
|
||||
# Execute action
|
||||
result = component.execute_action()
|
||||
assert result == {"result": "mocked response"}
|
||||
|
||||
def test_execute_action_fetch_emails(self, component_class, default_kwargs, monkeypatch):
|
||||
# Mock Action enum
|
||||
monkeypatch.setattr(Action, "GMAIL_FETCH_EMAILS", MockAction.GMAIL_FETCH_EMAILS)
|
||||
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Fetch Emails"}]
|
||||
component.max_results = 10
|
||||
component.query = "from:test@example.com"
|
||||
|
||||
# For this specific test, we need to customize the action_data to handle results field
|
||||
component._actions_data = {
|
||||
"GMAIL_FETCH_EMAILS": {
|
||||
"display_name": "Fetch Emails",
|
||||
"action_fields": ["max_results", "query"],
|
||||
"result_field": "messages",
|
||||
"get_result_field": True,
|
||||
}
|
||||
}
|
||||
|
||||
# Create a mock for the toolset with specific structure for this test
|
||||
mock_toolset = MagicMock()
|
||||
mock_toolset.execute_action.return_value = {"successful": True, "data": {"messages": "mocked response"}}
|
||||
|
||||
# Patch the _build_wrapper method
|
||||
with patch.object(component, "_build_wrapper", return_value=mock_toolset):
|
||||
result = component.execute_action()
|
||||
# Based on the component's actual behavior, it returns the result_field directly
|
||||
assert result == "mocked response"
|
||||
|
||||
def test_execute_action_get_profile(self, component_class, default_kwargs, monkeypatch):
|
||||
# Mock Action enum
|
||||
monkeypatch.setattr(Action, "GMAIL_GET_PROFILE", MockAction.GMAIL_GET_PROFILE)
|
||||
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Get User Profile"}]
|
||||
|
||||
# For this specific test, customize the _actions_data to not use get_result_field
|
||||
component._actions_data = {
|
||||
"GMAIL_GET_PROFILE": {
|
||||
"display_name": "Get User Profile",
|
||||
"action_fields": ["gmail_user_id"],
|
||||
"get_result_field": False,
|
||||
}
|
||||
}
|
||||
|
||||
# Execute action
|
||||
result = component.execute_action()
|
||||
assert result == {"result": "mocked response"}
|
||||
|
||||
def test_execute_action_invalid_action(self, component_class, default_kwargs):
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Invalid Action"}]
|
||||
|
||||
# Execute action should raise ValueError
|
||||
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
|
||||
component.execute_action()
|
||||
|
||||
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
|
||||
# Mock Action enum
|
||||
monkeypatch.setattr(Action, "GMAIL_FETCH_EMAILS", MockAction.GMAIL_FETCH_EMAILS)
|
||||
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Fetch Emails"}]
|
||||
component.max_results = 10
|
||||
|
||||
# Create mock email data that would be returned by execute_action
|
||||
mock_emails = [
|
||||
{
|
||||
"id": "1",
|
||||
"threadId": "thread1",
|
||||
"subject": "Test Email 1",
|
||||
"from": "sender1@example.com",
|
||||
"date": "2023-01-01",
|
||||
"snippet": "This is a test email",
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"threadId": "thread2",
|
||||
"subject": "Test Email 2",
|
||||
"from": "sender2@example.com",
|
||||
"date": "2023-01-02",
|
||||
"snippet": "This is another test email",
|
||||
},
|
||||
]
|
||||
|
||||
# Mock the execute_action method to return our mock data
|
||||
with patch.object(component, "execute_action", return_value=mock_emails):
|
||||
# Test as_dataframe method
|
||||
result = component.as_dataframe()
|
||||
|
||||
# Verify the result is a DataFrame
|
||||
assert isinstance(result, DataFrame)
|
||||
|
||||
# Verify the DataFrame is not empty
|
||||
assert not result.empty
|
||||
|
||||
# Check for expected content in the DataFrame string representation
|
||||
data_str = str(result)
|
||||
assert "test email" in data_str
|
||||
|
||||
def test_update_build_config(self, component_class, default_kwargs):
|
||||
# Test that the Gmail component properly inherits and uses the base component's
|
||||
# update_build_config method
|
||||
component = component_class(**default_kwargs)
|
||||
build_config = {
|
||||
"auth_link": {"value": "", "auth_tooltip": ""},
|
||||
"action": {
|
||||
"options": [],
|
||||
"helper_text": "",
|
||||
"helper_text_metadata": {},
|
||||
},
|
||||
}
|
||||
|
||||
# Test with empty API key
|
||||
result = component.update_build_config(build_config, "", "api_key")
|
||||
assert result["auth_link"]["value"] == ""
|
||||
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
|
||||
assert result["action"]["options"] == []
|
||||
|
||||
# Test with valid API key
|
||||
component.api_key = "test_key"
|
||||
result = component.update_build_config(build_config, "test_key", "api_key")
|
||||
assert len(result["action"]["options"]) > 0 # Should have Gmail actions
|
||||
|
|
@ -1,182 +0,0 @@
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from composio import Action
|
||||
from langflow.components.composio.googlecalendar_composio import ComposioGoogleCalendarAPIComponent
|
||||
from langflow.schema.dataframe import DataFrame
|
||||
|
||||
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
|
||||
|
||||
from .test_base import MockComposioToolSet
|
||||
|
||||
|
||||
class MockAction:
|
||||
GOOGLECALENDAR_CREATE_EVENT = "GOOGLECALENDAR_CREATE_EVENT"
|
||||
GOOGLECALENDAR_LIST_CALENDARS = "GOOGLECALENDAR_LIST_CALENDARS"
|
||||
|
||||
|
||||
class TestGoogleCalendarComponent(ComponentTestBaseWithoutClient):
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_composio_toolset(self):
|
||||
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
|
||||
yield
|
||||
|
||||
@pytest.fixture
|
||||
def component_class(self):
|
||||
return ComposioGoogleCalendarAPIComponent
|
||||
|
||||
@pytest.fixture
|
||||
def default_kwargs(self):
|
||||
return {
|
||||
"api_key": "",
|
||||
"entity_id": "default",
|
||||
"action": None,
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def file_names_mapping(self):
|
||||
return [
|
||||
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
]
|
||||
|
||||
def test_init(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
assert component.display_name == "Google Calendar"
|
||||
assert component.app_name == "googlecalendar"
|
||||
assert "GOOGLECALENDAR_CREATE_EVENT" in component._actions_data
|
||||
assert "GOOGLECALENDAR_LIST_CALENDARS" in component._actions_data
|
||||
|
||||
def test_execute_action_create_event(self, component_class, default_kwargs, monkeypatch):
|
||||
monkeypatch.setattr(Action, "GOOGLECALENDAR_CREATE_EVENT", MockAction.GOOGLECALENDAR_CREATE_EVENT)
|
||||
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Create Event"}]
|
||||
component.GOOGLECALENDAR_CREATE_EVENT_attendees = "test@example.com"
|
||||
component.GOOGLECALENDAR_CREATE_EVENT_start_datetime = "2025-01-16T15:00:00"
|
||||
component.GOOGLECALENDAR_CREATE_EVENT_summary = "test title"
|
||||
|
||||
component._actions_data = {
|
||||
"GOOGLECALENDAR_CREATE_EVENT": {
|
||||
"display_name": "Create Event",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_CREATE_EVENT_description",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_eventType",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_create_meeting_room",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_guestsCanSeeOtherGuests",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_guestsCanInviteOthers",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_location",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_summary",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_transparency",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_visibility",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_timezone",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_recurrence",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_guests_can_modify",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_attendees",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_send_updates",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_start_datetime",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_event_duration_hour",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_event_duration_minutes",
|
||||
"GOOGLECALENDAR_CREATE_EVENT_calendar_id",
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
result = component.execute_action()
|
||||
assert result == "mocked response"
|
||||
|
||||
def test_execute_action_list_calendars(self, component_class, default_kwargs, monkeypatch):
|
||||
monkeypatch.setattr(Action, "GOOGLECALENDAR_LIST_CALENDARS", MockAction.GOOGLECALENDAR_LIST_CALENDARS)
|
||||
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "List Google Calendars"}]
|
||||
component.GOOGLECALENDAR_LIST_CALENDARS_max_results = 1
|
||||
|
||||
component._actions_data = {
|
||||
"GOOGLECALENDAR_LIST_CALENDARS": {
|
||||
"display_name": "List Google Calendars",
|
||||
"action_fields": [
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_max_results",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_min_access_role",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_page_token",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_show_deleted",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_show_hidden",
|
||||
"GOOGLECALENDAR_LIST_CALENDARS_sync_token",
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
mock_toolset = MagicMock()
|
||||
mock_toolset.execute_action.return_value = {"successful": True, "data": {"messages": "mocked response"}}
|
||||
|
||||
with patch.object(component, "_build_wrapper", return_value=mock_toolset):
|
||||
result = component.execute_action()
|
||||
assert result == "mocked response"
|
||||
|
||||
def test_execute_action_invalid_action(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Invalid Action"}]
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
|
||||
component.execute_action()
|
||||
|
||||
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
|
||||
monkeypatch.setattr(Action, "GOOGLECALENDAR_LIST_CALENDARS", MockAction.GOOGLECALENDAR_LIST_CALENDARS)
|
||||
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "List Google Calendars"}]
|
||||
component.GOOGLECALENDAR_LIST_CALENDARS_max_results = 10
|
||||
|
||||
mock_emails = [
|
||||
{
|
||||
"kind": "test kind 1",
|
||||
"etag": "1",
|
||||
"id": "1",
|
||||
"summary": "test summary 1",
|
||||
"description": "test description 1",
|
||||
},
|
||||
{
|
||||
"kind": "test kind 2",
|
||||
"etag": "2",
|
||||
"id": "2",
|
||||
"summary": "test summary 2",
|
||||
"description": "test description 2",
|
||||
},
|
||||
]
|
||||
|
||||
with patch.object(component, "execute_action", return_value=mock_emails):
|
||||
result = component.as_dataframe()
|
||||
|
||||
assert isinstance(result, DataFrame)
|
||||
|
||||
assert not result.empty
|
||||
|
||||
data_str = str(result)
|
||||
assert "test summary 2" in data_str
|
||||
|
||||
def test_update_build_config(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
build_config = {
|
||||
"auth_link": {"value": "", "auth_tooltip": ""},
|
||||
"action": {
|
||||
"options": [],
|
||||
"helper_text": "",
|
||||
"helper_text_metadata": {},
|
||||
},
|
||||
}
|
||||
|
||||
result = component.update_build_config(build_config, "", "api_key")
|
||||
assert result["auth_link"]["value"] == ""
|
||||
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
|
||||
assert result["action"]["options"] == []
|
||||
|
||||
component.api_key = "test_key"
|
||||
result = component.update_build_config(build_config, "test_key", "api_key")
|
||||
assert len(result["action"]["options"]) > 0
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from composio import Action
|
||||
from langflow.components.composio.outlook_composio import ComposioOutlookAPIComponent
|
||||
from langflow.schema.dataframe import DataFrame
|
||||
|
||||
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
|
||||
|
||||
from .test_base import MockComposioToolSet
|
||||
|
||||
|
||||
class MockAction:
|
||||
OUTLOOK_OUTLOOK_SEND_EMAIL = "OUTLOOK_OUTLOOK_SEND_EMAIL"
|
||||
OUTLOOK_OUTLOOK_LIST_MESSAGES = "OUTLOOK_OUTLOOK_LIST_MESSAGES"
|
||||
|
||||
|
||||
class TestOutlookComponent(ComponentTestBaseWithoutClient):
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_composio_toolset(self):
|
||||
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
|
||||
yield
|
||||
|
||||
@pytest.fixture
|
||||
def component_class(self):
|
||||
return ComposioOutlookAPIComponent
|
||||
|
||||
@pytest.fixture
|
||||
def default_kwargs(self):
|
||||
return {
|
||||
"api_key": "",
|
||||
"entity_id": "default",
|
||||
"action": None,
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def file_names_mapping(self):
|
||||
return [
|
||||
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
]
|
||||
|
||||
def test_init(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
assert component.display_name == "Outlook"
|
||||
assert component.app_name == "outlook"
|
||||
assert "OUTLOOK_OUTLOOK_SEND_EMAIL" in component._actions_data
|
||||
assert "OUTLOOK_OUTLOOK_LIST_MESSAGES" in component._actions_data
|
||||
|
||||
def test_execute_action_send_email(self, component_class, default_kwargs, monkeypatch):
|
||||
monkeypatch.setattr(Action, "OUTLOOK_OUTLOOK_SEND_EMAIL", MockAction.OUTLOOK_OUTLOOK_SEND_EMAIL)
|
||||
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Send Email"}]
|
||||
component.OUTLOOK_OUTLOOK_SEND_EMAIL_subject = "Test Subject"
|
||||
component.OUTLOOK_OUTLOOK_SEND_EMAIL_body = "Test Body"
|
||||
component.OUTLOOK_OUTLOOK_SEND_EMAIL_to_email = "test@example.com"
|
||||
|
||||
component._actions_data = {
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL": {
|
||||
"display_name": "Send Email",
|
||||
"action_fields": [
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_user_id",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_subject",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_body",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_to_email",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_to_name",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_cc_emails",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_bcc_emails",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_is_html",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_save_to_sent_items",
|
||||
"OUTLOOK_OUTLOOK_SEND_EMAIL_attachment",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
result = component.execute_action()
|
||||
assert result == {"result": "mocked response"}
|
||||
|
||||
def test_execute_action_fetch_emails(self, component_class, default_kwargs, monkeypatch):
|
||||
monkeypatch.setattr(Action, "OUTLOOK_OUTLOOK_LIST_MESSAGES", MockAction.OUTLOOK_OUTLOOK_LIST_MESSAGES)
|
||||
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "List Messages"}]
|
||||
component.OUTLOOK_OUTLOOK_LIST_MESSAGES_folder = "Inbox"
|
||||
component.OUTLOOK_OUTLOOK_LIST_MESSAGES_top = 10
|
||||
|
||||
component._actions_data = {
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES": {
|
||||
"display_name": "List Messages",
|
||||
"action_fields": [
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_user_id",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_folder",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_top",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_skip",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_is_read",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_importance",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_gt",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_startswith",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_endswith",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_subject_contains",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_ge",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_lt",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_received_date_time_le",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_from_address",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_has_attachments",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_body_preview_contains",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_sent_date_time_gt",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_sent_date_time_lt",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_categories",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_select",
|
||||
"OUTLOOK_OUTLOOK_LIST_MESSAGES_orderby",
|
||||
],
|
||||
"get_result_field": True,
|
||||
"result_field": "value",
|
||||
},
|
||||
}
|
||||
|
||||
mock_toolset = MagicMock()
|
||||
mock_toolset.execute_action.return_value = {
|
||||
"successful": True,
|
||||
"data": {"response_data": {"value": [{"subject": "Test Email", "from": "test@example.com"}]}},
|
||||
}
|
||||
|
||||
with patch.object(component, "_build_wrapper", return_value=mock_toolset):
|
||||
result = component.execute_action()
|
||||
assert result == [{"subject": "Test Email", "from": "test@example.com"}]
|
||||
|
||||
def test_execute_action_invalid_action(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Invalid Action"}]
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
|
||||
component.execute_action()
|
||||
|
||||
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
|
||||
monkeypatch.setattr(Action, "OUTLOOK_OUTLOOK_SEND_EMAIL", MockAction.OUTLOOK_OUTLOOK_SEND_EMAIL)
|
||||
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Send Email"}]
|
||||
component.OUTLOOK_OUTLOOK_SEND_EMAIL_subject = "Test Subject"
|
||||
component.OUTLOOK_OUTLOOK_SEND_EMAIL_body = "Test Body"
|
||||
component.OUTLOOK_OUTLOOK_SEND_EMAIL_to_email = "test@example.com"
|
||||
|
||||
mock_emails = [
|
||||
{
|
||||
"message": "Email sent successfully.",
|
||||
}
|
||||
]
|
||||
with patch.object(component, "execute_action", return_value=mock_emails):
|
||||
result = component.as_dataframe()
|
||||
|
||||
assert isinstance(result, DataFrame)
|
||||
assert not result.empty
|
||||
data_str = str(result)
|
||||
assert "Email sent successfully." in data_str
|
||||
|
||||
def test_update_build_config(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
build_config = {
|
||||
"auth_link": {"value": "", "auth_tooltip": ""},
|
||||
"action": {
|
||||
"options": [],
|
||||
"helper_text": "",
|
||||
"helper_text_metadata": {},
|
||||
},
|
||||
}
|
||||
|
||||
result = component.update_build_config(build_config, "", "api_key")
|
||||
assert result["auth_link"]["value"] == ""
|
||||
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
|
||||
assert result["action"]["options"] == []
|
||||
|
||||
component.api_key = "test_key"
|
||||
result = component.update_build_config(build_config, "test_key", "api_key")
|
||||
assert len(result["action"]["options"]) > 0
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from composio import Action
|
||||
from langflow.components.composio.slack_composio import ComposioSlackAPIComponent
|
||||
from langflow.schema.dataframe import DataFrame
|
||||
|
||||
from tests.base import DID_NOT_EXIST, ComponentTestBaseWithoutClient
|
||||
|
||||
from .test_base import MockComposioToolSet
|
||||
|
||||
|
||||
class MockAction:
|
||||
SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL = "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL"
|
||||
SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION = "SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION"
|
||||
|
||||
|
||||
class TestSlackComponent(ComponentTestBaseWithoutClient):
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_composio_toolset(self):
|
||||
with patch("langflow.base.composio.composio_base.ComposioToolSet", MockComposioToolSet):
|
||||
yield
|
||||
|
||||
@pytest.fixture
|
||||
def component_class(self):
|
||||
return ComposioSlackAPIComponent
|
||||
|
||||
@pytest.fixture
|
||||
def default_kwargs(self):
|
||||
return {
|
||||
"api_key": "",
|
||||
"entity_id": "default",
|
||||
"action": None,
|
||||
}
|
||||
|
||||
@pytest.fixture
|
||||
def file_names_mapping(self):
|
||||
return [
|
||||
{"version": "1.0.17", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.18", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.0.19", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.0", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
{"version": "1.1.1", "module": "composio", "file_name": DID_NOT_EXIST},
|
||||
]
|
||||
|
||||
def test_init(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
assert component.display_name == "Slack"
|
||||
assert component.app_name == "slack"
|
||||
assert "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL" in component._actions_data
|
||||
assert "SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION" in component._actions_data
|
||||
|
||||
def test_execute_action_send_message_to_channel(self, component_class, default_kwargs, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
Action, "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL", MockAction.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL
|
||||
)
|
||||
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Post Message To Channel"}]
|
||||
component.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_channel = "random"
|
||||
component.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_text = "Test Body"
|
||||
|
||||
component._actions_data = {
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL": {
|
||||
"display_name": "Post Message To Channel",
|
||||
"action_fields": [
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_as_user",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_attachments",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_blocks",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_channel",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_icon_emoji",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_icon_url",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_link_names",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_mrkdwn",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_parse",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_reply_broadcast",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_text",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_thread_ts",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_links",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_unfurl_media",
|
||||
"SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_username",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
result = component.execute_action()
|
||||
assert result == {"result": "mocked response"}
|
||||
|
||||
def test_execute_action_list_all_slack_team_users(self, component_class, default_kwargs, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
Action,
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION",
|
||||
MockAction.SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION,
|
||||
)
|
||||
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "List Users"}]
|
||||
component.SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_limit = 1
|
||||
|
||||
component._actions_data = {
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION": {
|
||||
"display_name": "List Users",
|
||||
"action_fields": [
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_limit",
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_cursor",
|
||||
"SLACK_LIST_ALL_SLACK_TEAM_USERS_WITH_PAGINATION_include_locale",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mock_toolset = MagicMock()
|
||||
mock_toolset.execute_action.return_value = {"successful": True, "data": {"messages": "mocked response"}}
|
||||
|
||||
with patch.object(component, "_build_wrapper", return_value=mock_toolset):
|
||||
result = component.execute_action()
|
||||
assert result == {"messages": "mocked response"}
|
||||
|
||||
def test_execute_action_invalid_action(self, component_class, default_kwargs):
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Invalid Action"}]
|
||||
|
||||
# Execute action should raise ValueError
|
||||
with pytest.raises(ValueError, match="Invalid action: Invalid Action"):
|
||||
component.execute_action()
|
||||
|
||||
def test_as_dataframe(self, component_class, default_kwargs, monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
Action, "SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL", MockAction.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL
|
||||
)
|
||||
|
||||
# Setup component
|
||||
component = component_class(**default_kwargs)
|
||||
component.api_key = "test_key"
|
||||
component.action = [{"name": "Post Message To Channel"}]
|
||||
component.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_channel = "random"
|
||||
component.SLACK_SENDS_A_MESSAGE_TO_A_SLACK_CHANNEL_text = "Test Body"
|
||||
|
||||
mock_slack_messages = [
|
||||
{"channel": "channel1", "user": "user1", "text": "text message 1", "ts": "ts1", "team": "team1"},
|
||||
{"channel": "channel2", "user": "user2", "text": "text message 2", "ts": "ts2", "team": "team2"},
|
||||
]
|
||||
|
||||
with patch.object(component, "execute_action", return_value=mock_slack_messages):
|
||||
result = component.as_dataframe()
|
||||
|
||||
assert isinstance(result, DataFrame)
|
||||
|
||||
assert not result.empty
|
||||
|
||||
data_str = str(result)
|
||||
assert "text message 1" in data_str
|
||||
assert "text message 2" in data_str
|
||||
|
||||
def test_update_build_config(self, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
build_config = {
|
||||
"auth_link": {"value": "", "auth_tooltip": ""},
|
||||
"action": {
|
||||
"options": [],
|
||||
"helper_text": "",
|
||||
"helper_text_metadata": {},
|
||||
},
|
||||
}
|
||||
|
||||
result = component.update_build_config(build_config, "", "api_key")
|
||||
assert result["auth_link"]["value"] == ""
|
||||
assert "Please provide a valid Composio API Key" in result["auth_link"]["auth_tooltip"]
|
||||
assert result["action"]["options"] == []
|
||||
|
||||
component.api_key = "test_key"
|
||||
result = component.update_build_config(build_config, "test_key", "api_key")
|
||||
assert len(result["action"]["options"]) > 0
|
||||
|
|
@ -70,6 +70,7 @@ export default function NodeStatus({
|
|||
);
|
||||
|
||||
const connectionLink = nodeAuth?.value;
|
||||
const apiKeyValue = (data.node?.template as any)?.api_key?.value ?? "";
|
||||
const isAuthenticated = nodeAuth?.value === "validated";
|
||||
|
||||
const pollingInterval = useRef<NodeJS.Timeout | null>(null);
|
||||
|
|
@ -104,33 +105,61 @@ export default function NodeStatus({
|
|||
|
||||
// Start polling when connection is initiated
|
||||
const startPolling = () => {
|
||||
customOpenNewTab(connectionLink);
|
||||
stopPolling();
|
||||
|
||||
setIsPolling(true);
|
||||
|
||||
pollingInterval.current = setInterval(() => {
|
||||
mutateTemplate(
|
||||
{ validate: data.node?.template?.auth?.value || "" },
|
||||
data.id,
|
||||
data.node,
|
||||
(newNode) => {
|
||||
setNode(nodeId, (old) => ({
|
||||
...old,
|
||||
data: { ...old.data, node: newNode },
|
||||
}));
|
||||
},
|
||||
postTemplateValue,
|
||||
setErrorData,
|
||||
nodeAuth?.name ?? "auth_link",
|
||||
() => {},
|
||||
data.node.tool_mode,
|
||||
);
|
||||
}, POLLING_INTERVAL);
|
||||
mutateTemplate(
|
||||
{ validate: "" },
|
||||
data.id,
|
||||
data.node,
|
||||
(newNode) => {
|
||||
setNode(nodeId, (old) => ({
|
||||
...old,
|
||||
data: { ...old.data, node: newNode },
|
||||
}));
|
||||
|
||||
pollingTimeout.current = setTimeout(() => {
|
||||
stopPolling();
|
||||
}, POLLING_TIMEOUT);
|
||||
const updatedAuth = Object.values(newNode?.template ?? {}).find(
|
||||
(value: any) => value?.type === "auth",
|
||||
) as any;
|
||||
const oauthUrl = updatedAuth?.value;
|
||||
|
||||
if (
|
||||
oauthUrl &&
|
||||
typeof oauthUrl === "string" &&
|
||||
oauthUrl.startsWith("http")
|
||||
) {
|
||||
customOpenNewTab(oauthUrl);
|
||||
}
|
||||
},
|
||||
postTemplateValue,
|
||||
setErrorData,
|
||||
nodeAuth?.name ?? "auth_link",
|
||||
() => {
|
||||
pollingInterval.current = setInterval(() => {
|
||||
mutateTemplate(
|
||||
{ validate: data.node?.template?.auth?.value || "" },
|
||||
data.id,
|
||||
data.node,
|
||||
(newNode) => {
|
||||
setNode(nodeId, (old) => ({
|
||||
...old,
|
||||
data: { ...old.data, node: newNode },
|
||||
}));
|
||||
},
|
||||
postTemplateValue,
|
||||
setErrorData,
|
||||
nodeAuth?.name ?? "auth_link",
|
||||
() => {},
|
||||
data.node.tool_mode,
|
||||
);
|
||||
}, POLLING_INTERVAL);
|
||||
|
||||
pollingTimeout.current = setTimeout(() => {
|
||||
stopPolling();
|
||||
}, POLLING_TIMEOUT);
|
||||
},
|
||||
data.node.tool_mode,
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -316,7 +345,9 @@ export default function NodeStatus({
|
|||
: isAuthenticated && !isPolling
|
||||
? "border-accent-emerald-foreground hover:border-accent-amber-foreground"
|
||||
: "",
|
||||
connectionLink === "" && "cursor-not-allowed opacity-50",
|
||||
connectionLink === "" &&
|
||||
(!apiKeyValue || apiKeyValue === "COMPOSIO_API_KEY") &&
|
||||
"cursor-not-allowed opacity-50",
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -394,7 +425,11 @@ export default function NodeStatus({
|
|||
<div>
|
||||
<Button
|
||||
unstyled
|
||||
disabled={connectionLink === "" || connectionLink === "error"}
|
||||
disabled={
|
||||
(connectionLink === "" &&
|
||||
(!apiKeyValue || apiKeyValue === "COMPOSIO_API_KEY")) ||
|
||||
connectionLink === "error"
|
||||
}
|
||||
className={getConnectionButtonClasses(
|
||||
connectionLink,
|
||||
isAuthenticated,
|
||||
|
|
|
|||
21
src/frontend/src/icons/GoogleTasks/googletasks.jsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const GoogleTasks = (props) => (
|
||||
<svg
|
||||
version="1.0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 755.78 560.77"
|
||||
height="23"
|
||||
width="23"
|
||||
{...props}
|
||||
>
|
||||
<polygon
|
||||
fill="#0066DA"
|
||||
points="505.79,113.39 469.59,133.29 451.59,167.59 469.59,209.59 503.59,246.39 540.29,225.89 562.89,186.99 540.29,138.79"
|
||||
/>
|
||||
<path
|
||||
fill="#2684FC"
|
||||
d="M365.49,253.69l86.2-86.2c25.4,19.3,44,46.9,51.9,78.8l-118.3,118.4c-10.6,10.6-27.9,10.6-38.5,0l-79.7-79.7c-8.5-8.5-8.5-22.3,0-30.8l34-34c8.5-8.5,22.3-8.5,30.8,0L365.49,253.69z M601.19,117.99l-34.6-34.6c-8.5-8.5-22.3-8.5-30.8,0l-30,30c24,20,43.5,45.1,57.1,73.6l38.3-38.3C609.69,140.29,609.69,126.39,601.19,117.99z M507.69,280.39c0,78.2-63.4,141.6-141.6,141.6s-141.6-63.4-141.6-141.6s63.3-141.6,141.6-141.6c32.2,0,61.8,10.7,85.6,28.8l54.2-54.2c-37.8-31.8-86.5-50.9-139.8-50.9c-120.4,0.1-217.9,97.6-217.9,217.9s97.5,217.8,217.8,217.8s217.9-97.5,217.9-217.8c0-33.4-7.6-65.1-21-93.4l-59.3,59.4C506.19,257.29,507.69,268.69,507.69,280.39z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default GoogleTasks;
|
||||
1
src/frontend/src/icons/GoogleTasks/googletasks.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 755.78 560.77" style="enable-background:new 0 0 755.78 560.77;" xml:space="preserve" height="17" width="23"><style type="text/css"> .st0{fill:#0066DA;} .st1{fill:#2684FC;} .st2{fill:none;}</style><g> <polygon class="st0" points="505.79,113.39 469.59,133.29 451.59,167.59 469.59,209.59 503.59,246.39 540.29,225.89 562.89,186.99 540.29,138.79 " /> <path class="st1" d="M365.49,253.69l86.2-86.2c25.4,19.3,44,46.9,51.9,78.8l-118.3,118.4c-10.6,10.6-27.9,10.6-38.5,0l-79.7-79.7 c-8.5-8.5-8.5-22.3,0-30.8l34-34c8.5-8.5,22.3-8.5,30.8,0L365.49,253.69z M601.19,117.99l-34.6-34.6c-8.5-8.5-22.3-8.5-30.8,0 l-30,30c24,20,43.5,45.1,57.1,73.6l38.3-38.3C609.69,140.29,609.69,126.39,601.19,117.99z M507.69,280.39 c0,78.2-63.4,141.6-141.6,141.6s-141.6-63.4-141.6-141.6s63.3-141.6,141.6-141.6c32.2,0,61.8,10.7,85.6,28.8l54.2-54.2 c-37.8-31.8-86.5-50.9-139.8-50.9c-120.4,0.1-217.9,97.6-217.9,217.9s97.5,217.8,217.8,217.8s217.9-97.5,217.9-217.8 c0-33.4-7.6-65.1-21-93.4l-59.3,59.4C506.19,257.29,507.69,268.69,507.69,280.39z" /></g><rect x="127.89" y="30.39" class="st2" width="500" height="500" /></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
9
src/frontend/src/icons/GoogleTasks/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import GoogleTasksIconSVG from "./googletasks";
|
||||
|
||||
export const GoogleTasksIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <GoogleTasksIconSVG ref={ref} {...props} />;
|
||||
});
|
||||
30
src/frontend/src/icons/googlemaps/googlemaps.jsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
const Icon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 48 48"
|
||||
width="23px"
|
||||
height="23px"
|
||||
>
|
||||
<path
|
||||
fill="#48b564"
|
||||
d="M35.76,26.36h0.01c0,0-3.77,5.53-6.94,9.64c-2.74,3.55-3.54,6.59-3.77,8.06 C24.97,44.6,24.53,45,24,45s-0.97-0.4-1.06-0.94c-0.23-1.47-1.03-4.51-3.77-8.06c-0.42-0.55-0.85-1.12-1.28-1.7L28.24,22l8.33-9.88 C37.49,14.05,38,16.21,38,18.5C38,21.4,37.17,24.09,35.76,26.36z"
|
||||
/>
|
||||
<path
|
||||
fill="#fcc60e"
|
||||
d="M28.24,22L17.89,34.3c-2.82-3.78-5.66-7.94-5.66-7.94h0.01c-0.3-0.48-0.57-0.97-0.8-1.48L19.76,15 c-0.79,0.95-1.26,2.17-1.26,3.5c0,3.04,2.46,5.5,5.5,5.5C25.71,24,27.24,23.22,28.24,22z"
|
||||
/>
|
||||
<path
|
||||
fill="#2c85eb"
|
||||
d="M28.4,4.74l-8.57,10.18L13.27,9.2C15.83,6.02,19.69,4,24,4C25.54,4,27.02,4.26,28.4,4.74z"
|
||||
/>
|
||||
<path
|
||||
fill="#ed5748"
|
||||
d="M19.83,14.92L19.76,15l-8.32,9.88C10.52,22.95,10,20.79,10,18.5c0-3.54,1.23-6.79,3.27-9.3 L19.83,14.92z"
|
||||
/>
|
||||
<path
|
||||
fill="#5695f6"
|
||||
d="M28.24,22c0.79-0.95,1.26-2.17,1.26-3.5c0-3.04-2.46-5.5-5.5-5.5c-1.71,0-3.24,0.78-4.24,2L28.4,4.74 c3.59,1.22,6.53,3.91,8.17,7.38L28.24,22z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default Icon;
|
||||
1
src/frontend/src/icons/googlemaps/googlemaps.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="23px" height="23px"><path fill="#48b564" d="M35.76,26.36h0.01c0,0-3.77,5.53-6.94,9.64c-2.74,3.55-3.54,6.59-3.77,8.06 C24.97,44.6,24.53,45,24,45s-0.97-0.4-1.06-0.94c-0.23-1.47-1.03-4.51-3.77-8.06c-0.42-0.55-0.85-1.12-1.28-1.7L28.24,22l8.33-9.88 C37.49,14.05,38,16.21,38,18.5C38,21.4,37.17,24.09,35.76,26.36z"/><path fill="#fcc60e" d="M28.24,22L17.89,34.3c-2.82-3.78-5.66-7.94-5.66-7.94h0.01c-0.3-0.48-0.57-0.97-0.8-1.48L19.76,15 c-0.79,0.95-1.26,2.17-1.26,3.5c0,3.04,2.46,5.5,5.5,5.5C25.71,24,27.24,23.22,28.24,22z"/><path fill="#2c85eb" d="M28.4,4.74l-8.57,10.18L13.27,9.2C15.83,6.02,19.69,4,24,4C25.54,4,27.02,4.26,28.4,4.74z"/><path fill="#ed5748" d="M19.83,14.92L19.76,15l-8.32,9.88C10.52,22.95,10,20.79,10,18.5c0-3.54,1.23-6.79,3.27-9.3 L19.83,14.92z"/><path fill="#5695f6" d="M28.24,22c0.79-0.95,1.26-2.17,1.26-3.5c0-3.04-2.46-5.5-5.5-5.5c-1.71,0-3.24,0.78-4.24,2L28.4,4.74 c3.59,1.22,6.53,3.91,8.17,7.38L28.24,22z"/></svg>
|
||||
|
After Width: | Height: | Size: 996 B |
9
src/frontend/src/icons/googlemaps/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import GooglemapsIconSVG from "./googlemaps";
|
||||
|
||||
export const GooglemapsIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <GooglemapsIconSVG ref={ref} {...props} />;
|
||||
});
|
||||
34
src/frontend/src/icons/googlemeet/googlemeet.jsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
const GooglemeetIcon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 48 48"
|
||||
width="23px"
|
||||
height="23px"
|
||||
>
|
||||
<rect
|
||||
width="16"
|
||||
height="16"
|
||||
x="12"
|
||||
y="16"
|
||||
fill="#fff"
|
||||
transform="rotate(-90 20 24)"
|
||||
/>
|
||||
<polygon fill="#1e88e5" points="3,17 3,31 8,32 13,31 13,17 8,16" />
|
||||
<path
|
||||
fill="#4caf50"
|
||||
d="M37,24v14c0,1.657-1.343,3-3,3H13l-1-5l1-5h14v-7l5-1L37,24z"
|
||||
/>
|
||||
<path
|
||||
fill="#fbc02d"
|
||||
d="M37,10v14H27v-7H13l-1-5l1-5h21C35.657,7,37,8.343,37,10z"
|
||||
/>
|
||||
<path fill="#1565c0" d="M13,31v10H6c-1.657,0-3-1.343-3-3v-7H13z" />
|
||||
<polygon fill="#e53935" points="13,7 13,17 3,17" />
|
||||
<polygon fill="#2e7d32" points="38,24 37,32.45 27,24 37,15.55" />
|
||||
<path
|
||||
fill="#4caf50"
|
||||
d="M46,10.11v27.78c0,0.84-0.98,1.31-1.63,0.78L37,32.45v-16.9l7.37-6.22C45.02,8.8,46,9.27,46,10.11z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default GooglemeetIcon;
|
||||
1
src/frontend/src/icons/googlemeet/googlemeet.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="23px" height="23px"><rect width="16" height="16" x="12" y="16" fill="#fff" transform="rotate(-90 20 24)"/><polygon fill="#1e88e5" points="3,17 3,31 8,32 13,31 13,17 8,16"/><path fill="#4caf50" d="M37,24v14c0,1.657-1.343,3-3,3H13l-1-5l1-5h14v-7l5-1L37,24z"/><path fill="#fbc02d" d="M37,10v14H27v-7H13l-1-5l1-5h21C35.657,7,37,8.343,37,10z"/><path fill="#1565c0" d="M13,31v10H6c-1.657,0-3-1.343-3-3v-7H13z"/><polygon fill="#e53935" points="13,7 13,17 3,17"/><polygon fill="#2e7d32" points="38,24 37,32.45 27,24 37,15.55"/><path fill="#4caf50" d="M46,10.11v27.78c0,0.84-0.98,1.31-1.63,0.78L37,32.45v-16.9l7.37-6.22C45.02,8.8,46,9.27,46,10.11z"/></svg>
|
||||
|
After Width: | Height: | Size: 715 B |
9
src/frontend/src/icons/googlemeet/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import GooglemeetIconSVG from "./googlemeet";
|
||||
|
||||
export const GooglemeetIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <GooglemeetIconSVG ref={ref} {...props} />;
|
||||
});
|
||||
20
src/frontend/src/icons/googlesheets/googlesheets.jsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
const Icon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 48 48"
|
||||
width="23px"
|
||||
height="23px"
|
||||
>
|
||||
<path
|
||||
fill="#43a047"
|
||||
d="M37,45H11c-1.657,0-3-1.343-3-3V6c0-1.657,1.343-3,3-3h19l10,10v29C40,43.657,38.657,45,37,45z"
|
||||
/>
|
||||
<path fill="#c8e6c9" d="M40 13L30 13 30 3z" />
|
||||
<path fill="#2e7d32" d="M30 13L40 23 40 13z" />
|
||||
<path
|
||||
fill="#e8f5e9"
|
||||
d="M31,23H17h-2v2v2v2v2v2v2v2h18v-2v-2v-2v-2v-2v-2v-2H31z M17,25h4v2h-4V25z M17,29h4v2h-4V29z M17,33h4v2h-4V33z M31,35h-8v-2h8V35z M31,31h-8v-2h8V31z M31,27h-8v-2h8V27z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default Icon;
|
||||
1
src/frontend/src/icons/googlesheets/googlesheets.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="23px" height="23px"><path fill="#43a047" d="M37,45H11c-1.657,0-3-1.343-3-3V6c0-1.657,1.343-3,3-3h19l10,10v29C40,43.657,38.657,45,37,45z"/><path fill="#c8e6c9" d="M40 13L30 13 30 3z"/><path fill="#2e7d32" d="M30 13L40 23 40 13z"/><path fill="#e8f5e9" d="M31,23H17h-2v2v2v2v2v2v2v2h18v-2v-2v-2v-2v-2v-2v-2H31z M17,25h4v2h-4V25z M17,29h4v2h-4V29z M17,33h4v2h-4V33z M31,35h-8v-2h8V35z M31,31h-8v-2h8V31z M31,27h-8v-2h8V27z"/></svg>
|
||||
|
After Width: | Height: | Size: 495 B |
9
src/frontend/src/icons/googlesheets/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import GooglesheetsIconSVG from "./googlesheets";
|
||||
|
||||
export const GooglesheetsIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <GooglesheetsIconSVG ref={ref} {...props} />;
|
||||
});
|
||||
|
|
@ -106,6 +106,18 @@ export const lazyIconsMapping = {
|
|||
import("@/icons/GoogleDrive").then((mod) => ({
|
||||
default: mod.GoogleDriveIcon,
|
||||
})),
|
||||
Googlemeet: () =>
|
||||
import("@/icons/googlemeet").then((mod) => ({
|
||||
default: mod.GooglemeetIcon,
|
||||
})),
|
||||
GoogleTasks: () =>
|
||||
import("@/icons/GoogleTasks").then((mod) => ({
|
||||
default: mod.GoogleTasksIcon,
|
||||
})),
|
||||
Googlesheets: () =>
|
||||
import("@/icons/googlesheets").then((mod) => ({
|
||||
default: mod.GooglesheetsIcon,
|
||||
})),
|
||||
GoogleGenerativeAI: () =>
|
||||
import("@/icons/GoogleGenerativeAI").then((mod) => ({
|
||||
default: mod.GoogleGenerativeAIIcon,
|
||||
|
|
@ -122,6 +134,18 @@ export const lazyIconsMapping = {
|
|||
import("@/icons/GradientSparkles").then((mod) => ({
|
||||
default: mod.GradientInfinity,
|
||||
})),
|
||||
Googlemaps: () =>
|
||||
import("@/icons/googlemaps").then((mod) => ({
|
||||
default: mod.GooglemapsIcon,
|
||||
})),
|
||||
Todoist: () =>
|
||||
import("@/icons/todoist").then((mod) => ({
|
||||
default: mod.TodoistIcon,
|
||||
})),
|
||||
Zoom: () =>
|
||||
import("@/icons/zoom").then((mod) => ({
|
||||
default: mod.ZoomIcon,
|
||||
})),
|
||||
GradientUngroup: () =>
|
||||
import("@/icons/GradientSparkles").then((mod) => ({
|
||||
default: mod.GradientUngroup,
|
||||
|
|
@ -158,6 +182,8 @@ export const lazyIconsMapping = {
|
|||
import("@/icons/JigsawStack").then((mod) => ({
|
||||
default: mod.JigsawStackIcon,
|
||||
})),
|
||||
Linear: () =>
|
||||
import("@/icons/linear").then((mod) => ({ default: mod.LinearIcon })),
|
||||
LangChain: () =>
|
||||
import("@/icons/LangChain").then((mod) => ({ default: mod.LangChainIcon })),
|
||||
Langwatch: () =>
|
||||
|
|
@ -221,6 +247,8 @@ export const lazyIconsMapping = {
|
|||
})),
|
||||
Redis: () =>
|
||||
import("@/icons/Redis").then((mod) => ({ default: mod.RedisIcon })),
|
||||
Reddit: () =>
|
||||
import("@/icons/reddit").then((mod) => ({ default: mod.RedditIcon })),
|
||||
SambaNova: () =>
|
||||
import("@/icons/SambaNova").then((mod) => ({ default: mod.SambaNovaIcon })),
|
||||
ScrapeGraph: () =>
|
||||
|
|
|
|||
9
src/frontend/src/icons/linear/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import LinearIconSVG from "./linear";
|
||||
|
||||
export const LinearIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <LinearIconSVG ref={ref} {...props} />;
|
||||
});
|
||||
20
src/frontend/src/icons/linear/linear.jsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
const Icon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
width="23px"
|
||||
height="23px"
|
||||
viewBox="0 0 23 23"
|
||||
version="1.1"
|
||||
fill="currentColor"
|
||||
{...props}
|
||||
>
|
||||
<g id="surface1">
|
||||
<path d="M 2.910156 12.402344 C 3.105469 14.300781 3.933594 16.144531 5.386719 17.597656 C 6.839844 19.050781 8.683594 19.878906 10.582031 20.078125 Z M 2.910156 12.402344 " />
|
||||
<path d="M 2.875 11.015625 L 11.972656 20.113281 C 12.742188 20.066406 13.511719 19.921875 14.25 19.671875 L 3.3125 8.734375 C 3.066406 9.476562 2.917969 10.242188 2.875 11.015625 Z M 2.875 11.015625 " />
|
||||
<path d="M 3.707031 7.773438 L 15.214844 19.28125 C 15.8125 18.996094 16.382812 18.636719 16.914062 18.203125 L 4.78125 6.074219 C 4.351562 6.605469 3.992188 7.175781 3.707031 7.773438 Z M 3.707031 7.773438 " />
|
||||
<path d="M 5.425781 5.363281 C 8.796875 2.03125 14.230469 2.046875 17.585938 5.402344 C 20.941406 8.757812 20.953125 14.1875 17.625 17.558594 Z M 5.425781 5.363281 " />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
export default Icon;
|
||||
8
src/frontend/src/icons/linear/linear.svg
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="23px" height="23px" viewBox="0 0 23 23" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 2.910156 12.402344 C 3.105469 14.300781 3.933594 16.144531 5.386719 17.597656 C 6.839844 19.050781 8.683594 19.878906 10.582031 20.078125 Z M 2.910156 12.402344 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 2.875 11.015625 L 11.972656 20.113281 C 12.742188 20.066406 13.511719 19.921875 14.25 19.671875 L 3.3125 8.734375 C 3.066406 9.476562 2.917969 10.242188 2.875 11.015625 Z M 2.875 11.015625 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 3.707031 7.773438 L 15.214844 19.28125 C 15.8125 18.996094 16.382812 18.636719 16.914062 18.203125 L 4.78125 6.074219 C 4.351562 6.605469 3.992188 7.175781 3.707031 7.773438 Z M 3.707031 7.773438 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 5.425781 5.363281 C 8.796875 2.03125 14.230469 2.046875 17.585938 5.402344 C 20.941406 8.757812 20.953125 14.1875 17.625 17.558594 Z M 5.425781 5.363281 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
9
src/frontend/src/icons/reddit/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import RedditIconSVG from "./reddit";
|
||||
|
||||
export const RedditIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <RedditIconSVG ref={ref} {...props} />;
|
||||
});
|
||||
13
src/frontend/src/icons/reddit/reddit.jsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
const RedditIconSVG = (props) => (
|
||||
<svg viewBox="0 0 16 16" width="23px" height="23px" {...props}>
|
||||
<path
|
||||
fill="#fe3155"
|
||||
d="M8,0c4.418,0,8,3.582,8,8s-3.582,8-8,8s-8-3.582-8-8S3.582,0,8,0z"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M12.124,6.875c-0.285,0.009-0.558,0.127-0.766,0.324c-0.911-0.62-1.98-0.96-3.08-0.984l0.52-2.496 l1.711,0.36c0.047,0.44,0.442,0.758,0.882,0.711c0.438-0.049,0.756-0.442,0.709-0.882c-0.047-0.44-0.442-0.758-0.88-0.709 c-0.253,0.025-0.48,0.173-0.605,0.391l-1.96-0.391C8.522,3.167,8.387,3.251,8.358,3.387c0,0.002,0,0.002,0,0.004L7.767,6.167 c-1.113,0.016-2.198,0.36-3.12,0.984c-0.469-0.442-1.209-0.42-1.651,0.051c-0.442,0.471-0.42,1.209,0.053,1.651 c0.091,0.085,0.196,0.16,0.313,0.211c-0.007,0.118-0.007,0.235,0,0.353c0,1.791,2.089,3.249,4.664,3.249 c2.576,0,4.664-1.456,4.664-3.249c0.007-0.118,0.007-0.234,0-0.353c0.402-0.2,0.655-0.615,0.645-1.064 C13.311,7.356,12.771,6.853,12.124,6.875z M5.335,8.8c0-0.442,0.358-0.8,0.802-0.8c0.442,0,0.8,0.358,0.8,0.8s-0.358,0.8-0.8,0.8 S5.335,9.242,5.335,8.8z M9.971,11C9.616,11.328,8.988,11.616,8,11.616S6.384,11.328,6.029,11c-0.073-0.067-0.062-0.227,0.031-0.304 c0.078-0.065,0.193-0.065,0.275,0c0.48,0.353,1.07,0.48,1.665,0.48s1.185-0.127,1.665-0.48c0.082-0.065,0.196-0.065,0.275,0 C10.033,10.773,10.044,10.933,9.971,11z M9.842,9.6C9.4,9.6,9.04,9.242,9.04,8.8C9.042,8.358,9.4,8,9.842,8 c0.442,0,0.8,0.358,0.8,0.8S10.284,9.6,9.842,9.6z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default RedditIconSVG;
|
||||
1
src/frontend/src/icons/reddit/reddit.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px" baseProfile="basic"><path fill="#fe3155" d="M8,0c4.418,0,8,3.582,8,8s-3.582,8-8,8s-8-3.582-8-8S3.582,0,8,0z"/><path fill="#fff" d="M12.124,6.875c-0.285,0.009-0.558,0.127-0.766,0.324c-0.911-0.62-1.98-0.96-3.08-0.984l0.52-2.496 l1.711,0.36c0.047,0.44,0.442,0.758,0.882,0.711c0.438-0.049,0.756-0.442,0.709-0.882c-0.047-0.44-0.442-0.758-0.88-0.709 c-0.253,0.025-0.48,0.173-0.605,0.391l-1.96-0.391C8.522,3.167,8.387,3.251,8.358,3.387c0,0.002,0,0.002,0,0.004L7.767,6.167 c-1.113,0.016-2.198,0.36-3.12,0.984c-0.469-0.442-1.209-0.42-1.651,0.051c-0.442,0.471-0.42,1.209,0.053,1.651 c0.091,0.085,0.196,0.16,0.313,0.211c-0.007,0.118-0.007,0.235,0,0.353c0,1.791,2.089,3.249,4.664,3.249 c2.576,0,4.664-1.456,4.664-3.249c0.007-0.118,0.007-0.234,0-0.353c0.402-0.2,0.655-0.615,0.645-1.064 C13.311,7.356,12.771,6.853,12.124,6.875z M5.335,8.8c0-0.442,0.358-0.8,0.802-0.8c0.442,0,0.8,0.358,0.8,0.8s-0.358,0.8-0.8,0.8 S5.335,9.242,5.335,8.8z M9.971,11C9.616,11.328,8.988,11.616,8,11.616S6.384,11.328,6.029,11c-0.073-0.067-0.062-0.227,0.031-0.304 c0.078-0.065,0.193-0.065,0.275,0c0.48,0.353,1.07,0.48,1.665,0.48s1.185-0.127,1.665-0.48c0.082-0.065,0.196-0.065,0.275,0 C10.033,10.773,10.044,10.933,9.971,11z M9.842,9.6C9.4,9.6,9.04,9.242,9.04,8.8C9.042,8.358,9.4,8,9.842,8 c0.442,0,0.8,0.358,0.8,0.8S10.284,9.6,9.842,9.6z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
9
src/frontend/src/icons/todoist/index.tsx
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import TodoistIconSVG from "./todoist";
|
||||
|
||||
export const TodoistIcon = forwardRef<
|
||||
SVGSVGElement,
|
||||
React.PropsWithChildren<{}>
|
||||
>((props, ref) => {
|
||||
return <TodoistIconSVG ref={ref} {...props} />;
|
||||
});
|
||||
15
src/frontend/src/icons/todoist/todoist.jsx
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
const Icon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 48 48"
|
||||
width="23px"
|
||||
height="23px"
|
||||
baseProfile="basic"
|
||||
>
|
||||
<path
|
||||
fill="#f44336"
|
||||
d="M37,6H11c-2.761,0-5,2.239-5,5v5.29l6.406,3.7c0.309,0.178,0.689,0.179,0.999,0.001l12.747-7.329 c0.152-0.087,0.338-0.089,0.492-0.004l1.666,0.923c0.34,0.189,0.344,0.677,0.007,0.871L13.4,23.024 c-0.309,0.178-0.689,0.177-0.998-0.001L6,19.329v3.045l6.406,3.7c0.309,0.178,0.689,0.179,0.999,0.001l12.747-7.329 c0.152-0.087,0.338-0.089,0.492-0.004l1.666,0.923c0.34,0.189,0.344,0.677,0.007,0.871L13.4,29.108 c-0.309,0.178-0.689,0.177-0.998-0.001L6,25.413v3.045l6.406,3.7c0.309,0.178,0.689,0.179,0.999,0.001l12.747-7.329 c0.152-0.087,0.338-0.089,0.492-0.004l1.666,0.923c0.34,0.189,0.344,0.677,0.007,0.871L13.4,35.192 c-0.309,0.178-0.689,0.177-0.998-0.001L6,31.497V37c0,2.761,2.239,5,5,5h26c2.761,0,5-2.239,5-5V11C42,8.239,39.761,6,37,6z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
export default Icon;
|
||||
1
src/frontend/src/icons/todoist/todoist.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="23px" height="23px" baseProfile="basic"><path fill="#f44336" d="M37,6H11c-2.761,0-5,2.239-5,5v5.29l6.406,3.7c0.309,0.178,0.689,0.179,0.999,0.001l12.747-7.329 c0.152-0.087,0.338-0.089,0.492-0.004l1.666,0.923c0.34,0.189,0.344,0.677,0.007,0.871L13.4,23.024 c-0.309,0.178-0.689,0.177-0.998-0.001L6,19.329v3.045l6.406,3.7c0.309,0.178,0.689,0.179,0.999,0.001l12.747-7.329 c0.152-0.087,0.338-0.089,0.492-0.004l1.666,0.923c0.34,0.189,0.344,0.677,0.007,0.871L13.4,29.108 c-0.309,0.178-0.689,0.177-0.998-0.001L6,25.413v3.045l6.406,3.7c0.309,0.178,0.689,0.179,0.999,0.001l12.747-7.329 c0.152-0.087,0.338-0.089,0.492-0.004l1.666,0.923c0.34,0.189,0.344,0.677,0.007,0.871L13.4,35.192 c-0.309,0.178-0.689,0.177-0.998-0.001L6,31.497V37c0,2.761,2.239,5,5,5h26c2.761,0,5-2.239,5-5V11C42,8.239,39.761,6,37,6z"/></svg>
|
||||
|
After Width: | Height: | Size: 866 B |
8
src/frontend/src/icons/zoom/index.tsx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import React, { forwardRef } from "react";
|
||||
import ZoomIconSVG from "./zoom";
|
||||
|
||||
export const ZoomIcon = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
|
||||
(props, ref) => {
|
||||
return <ZoomIconSVG ref={ref} {...props} />;
|
||||
},
|
||||
);
|
||||
16
src/frontend/src/icons/zoom/zoom.jsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const Icon = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 48 48"
|
||||
width="23px"
|
||||
height="23px"
|
||||
>
|
||||
<circle cx="24" cy="24" r="20" fill="#2196f3" />
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M29,31H14c-1.657,0-3-1.343-3-3V17h15c1.657,0,3,1.343,3,3V31z"
|
||||
/>
|
||||
<polygon fill="#fff" points="37,31 31,27 31,21 37,17" />
|
||||
</svg>
|
||||
);
|
||||
export default Icon;
|
||||
1
src/frontend/src/icons/zoom/zoom.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="23px" height="23px"><circle cx="24" cy="24" r="20" fill="#2196f3"/><path fill="#fff" d="M29,31H14c-1.657,0-3-1.343-3-3V17h15c1.657,0,3,1.343,3,3V31z"/><polygon fill="#fff" points="37,31 31,27 31,21 37,17"/></svg>
|
||||
|
After Width: | Height: | Size: 280 B |
|
|
@ -232,6 +232,13 @@ export const SIDEBAR_CATEGORIES = [
|
|||
export const SIDEBAR_BUNDLES = [
|
||||
{ display_name: "AI/ML API", name: "aiml", icon: "AIML" },
|
||||
{ display_name: "AgentQL", name: "agentql", icon: "AgentQL" },
|
||||
{
|
||||
display_name: "Language Models",
|
||||
name: "languagemodels",
|
||||
icon: "BrainCircuit",
|
||||
},
|
||||
{ display_name: "Embeddings", name: "embeddings", icon: "Binary" },
|
||||
{ display_name: "Memories", name: "memories", icon: "Cpu" },
|
||||
{ display_name: "Amazon", name: "amazon", icon: "Amazon" },
|
||||
{ display_name: "Anthropic", name: "anthropic", icon: "Anthropic" },
|
||||
{ display_name: "Apify", name: "apify", icon: "Apify" },
|
||||
|
|
@ -255,15 +262,9 @@ export const SIDEBAR_BUNDLES = [
|
|||
{ display_name: "Exa", name: "exa", icon: "Exa" },
|
||||
{ display_name: "Firecrawl", name: "firecrawl", icon: "FirecrawlCrawlApi" },
|
||||
{ display_name: "Git", name: "git", icon: "GitLoader" },
|
||||
{ display_name: "GitHub", name: "github", icon: "Github" },
|
||||
{ display_name: "Glean", name: "glean", icon: "Glean" },
|
||||
{ display_name: "Gmail", name: "gmail", icon: "Gmail" },
|
||||
{ display_name: "Google", name: "google", icon: "Google" },
|
||||
{
|
||||
display_name: "Googlecalendar",
|
||||
name: "googlecalendar",
|
||||
icon: "Googlecalendar",
|
||||
},
|
||||
{ display_name: "Groq", name: "groq", icon: "Groq" },
|
||||
{
|
||||
display_name: "Home Assistant",
|
||||
|
|
@ -290,7 +291,6 @@ export const SIDEBAR_BUNDLES = [
|
|||
{ display_name: "Ollama", name: "ollama", icon: "Ollama" },
|
||||
{ display_name: "OpenAI", name: "openai", icon: "OpenAI" },
|
||||
{ display_name: "OpenRouter", name: "openrouter", icon: "OpenRouter" },
|
||||
{ display_name: "Outlook", name: "outlook", icon: "Outlook" },
|
||||
{ display_name: "Perplexity", name: "perplexity", icon: "Perplexity" },
|
||||
{ display_name: "Redis", name: "redis", icon: "Redis" },
|
||||
{ display_name: "SambaNova", name: "sambanova", icon: "SambaNova" },
|
||||
|
|
@ -372,7 +372,12 @@ export const nodeIconToDisplayIconMap: Record<string, string> = {
|
|||
ChatOutput: "MessagesSquare",
|
||||
//Integration Icons
|
||||
Outlook: "Outlook",
|
||||
AIML: "AIML",
|
||||
Linear: "Linear",
|
||||
Reddit: "Reddit",
|
||||
Googlemaps: "Googlemaps",
|
||||
Todoist: "Todoist",
|
||||
Zoom: "Zoom",
|
||||
AIML: "AI/ML",
|
||||
AgentQL: "AgentQL",
|
||||
LanguageModels: "BrainCircuit",
|
||||
EmbeddingModels: "Binary",
|
||||
|
|
@ -409,8 +414,11 @@ export const nodeIconToDisplayIconMap: Record<string, string> = {
|
|||
FirecrawlScrapeApi: "Firecrawl",
|
||||
GitbookLoader: "GitBook",
|
||||
GoogleGenerativeAI: "GoogleGenerativeAI",
|
||||
Googlesheets: "Googlesheets",
|
||||
GoogleSearchAPI: "Google",
|
||||
GoogleSearchAPIWrapper: "Google",
|
||||
Googlemeet: "Googlemeet",
|
||||
GoogleTasks: "GoogleTasks",
|
||||
GoogleSearchResults: "Google",
|
||||
GoogleSearchRun: "Google",
|
||||
GoogleSerperAPI: "Google",
|
||||
|
|
|
|||
103
uv.lock
generated
|
|
@ -1316,47 +1316,49 @@ wheels = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "composio-core"
|
||||
version = "0.7.15"
|
||||
name = "composio"
|
||||
version = "0.8.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
{ name = "click" },
|
||||
{ name = "fastapi" },
|
||||
{ name = "importlib-metadata" },
|
||||
{ name = "inflection" },
|
||||
{ name = "jsonref" },
|
||||
{ name = "jsonschema" },
|
||||
{ name = "paramiko" },
|
||||
{ name = "composio-client" },
|
||||
{ name = "openai" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "pyperclip" },
|
||||
{ name = "pysher" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "requests" },
|
||||
{ name = "rich" },
|
||||
{ name = "semver" },
|
||||
{ name = "sentry-sdk" },
|
||||
{ name = "uvicorn" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3c/9d/3b3b8ca7269da92e602601afc2b32656f6ac94d42754292c5f9f669a575a/composio_core-0.7.15.tar.gz", hash = "sha256:6797c46f404b9c265cd05c07bd28b4104f425024ef683ac24092087e9590033d", size = 330325, upload-time = "2025-04-07T21:52:51.921Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/46/31/c9486e483832199f957c6728b2479ffd8946af1b7f69297aca5c0c8b1b1a/composio-0.8.5.tar.gz", hash = "sha256:7e5a5c2cfc3d13083c35e0279cd28e9df30acaae21632cb0eb7f948e6500a2a6", size = 38268, upload-time = "2025-07-24T11:44:35.777Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/b4/928bd4cd6d5fa6f8c4d651cf11cfb99a257ad3c4988e084bc99a794520c6/composio_core-0.7.15-py3-none-any.whl", hash = "sha256:3b4c19fcf8c084ed8b873b8e24ea55084495a046eb971af994e6aae6e1bfcea7", size = 493768, upload-time = "2025-04-07T21:52:50.085Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/7f/b5bc19d2246e3171227cfe6f33a63f5e3670d09ff535d7a9c7a94658c1fb/composio-0.8.5-py3-none-any.whl", hash = "sha256:b673554a0276c5394d639382edb7300a8c0506a1452316535eb7cc17daff0528", size = 47275, upload-time = "2025-07-24T11:44:22.666Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "composio-client"
|
||||
version = "1.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "distro" },
|
||||
{ name = "httpx" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "sniffio" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/21/a2/71e97c8f9c0c4fb0a1734417f629c83717c5f973b0cd8ccb95b803dd9224/composio_client-1.6.0.tar.gz", hash = "sha256:2adaf9bdaf449384d1d32f9361af20009c0675c68381cd9e9a4234f08969ab23", size = 169235, upload-time = "2025-07-21T06:07:18.72Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/2c/19f4ff86f0b3c5fb442e70185ead4feae6237abecfb35f109c9ccc2a6d5e/composio_client-1.6.0-py3-none-any.whl", hash = "sha256:e32055eeae8b49e7b94329808604f849e74bc557a453d3e37b37fe5cb627930b", size = 199273, upload-time = "2025-07-21T06:07:17.316Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "composio-langchain"
|
||||
version = "0.7.15"
|
||||
version = "0.8.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "composio-core" },
|
||||
{ name = "composio" },
|
||||
{ name = "langchain" },
|
||||
{ name = "langchain-openai" },
|
||||
{ name = "langchainhub" },
|
||||
{ name = "pydantic" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a3/7a/e127ea858ceda35341ee399480d2d5705bd4a2dbbabadf8f0bc35d05c48d/composio_langchain-0.7.15.tar.gz", hash = "sha256:cb75c460289ecdf9590caf7ddc0d7888b0a6622ca4f800c9358abe90c25d055e", size = 4477, upload-time = "2025-04-07T21:50:19.224Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/48/fe/43cac59464b70794ab56d9e79d2a6325e067208beedf8f647ef100af4a71/composio_langchain-0.8.5.tar.gz", hash = "sha256:2ed844fd9cec9f2fce1e7f4a7a8e3cde9b1921277a6835d21ab6e0d05e4aa847", size = 3072, upload-time = "2025-07-24T11:44:41.2Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/40/67/324cb0d8ce29c6689d41751b8e29c2600ac89028fd2e69440526929cb422/composio_langchain-0.7.15-py3-none-any.whl", hash = "sha256:a71b5371ad6c3ee4d4289c7a994fad1424e24c29a38e820b6b2ed259056abb65", size = 4897, upload-time = "2025-04-07T21:50:01.291Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/fe/9463c00a48a740e13af0ac82af6091a6ab536d19d0bae5f00c1a6154bc47/composio_langchain-0.8.5-py3-none-any.whl", hash = "sha256:b7e983944aa352b0b763086ae57ac93ce3a7c9d845295c60378e3f196c13cf4e", size = 3351, upload-time = "2025-07-24T11:44:31.291Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3878,15 +3880,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inflection"
|
||||
version = "0.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e1/7e/691d061b7329bc8d54edbf0ec22fbfb2afe61facb681f9aaa9bff7a27d04/inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417", size = 15091, upload-time = "2020-08-22T08:16:29.139Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/59/91/aa6bde563e0085a02a435aa99b49ef75b0a4b062635e606dab23ce18d720/inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2", size = 9454, upload-time = "2020-08-22T08:16:27.816Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.1.0"
|
||||
|
|
@ -4841,7 +4834,7 @@ dependencies = [
|
|||
{ name = "certifi" },
|
||||
{ name = "chromadb" },
|
||||
{ name = "cleanlab-tlm" },
|
||||
{ name = "composio-core" },
|
||||
{ name = "composio" },
|
||||
{ name = "composio-langchain" },
|
||||
{ name = "datasets" },
|
||||
{ name = "docling-core" },
|
||||
|
|
@ -5037,8 +5030,8 @@ requires-dist = [
|
|||
{ name = "chromadb", specifier = "==0.5.23" },
|
||||
{ name = "cleanlab-tlm", specifier = ">=1.1.2" },
|
||||
{ name = "clickhouse-connect", marker = "extra == 'clickhouse-connect'", specifier = "==0.7.19" },
|
||||
{ name = "composio-core", specifier = "==0.7.15" },
|
||||
{ name = "composio-langchain", specifier = "==0.7.15" },
|
||||
{ name = "composio", specifier = "==0.8.5" },
|
||||
{ name = "composio-langchain", specifier = "==0.8.5" },
|
||||
{ name = "couchbase", marker = "extra == 'couchbase'", specifier = ">=4.2.1" },
|
||||
{ name = "ctransformers", marker = "extra == 'local'", specifier = ">=0.2.10" },
|
||||
{ name = "datasets", specifier = ">2.14.7" },
|
||||
|
|
@ -7545,20 +7538,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/00/2f/804f58f0b856ab3bf21617cccf5b39206e6c4c94c2cd227bde125ea6105f/parameterized-0.9.0-py2.py3-none-any.whl", hash = "sha256:4e0758e3d41bea3bbd05ec14fc2c24736723f243b28d702081aef438c9372b1b", size = 20475, upload-time = "2023-03-27T02:01:09.31Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paramiko"
|
||||
version = "3.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "bcrypt" },
|
||||
{ name = "cryptography" },
|
||||
{ name = "pynacl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/15/ad6ce226e8138315f2451c2aeea985bf35ee910afb477bae7477dc3a8f3b/paramiko-3.5.1.tar.gz", hash = "sha256:b2c665bc45b2b215bd7d7f039901b14b067da00f3a11e6640995fd58f2664822", size = 1566110, upload-time = "2025-02-04T02:37:59.783Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/15/f8/c7bd0ef12954a81a1d3cea60a13946bd9a49a0036a5927770c461eade7ae/paramiko-3.5.1-py3-none-any.whl", hash = "sha256:43b9a0501fc2b5e70680388d9346cf252cfb7d00b0667c39e80eb43a408b8f61", size = 227298, upload-time = "2025-02-04T02:37:57.672Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parso"
|
||||
version = "0.8.4"
|
||||
|
|
@ -8572,26 +8551,6 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/0d/2a/7c24a6144eaa06d18ed52822ea2b0f119fd9267cd1abbb75dae4d89a3803/pymongo-4.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:45ee87a4e12337353242bc758accc7fb47a2f2d9ecc0382a61e64c8f01e86708", size = 976873, upload-time = "2024-10-01T23:07:19.721Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pynacl"
|
||||
version = "1.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a7/22/27582568be639dfe22ddb3902225f91f2f17ceff88ce80e4db396c8986da/PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba", size = 3392854, upload-time = "2022-01-07T22:05:41.134Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/75/0b8ede18506041c0bf23ac4d8e2971b4161cd6ce630b177d0a08eb0d8857/PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1", size = 349920, upload-time = "2022-01-07T22:05:49.156Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/bb/fddf10acd09637327a97ef89d2a9d621328850a72f1fdc8c08bdf72e385f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92", size = 601722, upload-time = "2022-01-07T22:05:50.989Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/70/87a065c37cca41a75f2ce113a5a2c2aa7533be648b184ade58971b5f7ccc/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394", size = 680087, upload-time = "2022-01-07T22:05:52.539Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/87/f1bb6a595f14a327e8285b9eb54d41fef76c585a0edef0a45f6fc95de125/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d", size = 856678, upload-time = "2022-01-07T22:05:54.251Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/28/ca86676b69bf9f90e710571b67450508484388bfce09acf8a46f0b8c785f/PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858", size = 1133660, upload-time = "2022-01-07T22:05:56.056Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/85/c262db650e86812585e2bc59e497a8f59948a005325a11bbbc9ecd3fe26b/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b", size = 663824, upload-time = "2022-01-07T22:05:57.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/1a/cc308a884bd299b651f1633acb978e8596c71c33ca85e9dc9fa33a5399b9/PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff", size = 1117912, upload-time = "2022-01-07T22:05:58.665Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/2d/b7df6ddb0c2a33afdb358f8af6ea3b8c4d1196ca45497dd37a56f0c122be/PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543", size = 204624, upload-time = "2022-01-07T22:06:00.085Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/22/d3db169895faaf3e2eda892f005f433a62db2decbcfbc2f61e6517adfa87/PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93", size = 212141, upload-time = "2022-01-07T22:06:01.861Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.2.3"
|
||||
|
|
|
|||