feat: Refactor langflow Components - Consolidate Imports, Remove Unused Components, and Update Astra Assistant Icons (#4460)
* fix lint * fix lint * [autofix.ci] apply automated fixes * 📝 (message_to_data.py): refactor convert_message_to_data method to simplify code and improve readability 🔧 (conftest.py): update imports to organize them alphabetically for better code maintenance 🔧 (test_starter_projects.py): organize imports alphabetically for better code maintenance 🔧 (test_astra_component.py): organize imports alphabetically for better code maintenance 🔧 (test_parse_json_data.py): remove unnecessary whitespace for cleaner code 🔧 (test_chat_input.py): remove unnecessary whitespace for cleaner code 🔧 (test_text_input.py): remove unnecessary whitespace for cleaner code 🔧 (test_output_parser.py): remove unnecessary whitespace for cleaner code 🔧 (test_chat_output.py): remove unnecessary whitespace for cleaner code 🔧 (test_text_output.py): remove unnecessary whitespace for cleaner code 🔧 (test_prompt.py): remove unnecessary whitespace for cleaner code 🔧 (test_basic_prompting.py): remove unnecessary whitespace for cleaner code 🔧 (test_misc.py): remove unnecessary whitespace for cleaner code 🔧 (utils.py): remove unnecessary whitespace for cleaner code 🔧 (test_server_init.py): remove unnecessary whitespace for cleaner code 🔧 (test_endpoints.py): remove unnecessary whitespace for cleaner code 🔧 (test_component_toolkit.py): remove unnecessary whitespace for cleaner code 🔧 (test_agent_component.py): remove unnecessary whitespace for cleaner code 🔧 (test_agent_events.py): remove unnecessary whitespace for cleaner code 🔧 (test_tool_calling_agent.py): remove unnecessary whitespace for cleaner code 🔧 (test_structured_output_component.py): remove unnecessary whitespace for cleaner code 🔧 (test_chatollama_component.py): remove unnecessary whitespace for cleaner code 📝 (test_huggingface.py): Remove unnecessary import statement 📝 (test_create_data_component.py): Remove unnecessary import statement 📝 (test_update_data_component.py): Remove unnecessary import statement 📝 (test_componet_set_functionality.py): Remove unnecessary import statement 📝 (test_component.py): Remove unnecessary import statement 📝 (test_event_manager.py): Remove unnecessary import statement 📝 (test_edge_base.py): Remove unnecessary import statement 📝 (test_state_model.py): Remove unnecessary import statement 📝 (test_base.py): Remove unnecessary import statement 📝 (test_callback_graph.py): Remove unnecessary import statement 📝 (test_cycles.py): Remove unnecessary import statement 📝 (test_graph_state_model.py): Remove unnecessary import statement 📝 (test_runnable_vertices_manager.py): Remove unnecessary import statement 📝 (test_utils.py): Remove unnecessary import statement 📝 (test_graph.py): Remove unnecessary import statement 📝 (test_base_model_from_schema.py): Reorder import statements 📝 (test_memory_chatbot.py): Remove unnecessary import statement 📝 (test_vector_store_rag.py): Remove unnecessary import statement 📝 (test_inputs.py): Reorder import statements 📝 (test_io_schema.py): Remove unnecessary import statement 📝 (test_table_schema.py): Remove unnecessary import statement 📝 (test_schema_message.py): Remove unnecessary import statement 📝 (test_service.py): Reorder import statements 📝 (test_api_key.py): Remove unnecessary import statement 📝 (test_chat_endpoint.py): Remove unnecessary import statement 📝 (test_cli.py): Remove unnecessary import statement 📝 (test_custom_component.py): Add missing import statement in test_custom_component.py 📝 (test_custom_component_with_client.py): Add missing import statement in test_custom_component_with_client.py 📝 (test_data_class.py): Add missing import statement in test_data_class.py 📝 (test_data_components.py): Add missing import statement in test_data_components.py 📝 (test_database.py): Add missing import statement in test_database.py 📝 (test_endpoints.py): Add missing import statement in test_endpoints.py 📝 (test_files.py): Add missing import statement in test_files.py 📝 (test_frontend_nodes.py): Add missing import statement in test_frontend_nodes.py 📝 (test_initial_setup.py): Add missing import statement in test_initial_setup.py 📝 (test_kubernetes_secrets.py): Add missing import statement in test_kubernetes_secrets.py 📝 (test_logger.py): Add missing import statement in test_logger.py 📝 (test_login.py): Add missing import statement in test_login.py 📝 (test_messages.py): Add missing import statement in test_messages.py 📝 (test_messages_endpoints.py): Add missing import statement in test_messages_endpoints.py 📝 (test_schema.py): Add missing import statement in test_schema.py 📝 (test_telemetry.py): Add missing import statement in test_telemetry.py 📝 (test_template.py): Add missing import statement in test_template.py 📝 (test_user.py): Add missing import statement in test_user.py 📝 (test_validate_code.py): Add missing import statement in test_validate_code.py 📝 (test_connection_string_parser.py): Add missing import statement in test_connection_string_parser.py 📝 (test_format_directory_path.py): Add missing import statement in test_format_directory_path.py 📝 (test_rewrite_file_path.py): Add missing import statement in test_rewrite_file_path.py 📝 (test_truncate_long_strings.py): Add missing import statement in test_truncate_long_strings.py 📝 (test_truncate_long_strings_on_objects.py): Add missing import statement in test_truncate_long_strings_on_objects.py * [autofix.ci] apply automated fixes * formatting * Update import paths for MemoryComponent and HuggingFaceEndpointsComponent * fix tests * ✨ (decisionFlow.spec.ts): update test selectors to match changes in the application's UI for better test accuracy and reliability * [autofix.ci] apply automated fixes * ♻️ (test_output_parser.py): refactor import statements to improve code readability and maintainability * fix tests * ✅ (Simple Agent.spec.ts): enable test for "Simple Agent" by removing the skip flag to ensure it runs properly * [autofix.ci] apply automated fixes * Remove optional type from 'num_news' field in YahooFinanceSchema * Refactor JSON formatting for improved readability and maintainability across starter projects. * adding icon to flows * [autofix.ci] apply automated fixes * ✅ (similarity.spec.ts): update test selectors for filtering and connection nodes to match changes in the frontend implementation * [autofix.ci] apply automated fixes * removing init uncessary * ⬆️ (pyproject.toml): upgrade crewai dependency to version 0.76.9 to include the latest features and bug fixes * ✅ (Simple Agent.spec.ts): update test assertion to check if the count of "print(" is greater than or equal to 1 for better test coverage. * [autofix.ci] apply automated fixes * proposed changes * [autofix.ci] apply automated fixes * add zep icon * [autofix.ci] apply automated fixes * 🔧 (model.py): remove unused import and method 'cast_vector_store' from LCVectorStoreComponent 🔧 (openai_tools.py): remove beta flag from OpenAIToolsAgentComponent 🔧 (tool_calling.py): remove beta flag from ToolCallingAgentComponent 🔧 (Vector Store RAG.json): remove 'cast_vector_store' method from starter projects 🔧 (styleUtils.ts): add missing icon 'WolframAlphaAPI' to nodeIconsLucide in styleUtils module * add langwatch and mem0 icon * change mem0 name to mem0ai * ✨ (filterSidebar.spec.ts): update test to use first() method to select the first element with the specified test ID ♻️ (filterSidebar.spec.ts): remove unnecessary code related to checking the checkbox state in the test 🔧 (generalBugs-shard-2.spec.ts): update test to select the correct element with the test ID "dataWebhook" instead of "dataWebhook Input" * ♻️ (test_vector_store_rag.py): remove redundant code setting "vector_store" value in both ingestion_graph and rag_graph functions to improve code readability and maintainability * [autofix.ci] apply automated fixes * updating json test * [autofix.ci] apply automated fixes * ⬆️ (pyproject.toml): downgrade crewai dependency version from 0.76.9 to 0.74.2 to resolve compatibility issues * 🐛 (generalBugs-shard-5.spec.ts): fix typo in test case for component name 🐛 (generalBugs-shard-9.spec.ts): update endX calculation to move 600 pixels to the right instead of 300 🐛 (generalBugs-shard-9.spec.ts): fix typo in test case for component name and update test steps to click "fit_view" button twice --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
This commit is contained in:
parent
6b3bf7b523
commit
29e484465c
182 changed files with 7279 additions and 516 deletions
|
|
@ -1,6 +1,6 @@
|
|||
from abc import abstractmethod
|
||||
from functools import wraps
|
||||
from typing import TYPE_CHECKING, cast
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
|
@ -64,11 +64,6 @@ class LCVectorStoreComponent(Component):
|
|||
name="search_results",
|
||||
method="search_documents",
|
||||
),
|
||||
Output(
|
||||
display_name="Vector Store",
|
||||
name="vector_store",
|
||||
method="cast_vector_store",
|
||||
),
|
||||
]
|
||||
|
||||
def _validate_outputs(self) -> None:
|
||||
|
|
@ -120,9 +115,6 @@ class LCVectorStoreComponent(Component):
|
|||
self.status = data
|
||||
return data
|
||||
|
||||
def cast_vector_store(self) -> VectorStore:
|
||||
return cast(VectorStore, self.build_vector_store())
|
||||
|
||||
def build_base_retriever(self) -> Retriever: # type: ignore[type-var]
|
||||
"""Builds the BaseRetriever object."""
|
||||
if self._cached_vector_store is not None:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
from .add_content_to_page import AddContentToPage
|
||||
from .create_page import NotionPageCreator
|
||||
from .list_database_properties import NotionDatabaseProperties
|
||||
from .list_pages import NotionListPages
|
||||
from .list_users import NotionUserList
|
||||
from .page_content_viewer import NotionPageContent
|
||||
from .search import NotionSearch
|
||||
from .update_page_property import NotionPageUpdate
|
||||
|
||||
__all__ = [
|
||||
"AddContentToPage",
|
||||
"NotionPageCreator",
|
||||
"NotionDatabaseProperties",
|
||||
"NotionListPages",
|
||||
"NotionUserList",
|
||||
"NotionPageContent",
|
||||
"NotionSearch",
|
||||
"NotionPageUpdate",
|
||||
]
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
from . import (
|
||||
agents,
|
||||
chains,
|
||||
custom_component,
|
||||
documentloaders,
|
||||
embeddings,
|
||||
helpers,
|
||||
inputs,
|
||||
link_extractors,
|
||||
memories,
|
||||
models,
|
||||
outputs,
|
||||
prompts,
|
||||
prototypes,
|
||||
retrievers,
|
||||
textsplitters,
|
||||
toolkits,
|
||||
tools,
|
||||
vectorstores,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"agents",
|
||||
"chains",
|
||||
"documentloaders",
|
||||
"embeddings",
|
||||
"helpers",
|
||||
"inputs",
|
||||
"link_extractors",
|
||||
"custom_component",
|
||||
"memories",
|
||||
"models",
|
||||
"output_parsers",
|
||||
"outputs",
|
||||
"prompts",
|
||||
"prototypes",
|
||||
"retrievers",
|
||||
"textsplitters",
|
||||
"toolkits",
|
||||
"tools",
|
||||
"vectorstores",
|
||||
]
|
||||
|
|
@ -1,29 +1,3 @@
|
|||
from .crewai import CrewAIAgentComponent
|
||||
from .csv import CSVAgentComponent
|
||||
from .hierarchical_crew import HierarchicalCrewComponent
|
||||
from .json import JsonAgentComponent
|
||||
from .openai_tools import OpenAIToolsAgentComponent
|
||||
from .openapi import OpenAPIAgentComponent
|
||||
from .sequential_crew import SequentialCrewComponent
|
||||
from .sequential_task import SequentialTaskAgentComponent
|
||||
from .sql import SQLAgentComponent
|
||||
from .tool_calling import ToolCallingAgentComponent
|
||||
from .vector_store import VectorStoreAgentComponent
|
||||
from .vector_store_router import VectorStoreRouterAgentComponent
|
||||
from .xml import XMLAgentComponent
|
||||
from .agent import AgentComponent
|
||||
|
||||
__all__ = [
|
||||
"CSVAgentComponent",
|
||||
"CrewAIAgentComponent",
|
||||
"HierarchicalCrewComponent",
|
||||
"JsonAgentComponent",
|
||||
"OpenAIToolsAgentComponent",
|
||||
"OpenAPIAgentComponent",
|
||||
"SQLAgentComponent",
|
||||
"SequentialCrewComponent",
|
||||
"SequentialTaskAgentComponent",
|
||||
"ToolCallingAgentComponent",
|
||||
"VectorStoreAgentComponent",
|
||||
"VectorStoreRouterAgentComponent",
|
||||
"XMLAgentComponent",
|
||||
]
|
||||
__all__ = ["AgentComponent"]
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ from langchain_core.tools import StructuredTool
|
|||
|
||||
from langflow.base.agents.agent import LCToolsAgentComponent
|
||||
from langflow.base.models.model_input_constants import ALL_PROVIDER_FIELDS, MODEL_PROVIDERS_DICT
|
||||
from langflow.components.agents.tool_calling import ToolCallingAgentComponent
|
||||
from langflow.components.helpers import CurrentDateComponent
|
||||
from langflow.components.helpers.memory import MemoryComponent
|
||||
from langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent
|
||||
from langflow.components.memories.memory import MemoryComponent
|
||||
from langflow.io import BoolInput, DropdownInput, MultilineInput, Output
|
||||
from langflow.schema.dotdict import dotdict
|
||||
from langflow.schema.message import Message
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
from langchain.agents import AgentExecutor, create_vectorstore_agent
|
||||
from langchain.agents.agent_toolkits.vectorstore.toolkit import VectorStoreToolkit
|
||||
|
||||
from langflow.base.agents.agent import LCAgentComponent
|
||||
from langflow.inputs import HandleInput
|
||||
|
||||
|
||||
class VectorStoreAgentComponent(LCAgentComponent):
|
||||
display_name = "VectorStoreAgent"
|
||||
description = "Construct an agent from a Vector Store."
|
||||
name = "VectorStoreAgent"
|
||||
legacy: bool = True
|
||||
|
||||
inputs = [
|
||||
*LCAgentComponent._base_inputs,
|
||||
HandleInput(
|
||||
name="llm",
|
||||
display_name="Language Model",
|
||||
input_types=["LanguageModel"],
|
||||
required=True,
|
||||
),
|
||||
HandleInput(
|
||||
name="vectorstore",
|
||||
display_name="Vector Store",
|
||||
input_types=["VectorStoreInfo"],
|
||||
required=True,
|
||||
),
|
||||
]
|
||||
|
||||
def build_agent(self) -> AgentExecutor:
|
||||
toolkit = VectorStoreToolkit(vectorstore_info=self.vectorstore, llm=self.llm)
|
||||
return create_vectorstore_agent(llm=self.llm, toolkit=toolkit, **self.get_agent_kwargs())
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
from .assemblyai_get_subtitles import AssemblyAIGetSubtitles
|
||||
from .assemblyai_lemur import AssemblyAILeMUR
|
||||
from .assemblyai_list_transcripts import AssemblyAIListTranscripts
|
||||
from .assemblyai_poll_transcript import AssemblyAITranscriptionJobPoller
|
||||
from .assemblyai_start_transcript import AssemblyAITranscriptionJobCreator
|
||||
|
||||
__all__ = [
|
||||
"AssemblyAIGetSubtitles",
|
||||
"AssemblyAILeMUR",
|
||||
"AssemblyAIListTranscripts",
|
||||
"AssemblyAITranscriptionJobPoller",
|
||||
"AssemblyAITranscriptionJobCreator",
|
||||
]
|
||||
|
|
@ -18,7 +18,7 @@ from langflow.template import Output
|
|||
class AstraAssistantManager(ComponentWithCache):
|
||||
display_name = "Astra Assistant Manager"
|
||||
description = "Manages Assistant Interactions"
|
||||
icon = "bot"
|
||||
icon = "AstraDB"
|
||||
|
||||
inputs = [
|
||||
StrInput(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from langflow.template import Output
|
|||
|
||||
|
||||
class AssistantsCreateAssistant(ComponentWithCache):
|
||||
icon = "bot"
|
||||
icon = "AstraDB"
|
||||
display_name = "Create Assistant"
|
||||
description = "Creates an Assistant and returns it's id"
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from langflow.template import Output
|
|||
class AssistantsCreateThread(ComponentWithCache):
|
||||
display_name = "Create Assistant Thread"
|
||||
description = "Creates a thread and returns the thread id"
|
||||
|
||||
icon = "AstraDB"
|
||||
inputs = [
|
||||
MultilineInput(
|
||||
name="env_set",
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from langflow.template import Output
|
|||
class Dotenv(Component):
|
||||
display_name = "Dotenv"
|
||||
description = "Load .env file into env vars"
|
||||
|
||||
icon = "AstraDB"
|
||||
inputs = [
|
||||
MultilineSecretInput(
|
||||
name="dotenv_file_content",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from langflow.template import Output
|
|||
class AssistantsGetAssistantName(ComponentWithCache):
|
||||
display_name = "Get Assistant name"
|
||||
description = "Assistant by id"
|
||||
|
||||
icon = "AstraDB"
|
||||
inputs = [
|
||||
StrInput(
|
||||
name="assistant_id",
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from langflow.template import Output
|
|||
class GetEnvVar(Component):
|
||||
display_name = "Get env var"
|
||||
description = "Get env var"
|
||||
icon = "code"
|
||||
icon = "AstraDB"
|
||||
|
||||
inputs = [
|
||||
StrInput(
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from langflow.template.field.base import Output
|
|||
class AssistantsListAssistants(ComponentWithCache):
|
||||
display_name = "List Assistants"
|
||||
description = "Returns a list of assistant id's"
|
||||
|
||||
icon = "AstraDB"
|
||||
outputs = [
|
||||
Output(display_name="Assistants", name="assistants", method="process_inputs"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from langflow.template import Output
|
|||
class AssistantsRun(ComponentWithCache):
|
||||
display_name = "Run Assistant"
|
||||
description = "Executes an Assistant Run against a thread"
|
||||
icon = "AstraDB"
|
||||
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
from .conversation import ConversationChainComponent
|
||||
from .llm_checker import LLMCheckerChainComponent
|
||||
from .llm_math import LLMMathChainComponent
|
||||
from .retrieval_qa import RetrievalQAComponent
|
||||
from .sql_generator import SQLGeneratorComponent
|
||||
|
||||
__all__ = [
|
||||
"ConversationChainComponent",
|
||||
"LLMCheckerChainComponent",
|
||||
"LLMMathChainComponent",
|
||||
"RetrievalQAComponent",
|
||||
"SQLGeneratorComponent",
|
||||
]
|
||||
3
src/backend/base/langflow/components/cohere/__init__.py
Normal file
3
src/backend/base/langflow/components/cohere/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from .cohere_rerank import CohereRerankComponent
|
||||
|
||||
__all__ = ["CohereRerankComponent"]
|
||||
90
src/backend/base/langflow/components/cohere/cohere_rerank.py
Normal file
90
src/backend/base/langflow/components/cohere/cohere_rerank.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
from typing import cast
|
||||
|
||||
from langchain.retrievers import ContextualCompressionRetriever
|
||||
from langchain_cohere import CohereRerank
|
||||
|
||||
from langflow.base.vectorstores.model import (
|
||||
LCVectorStoreComponent,
|
||||
check_cached_vector_store,
|
||||
)
|
||||
from langflow.field_typing import Retriever, VectorStore
|
||||
from langflow.io import (
|
||||
DropdownInput,
|
||||
HandleInput,
|
||||
IntInput,
|
||||
MessageTextInput,
|
||||
MultilineInput,
|
||||
SecretStrInput,
|
||||
)
|
||||
from langflow.schema import Data
|
||||
from langflow.template.field.base import Output
|
||||
|
||||
|
||||
class CohereRerankComponent(LCVectorStoreComponent):
|
||||
display_name = "Cohere Rerank"
|
||||
description = "Rerank documents using the Cohere API and a retriever."
|
||||
name = "CohereRerank"
|
||||
icon = "Cohere"
|
||||
legacy: bool = True
|
||||
|
||||
inputs = [
|
||||
MultilineInput(
|
||||
name="search_query",
|
||||
display_name="Search Query",
|
||||
),
|
||||
DropdownInput(
|
||||
name="model",
|
||||
display_name="Model",
|
||||
options=[
|
||||
"rerank-english-v3.0",
|
||||
"rerank-multilingual-v3.0",
|
||||
"rerank-english-v2.0",
|
||||
"rerank-multilingual-v2.0",
|
||||
],
|
||||
value="rerank-english-v3.0",
|
||||
),
|
||||
SecretStrInput(name="api_key", display_name="API Key"),
|
||||
IntInput(name="top_n", display_name="Top N", value=3),
|
||||
MessageTextInput(
|
||||
name="user_agent",
|
||||
display_name="User Agent",
|
||||
value="langflow",
|
||||
advanced=True,
|
||||
),
|
||||
HandleInput(name="retriever", display_name="Retriever", input_types=["Retriever"]),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(
|
||||
display_name="Retriever",
|
||||
name="base_retriever",
|
||||
method="build_base_retriever",
|
||||
),
|
||||
Output(
|
||||
display_name="Search Results",
|
||||
name="search_results",
|
||||
method="search_documents",
|
||||
),
|
||||
]
|
||||
|
||||
def build_base_retriever(self) -> Retriever: # type: ignore[type-var]
|
||||
cohere_reranker = CohereRerank(
|
||||
cohere_api_key=self.api_key,
|
||||
model=self.model,
|
||||
top_n=self.top_n,
|
||||
user_agent=self.user_agent,
|
||||
)
|
||||
retriever = ContextualCompressionRetriever(base_compressor=cohere_reranker, base_retriever=self.retriever)
|
||||
return cast(Retriever, retriever)
|
||||
|
||||
async def search_documents(self) -> list[Data]: # type: ignore[override]
|
||||
retriever = self.build_base_retriever()
|
||||
documents = await retriever.ainvoke(self.search_query, config={"callbacks": self.get_langchain_callbacks()})
|
||||
data = self.to_data(documents)
|
||||
self.status = data
|
||||
return data
|
||||
|
||||
@check_cached_vector_store
|
||||
def build_vector_store(self) -> VectorStore:
|
||||
msg = "Cohere Rerank does not support vector stores."
|
||||
raise NotImplementedError(msg)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from .composio_api import ComposioAPIComponent
|
||||
|
||||
__all__ = ["ComposioAPIComponent"]
|
||||
173
src/backend/base/langflow/components/composio/composio_api.py
Normal file
173
src/backend/base/langflow/components/composio/composio_api.py
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from composio_langchain import Action, App, ComposioToolSet
|
||||
from langchain_core.tools import Tool
|
||||
from loguru import logger
|
||||
from typing_extensions import override
|
||||
|
||||
from langflow.base.langchain_utilities.model import LCToolComponent
|
||||
from langflow.inputs import DropdownInput, MessageTextInput, MultiselectInput, SecretStrInput, StrInput
|
||||
|
||||
|
||||
class ComposioAPIComponent(LCToolComponent):
|
||||
display_name: str = "Composio Tools"
|
||||
description: str = "Use Composio toolset to run actions with your agent"
|
||||
name = "ComposioAPI"
|
||||
icon = "Composio"
|
||||
documentation: str = "https://docs.composio.dev"
|
||||
|
||||
inputs = [
|
||||
MessageTextInput(name="entity_id", display_name="Entity ID", value="default", advanced=True),
|
||||
SecretStrInput(
|
||||
name="api_key",
|
||||
display_name="Composio API Key",
|
||||
required=True,
|
||||
refresh_button=True,
|
||||
info="Refer to https://docs.composio.dev/introduction/foundations/howtos/get_api_key",
|
||||
),
|
||||
DropdownInput(
|
||||
name="app_names",
|
||||
display_name="App Name",
|
||||
options=list(App.__annotations__),
|
||||
value="",
|
||||
info="The app name to use. Please refresh after selecting app name",
|
||||
refresh_button=True,
|
||||
),
|
||||
MultiselectInput(
|
||||
name="action_names",
|
||||
display_name="Actions to use",
|
||||
required=False,
|
||||
options=[],
|
||||
value=[],
|
||||
info="The actions to pass to agent to execute",
|
||||
),
|
||||
StrInput(
|
||||
name="auth_status_config",
|
||||
display_name="Auth status",
|
||||
value="",
|
||||
refresh_button=True,
|
||||
info="Open link or enter api key. Then refresh button",
|
||||
),
|
||||
]
|
||||
|
||||
def _check_for_authorization(self, app: str) -> str:
|
||||
"""Checks if the app is authorized.
|
||||
|
||||
Args:
|
||||
app (str): The app name to check authorization for.
|
||||
|
||||
Returns:
|
||||
str: The authorization status.
|
||||
"""
|
||||
toolset = self._build_wrapper()
|
||||
entity = toolset.client.get_entity(id=self.entity_id)
|
||||
try:
|
||||
entity.get_connection(app=app)
|
||||
except Exception: # noqa: BLE001
|
||||
logger.opt(exception=True).debug("Authorization error")
|
||||
return self._handle_authorization_failure(toolset, entity, app)
|
||||
|
||||
return f"{app} CONNECTED"
|
||||
|
||||
def _handle_authorization_failure(self, toolset: ComposioToolSet, entity: Any, app: str) -> str:
|
||||
"""Handles the authorization failure by attempting to process API key auth or initiate default connection.
|
||||
|
||||
Args:
|
||||
toolset (ComposioToolSet): The toolset instance.
|
||||
entity (Any): The entity instance.
|
||||
app (str): The app name.
|
||||
|
||||
Returns:
|
||||
str: The result of the authorization failure message.
|
||||
"""
|
||||
try:
|
||||
auth_schemes = toolset.client.apps.get(app).auth_schemes
|
||||
if auth_schemes[0].auth_mode == "API_KEY":
|
||||
return self._process_api_key_auth(entity, app)
|
||||
return self._initiate_default_connection(entity, app)
|
||||
except Exception: # noqa: BLE001
|
||||
logger.exception("Authorization error")
|
||||
return "Error"
|
||||
|
||||
def _process_api_key_auth(self, entity: Any, app: str) -> str:
|
||||
"""Processes the API key authentication.
|
||||
|
||||
Args:
|
||||
entity (Any): The entity instance.
|
||||
app (str): The app name.
|
||||
|
||||
Returns:
|
||||
str: The status of the API key authentication.
|
||||
"""
|
||||
auth_status_config = self.auth_status_config
|
||||
is_url = "http" in auth_status_config or "https" in auth_status_config
|
||||
is_different_app = "CONNECTED" in auth_status_config and app not in auth_status_config
|
||||
is_default_api_key_message = "API Key" in auth_status_config
|
||||
|
||||
if is_different_app or is_url or is_default_api_key_message:
|
||||
return "Enter API Key"
|
||||
if not is_default_api_key_message:
|
||||
entity.initiate_connection(
|
||||
app_name=app,
|
||||
auth_mode="API_KEY",
|
||||
auth_config={"api_key": self.auth_status_config},
|
||||
use_composio_auth=False,
|
||||
force_new_integration=True,
|
||||
)
|
||||
return f"{app} CONNECTED"
|
||||
return "Enter API Key"
|
||||
|
||||
def _initiate_default_connection(self, entity: Any, app: str) -> str:
|
||||
connection = entity.initiate_connection(app_name=app, use_composio_auth=True, force_new_integration=True)
|
||||
return connection.redirectUrl
|
||||
|
||||
def _get_connected_app_names_for_entity(self) -> list[str]:
|
||||
toolset = self._build_wrapper()
|
||||
connections = toolset.client.get_entity(id=self.entity_id).get_connections()
|
||||
return list({connection.appUniqueId for connection in connections})
|
||||
|
||||
def _update_app_names_with_connected_status(self, build_config: dict) -> dict:
|
||||
connected_app_names = self._get_connected_app_names_for_entity()
|
||||
|
||||
app_names = [
|
||||
f"{app_name}_CONNECTED" for app_name in App.__annotations__ if app_name.lower() in connected_app_names
|
||||
]
|
||||
non_connected_app_names = [
|
||||
app_name for app_name in App.__annotations__ if app_name.lower() not in connected_app_names
|
||||
]
|
||||
build_config["app_names"]["options"] = app_names + non_connected_app_names
|
||||
build_config["app_names"]["value"] = app_names[0] if app_names else ""
|
||||
return build_config
|
||||
|
||||
def _get_normalized_app_name(self) -> str:
|
||||
return self.app_names.replace("_CONNECTED", "").replace("_connected", "")
|
||||
|
||||
@override
|
||||
def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:
|
||||
if field_name == "api_key":
|
||||
if hasattr(self, "api_key") and self.api_key != "":
|
||||
build_config = self._update_app_names_with_connected_status(build_config)
|
||||
return build_config
|
||||
|
||||
if field_name in {"app_names", "auth_status_config"}:
|
||||
if hasattr(self, "api_key") and self.api_key != "":
|
||||
build_config["auth_status_config"]["value"] = self._check_for_authorization(
|
||||
self._get_normalized_app_name()
|
||||
)
|
||||
all_action_names = list(Action.__annotations__)
|
||||
app_action_names = [
|
||||
action_name
|
||||
for action_name in all_action_names
|
||||
if action_name.lower().startswith(self._get_normalized_app_name().lower() + "_")
|
||||
]
|
||||
build_config["action_names"]["options"] = app_action_names
|
||||
build_config["action_names"]["value"] = [app_action_names[0]] if app_action_names else [""]
|
||||
return build_config
|
||||
|
||||
def build_tool(self) -> Sequence[Tool]:
|
||||
composio_toolset = self._build_wrapper()
|
||||
return composio_toolset.get_tools(actions=self.action_names)
|
||||
|
||||
def _build_wrapper(self) -> ComposioToolSet:
|
||||
return ComposioToolSet(api_key=self.api_key)
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue