mirror of
https://github.com/hwchase17/langchain.git
synced 2025-06-30 02:13:23 +00:00
Added Slacktoolkit (#14012)
- **Description:** This PR introduces the Slack toolkit to LangChain, which allows users to read and write to Slack using the Slack API. Specifically, we've added the following tools. 1. get_channel: Provides a summary of all the channels in a workspace. 2. get_message: Gets the message history of a channel. 3. send_message: Sends a message to a channel. 4. schedule_message: Sends a message to a channel at a specific time and date. - **Issue:** This pull request addresses [Add Slack Toolkit #11747](https://github.com/langchain-ai/langchain/issues/11747) - **Dependencies:** package`slack_sdk` Note: For this toolkit to function you will need to add a Slack app to your workspace. Additional info can be found [here](https://slack.com/help/articles/202035138-Add-apps-to-your-Slack-workspace). --------- Co-authored-by: Bagatur <baskaryan@gmail.com> Co-authored-by: ArianneLavada <ariannelavada@gmail.com> Co-authored-by: ArianneLavada <84357335+ArianneLavada@users.noreply.github.com> Co-authored-by: ariannelavada@gmail.com <you@example.com>
This commit is contained in:
parent
99e5ee6a84
commit
32d4bb4590
147
docs/docs/integrations/toolkits/slack.ipynb
Normal file
147
docs/docs/integrations/toolkits/slack.ipynb
Normal file
@ -0,0 +1,147 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Slack\n",
|
||||
"\n",
|
||||
"This notebook walks through connecting LangChain to your `Slack` account.\n",
|
||||
"\n",
|
||||
"To use this toolkit, you will need to get a token explained in the [Slack API docs](https://api.slack.com/tutorials/tracks/getting-a-token). Once you've received a SLACK_USER_TOKEN, you can input it as an environmental variable below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install --upgrade slack_sdk > /dev/null\n",
|
||||
"!pip install beautifulsoup4 > /dev/null # This is optional but is useful for parsing HTML messages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Assign Environmental Variables\n",
|
||||
"\n",
|
||||
"The toolkit will read the SLACK_USER_TOKEN environmental variable to authenticate the user so you need to set them here. You will also need to set your OPENAI_API_KEY to use the agent later."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set environmental variables here"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Create the Toolkit and Get Tools\n",
|
||||
"\n",
|
||||
"To start, you need to create the toolkit, so you can access its tools later."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.agents.agent_toolkits import SlackToolkit\n",
|
||||
"\n",
|
||||
"toolkit = SlackToolkit()\n",
|
||||
"tools = toolkit.get_tools()\n",
|
||||
"tools"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Use within an Agent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.agents import AgentType, initialize_agent\n",
|
||||
"from langchain.llms import OpenAI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(temperature=0)\n",
|
||||
"agent = initialize_agent(\n",
|
||||
" tools=toolkit.get_tools(),\n",
|
||||
" llm=llm,\n",
|
||||
" verbose=False,\n",
|
||||
" agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"agent.run(\"Send a greeting to my coworkers in the #general channel.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"agent.run(\"How many channels are in the workspace? Please list out their names.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"agent.run(\n",
|
||||
" \"Tell me the number of messages sent in the #introductions channel from the past month.\"\n",
|
||||
")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
@ -42,6 +42,7 @@ from langchain.agents.agent_toolkits.playwright.toolkit import PlayWrightBrowser
|
||||
from langchain.agents.agent_toolkits.powerbi.base import create_pbi_agent
|
||||
from langchain.agents.agent_toolkits.powerbi.chat_base import create_pbi_chat_agent
|
||||
from langchain.agents.agent_toolkits.powerbi.toolkit import PowerBIToolkit
|
||||
from langchain.agents.agent_toolkits.slack.toolkit import SlackToolkit
|
||||
from langchain.agents.agent_toolkits.spark_sql.base import create_spark_sql_agent
|
||||
from langchain.agents.agent_toolkits.spark_sql.toolkit import SparkSQLToolkit
|
||||
from langchain.agents.agent_toolkits.sql.base import create_sql_agent
|
||||
@ -96,6 +97,7 @@ __all__ = [
|
||||
"OpenAPIToolkit",
|
||||
"PlayWrightBrowserToolkit",
|
||||
"PowerBIToolkit",
|
||||
"SlackToolkit",
|
||||
"SQLDatabaseToolkit",
|
||||
"SparkSQLToolkit",
|
||||
"VectorStoreInfo",
|
||||
|
@ -0,0 +1 @@
|
||||
"""Slack toolkit."""
|
@ -0,0 +1,35 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, List
|
||||
|
||||
from langchain.agents.agent_toolkits.base import BaseToolkit
|
||||
from langchain.pydantic_v1 import Field
|
||||
from langchain.tools import BaseTool
|
||||
from langchain.tools.slack.get_channel import SlackGetChannel
|
||||
from langchain.tools.slack.get_message import SlackGetMessage
|
||||
from langchain.tools.slack.schedule_message import SlackScheduleMessage
|
||||
from langchain.tools.slack.send_message import SlackSendMessage
|
||||
from langchain.tools.slack.utils import login
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from slack_sdk import WebClient
|
||||
|
||||
|
||||
class SlackToolkit(BaseToolkit):
|
||||
"""Toolkit for interacting with Slack."""
|
||||
|
||||
client: WebClient = Field(default_factory=login)
|
||||
|
||||
class Config:
|
||||
"""Pydantic config."""
|
||||
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
def get_tools(self) -> List[BaseTool]:
|
||||
"""Get the tools in the toolkit."""
|
||||
return [
|
||||
SlackGetChannel(),
|
||||
SlackGetMessage(),
|
||||
SlackScheduleMessage(),
|
||||
SlackSendMessage(),
|
||||
]
|
@ -558,6 +558,30 @@ def _import_shell_tool() -> Any:
|
||||
return ShellTool
|
||||
|
||||
|
||||
def _import_slack_get_channel() -> Any:
|
||||
from langchain.tools.slack.get_channel import SlackGetChannel
|
||||
|
||||
return SlackGetChannel
|
||||
|
||||
|
||||
def _import_slack_get_message() -> Any:
|
||||
from langchain.tools.slack.get_message import SlackGetMessage
|
||||
|
||||
return SlackGetMessage
|
||||
|
||||
|
||||
def _import_slack_schedule_message() -> Any:
|
||||
from langchain.tools.slack.schedule_message import SlackScheduleMessage
|
||||
|
||||
return SlackScheduleMessage
|
||||
|
||||
|
||||
def _import_slack_send_message() -> Any:
|
||||
from langchain.tools.slack.send_message import SlackSendMessage
|
||||
|
||||
return SlackSendMessage
|
||||
|
||||
|
||||
def _import_sleep_tool() -> Any:
|
||||
from langchain.tools.sleep.tool import SleepTool
|
||||
|
||||
@ -871,6 +895,14 @@ def __getattr__(name: str) -> Any:
|
||||
return _import_searx_search_tool_SearxSearchRun()
|
||||
elif name == "ShellTool":
|
||||
return _import_shell_tool()
|
||||
elif name == "SlackGetChannel":
|
||||
return _import_slack_get_channel
|
||||
elif name == "SlackGetMessage":
|
||||
return _import_slack_get_message
|
||||
elif name == "SlackScheduleMessage":
|
||||
return _import_slack_schedule_message
|
||||
elif name == "SlackSendMessage":
|
||||
return _import_slack_send_message
|
||||
elif name == "SleepTool":
|
||||
return _import_sleep_tool()
|
||||
elif name == "BaseSparkSQLTool":
|
||||
@ -1016,6 +1048,10 @@ __all__ = [
|
||||
"SearxSearchResults",
|
||||
"SearxSearchRun",
|
||||
"ShellTool",
|
||||
"SlackGetChannel",
|
||||
"SlackGetMessage",
|
||||
"SlackScheduleMessage",
|
||||
"SlackSendMessage",
|
||||
"SleepTool",
|
||||
"StdInInquireTool",
|
||||
"StackExchangeTool",
|
||||
|
15
libs/langchain/langchain/tools/slack/__init__.py
Normal file
15
libs/langchain/langchain/tools/slack/__init__.py
Normal file
@ -0,0 +1,15 @@
|
||||
"""Slack tools."""
|
||||
|
||||
from langchain.tools.slack.get_channel import SlackGetChannel
|
||||
from langchain.tools.slack.get_message import SlackGetMessage
|
||||
from langchain.tools.slack.schedule_message import SlackScheduleMessage
|
||||
from langchain.tools.slack.send_message import SlackSendMessage
|
||||
from langchain.tools.slack.utils import login
|
||||
|
||||
__all__ = [
|
||||
"SlackGetChannel",
|
||||
"SlackGetMessage",
|
||||
"SlackScheduleMessage",
|
||||
"SlackSendMessage",
|
||||
"login",
|
||||
]
|
18
libs/langchain/langchain/tools/slack/base.py
Normal file
18
libs/langchain/langchain/tools/slack/base.py
Normal file
@ -0,0 +1,18 @@
|
||||
"""Base class for Slack tools."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from langchain.pydantic_v1 import Field
|
||||
from langchain.tools.base import BaseTool
|
||||
from langchain.tools.slack.utils import login
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from slack_sdk import WebClient
|
||||
|
||||
|
||||
class SlackBaseTool(BaseTool):
|
||||
"""Base class for Slack tools."""
|
||||
|
||||
client: WebClient = Field(default_factory=login)
|
||||
"""The WebClient object."""
|
33
libs/langchain/langchain/tools/slack/get_channel.py
Normal file
33
libs/langchain/langchain/tools/slack/get_channel.py
Normal file
@ -0,0 +1,33 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from langchain.callbacks.manager import CallbackManagerForToolRun
|
||||
from langchain.tools.slack.base import SlackBaseTool
|
||||
|
||||
|
||||
class SlackGetChannel(SlackBaseTool):
|
||||
name: str = "get_channelid_name_dict"
|
||||
description: str = "Use this tool to get channelid-name dict."
|
||||
|
||||
def _run(
|
||||
self,
|
||||
run_manager: Optional[CallbackManagerForToolRun] = None,
|
||||
) -> str:
|
||||
try:
|
||||
logging.getLogger(__name__)
|
||||
|
||||
result = self.client.conversations_list()
|
||||
channels = result["channels"]
|
||||
filtered_result = [
|
||||
{key: channel[key] for key in ("id", "name", "created", "num_members")}
|
||||
for channel in channels
|
||||
if "id" in channel
|
||||
and "name" in channel
|
||||
and "created" in channel
|
||||
and "num_members" in channel
|
||||
]
|
||||
return json.dumps(filtered_result)
|
||||
|
||||
except Exception as e:
|
||||
return "Error creating conversation: {}".format(e)
|
41
libs/langchain/langchain/tools/slack/get_message.py
Normal file
41
libs/langchain/langchain/tools/slack/get_message.py
Normal file
@ -0,0 +1,41 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional, Type
|
||||
|
||||
from langchain.callbacks.manager import CallbackManagerForToolRun
|
||||
from langchain.pydantic_v1 import BaseModel, Field
|
||||
from langchain.tools.slack.base import SlackBaseTool
|
||||
|
||||
|
||||
class SlackGetMessageSchema(BaseModel):
|
||||
"""Input schema for SlackGetMessages."""
|
||||
|
||||
channel_id: str = Field(
|
||||
...,
|
||||
description="The channel id, private group, or IM channel to send message to.",
|
||||
)
|
||||
|
||||
|
||||
class SlackGetMessage(SlackBaseTool):
|
||||
name: str = "get_messages"
|
||||
description: str = "Use this tool to get messages from a channel."
|
||||
|
||||
args_schema: Type[SlackGetMessageSchema] = SlackGetMessageSchema
|
||||
|
||||
def _run(
|
||||
self,
|
||||
channel_id: str,
|
||||
run_manager: Optional[CallbackManagerForToolRun] = None,
|
||||
) -> str:
|
||||
logging.getLogger(__name__)
|
||||
try:
|
||||
result = self.client.conversations_history(channel=channel_id)
|
||||
messages = result["messages"]
|
||||
filtered_messages = [
|
||||
{key: message[key] for key in ("user", "text", "ts")}
|
||||
for message in messages
|
||||
if "user" in message and "text" in message and "ts" in message
|
||||
]
|
||||
return json.dumps(filtered_messages)
|
||||
except Exception as e:
|
||||
return "Error creating conversation: {}".format(e)
|
59
libs/langchain/langchain/tools/slack/schedule_message.py
Normal file
59
libs/langchain/langchain/tools/slack/schedule_message.py
Normal file
@ -0,0 +1,59 @@
|
||||
import logging
|
||||
from datetime import datetime as dt
|
||||
from typing import Optional, Type
|
||||
|
||||
from langchain.callbacks.manager import CallbackManagerForToolRun
|
||||
from langchain.pydantic_v1 import BaseModel, Field
|
||||
from langchain.tools.slack.base import SlackBaseTool
|
||||
from langchain.tools.slack.utils import UTC_FORMAT
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ScheduleMessageSchema(BaseModel):
|
||||
"""Input for ScheduleMessageTool."""
|
||||
|
||||
message: str = Field(
|
||||
...,
|
||||
description="The message to be sent.",
|
||||
)
|
||||
channel: str = Field(
|
||||
...,
|
||||
description="The channel, private group, or IM channel to send message to.",
|
||||
)
|
||||
timestamp: str = Field(
|
||||
...,
|
||||
description="The datetime for when the message should be sent in the "
|
||||
' following format: YYYY-MM-DDTHH:MM:SS±hh:mm, where "T" separates the date '
|
||||
" and time components, and the time zone offset is specified as ±hh:mm. "
|
||||
' For example: "2023-06-09T10:30:00+03:00" represents June 9th, '
|
||||
" 2023, at 10:30 AM in a time zone with a positive offset of 3 "
|
||||
" hours from Coordinated Universal Time (UTC).",
|
||||
)
|
||||
|
||||
|
||||
class SlackScheduleMessage(SlackBaseTool):
|
||||
"""Tool for scheduling a message in Slack."""
|
||||
|
||||
name: str = "schedule_message"
|
||||
description: str = (
|
||||
"Use this tool to schedule a message to be sent on a specific date and time."
|
||||
)
|
||||
args_schema: Type[ScheduleMessageSchema] = ScheduleMessageSchema
|
||||
|
||||
def _run(
|
||||
self,
|
||||
message: str,
|
||||
channel: str,
|
||||
timestamp: str,
|
||||
run_manager: Optional[CallbackManagerForToolRun] = None,
|
||||
) -> str:
|
||||
try:
|
||||
unix_timestamp = dt.timestamp(dt.strptime(timestamp, UTC_FORMAT))
|
||||
result = self.client.chat_scheduleMessage(
|
||||
channel=channel, text=message, post_at=unix_timestamp
|
||||
)
|
||||
output = "Message scheduled: " + str(result)
|
||||
return output
|
||||
except Exception as e:
|
||||
return "Error scheduling message: {}".format(e)
|
41
libs/langchain/langchain/tools/slack/send_message.py
Normal file
41
libs/langchain/langchain/tools/slack/send_message.py
Normal file
@ -0,0 +1,41 @@
|
||||
from typing import Optional, Type
|
||||
|
||||
from langchain.callbacks.manager import CallbackManagerForToolRun
|
||||
from langchain.pydantic_v1 import BaseModel, Field
|
||||
from langchain.tools.slack.base import SlackBaseTool
|
||||
|
||||
|
||||
class SendMessageSchema(BaseModel):
|
||||
"""Input for SendMessageTool."""
|
||||
|
||||
message: str = Field(
|
||||
...,
|
||||
description="The message to be sent.",
|
||||
)
|
||||
channel: str = Field(
|
||||
...,
|
||||
description="The channel, private group, or IM channel to send message to.",
|
||||
)
|
||||
|
||||
|
||||
class SlackSendMessage(SlackBaseTool):
|
||||
"""Tool for sending a message in Slack."""
|
||||
|
||||
name: str = "send_message"
|
||||
description: str = (
|
||||
"Use this tool to send a message with the provided message fields."
|
||||
)
|
||||
args_schema: Type[SendMessageSchema] = SendMessageSchema
|
||||
|
||||
def _run(
|
||||
self,
|
||||
message: str,
|
||||
channel: str,
|
||||
run_manager: Optional[CallbackManagerForToolRun] = None,
|
||||
) -> str:
|
||||
try:
|
||||
result = self.client.chat_postMessage(channel=channel, text=message)
|
||||
output = "Message sent: " + str(result)
|
||||
return output
|
||||
except Exception as e:
|
||||
return "Error creating conversation: {}".format(e)
|
42
libs/langchain/langchain/tools/slack/utils.py
Normal file
42
libs/langchain/langchain/tools/slack/utils.py
Normal file
@ -0,0 +1,42 @@
|
||||
"""Slack tool utils."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from slack_sdk import WebClient
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def login() -> WebClient:
|
||||
"""Authenticate using the Slack API."""
|
||||
try:
|
||||
from slack_sdk import WebClient
|
||||
except ImportError as e:
|
||||
raise ImportError(
|
||||
"Cannot import slack_sdk. Please install the package with \
|
||||
`pip install slack_sdk`."
|
||||
) from e
|
||||
|
||||
if "SLACK_BOT_TOKEN" in os.environ:
|
||||
token = os.environ["SLACK_BOT_TOKEN"]
|
||||
client = WebClient(token=token)
|
||||
logger.info("slack login success")
|
||||
return client
|
||||
elif "SLACK_USER_TOKEN" in os.environ:
|
||||
token = os.environ["SLACK_USER_TOKEN"]
|
||||
client = WebClient(token=token)
|
||||
logger.info("slack login success")
|
||||
return client
|
||||
else:
|
||||
logger.error(
|
||||
"Error: The SLACK_BOT_TOKEN or SLACK_USER_TOKEN \
|
||||
environment variable have not been set."
|
||||
)
|
||||
|
||||
|
||||
UTC_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
|
||||
"""UTC format for datetime objects."""
|
@ -94,6 +94,10 @@ EXPECTED_ALL = [
|
||||
"SearxSearchResults",
|
||||
"SearxSearchRun",
|
||||
"ShellTool",
|
||||
"SlackGetChannel",
|
||||
"SlackGetMessage",
|
||||
"SlackScheduleMessage",
|
||||
"SlackSendMessage",
|
||||
"SleepTool",
|
||||
"StackExchangeTool",
|
||||
"StdInInquireTool",
|
||||
|
@ -96,6 +96,10 @@ _EXPECTED = [
|
||||
"SearxSearchResults",
|
||||
"SearxSearchRun",
|
||||
"ShellTool",
|
||||
"SlackGetChannel",
|
||||
"SlackGetMessage",
|
||||
"SlackScheduleMessage",
|
||||
"SlackSendMessage",
|
||||
"SleepTool",
|
||||
"StdInInquireTool",
|
||||
"StackExchangeTool",
|
||||
|
@ -12,6 +12,7 @@ from langchain.tools.base import BaseTool
|
||||
from langchain.tools.gmail.base import GmailBaseTool
|
||||
from langchain.tools.office365.base import O365BaseTool
|
||||
from langchain.tools.playwright.base import BaseBrowserTool
|
||||
from langchain.tools.slack.base import SlackBaseTool
|
||||
|
||||
|
||||
def get_non_abstract_subclasses(cls: Type[BaseTool]) -> List[Type[BaseTool]]:
|
||||
@ -20,6 +21,7 @@ def get_non_abstract_subclasses(cls: Type[BaseTool]) -> List[Type[BaseTool]]:
|
||||
BaseBrowserTool,
|
||||
GmailBaseTool,
|
||||
O365BaseTool,
|
||||
SlackBaseTool,
|
||||
} # Abstract but not recognized
|
||||
subclasses = []
|
||||
for subclass in cls.__subclasses__():
|
||||
|
Loading…
Reference in New Issue
Block a user