camel-ai 0.1.9__py3-none-any.whl → 0.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of camel-ai might be problematic. Click here for more details.
- camel/__init__.py +1 -1
- camel/agents/chat_agent.py +334 -113
- camel/agents/knowledge_graph_agent.py +4 -6
- camel/bots/__init__.py +34 -0
- camel/bots/discord_app.py +138 -0
- camel/bots/slack/__init__.py +30 -0
- camel/bots/slack/models.py +158 -0
- camel/bots/slack/slack_app.py +255 -0
- camel/bots/telegram_bot.py +82 -0
- camel/configs/__init__.py +1 -2
- camel/configs/anthropic_config.py +2 -5
- camel/configs/base_config.py +6 -6
- camel/configs/gemini_config.py +1 -1
- camel/configs/groq_config.py +2 -3
- camel/configs/ollama_config.py +1 -2
- camel/configs/openai_config.py +2 -23
- camel/configs/samba_config.py +2 -2
- camel/configs/togetherai_config.py +1 -1
- camel/configs/vllm_config.py +1 -1
- camel/configs/zhipuai_config.py +2 -3
- camel/embeddings/openai_embedding.py +2 -2
- camel/loaders/__init__.py +2 -0
- camel/loaders/chunkr_reader.py +163 -0
- camel/loaders/firecrawl_reader.py +13 -45
- camel/loaders/unstructured_io.py +65 -29
- camel/messages/__init__.py +1 -0
- camel/messages/func_message.py +2 -2
- camel/models/__init__.py +2 -4
- camel/models/anthropic_model.py +32 -26
- camel/models/azure_openai_model.py +39 -36
- camel/models/base_model.py +31 -20
- camel/models/gemini_model.py +37 -29
- camel/models/groq_model.py +29 -23
- camel/models/litellm_model.py +44 -61
- camel/models/mistral_model.py +33 -30
- camel/models/model_factory.py +66 -76
- camel/models/nemotron_model.py +33 -23
- camel/models/ollama_model.py +42 -47
- camel/models/{openai_compatibility_model.py → openai_compatible_model.py} +36 -41
- camel/models/openai_model.py +60 -25
- camel/models/reka_model.py +30 -28
- camel/models/samba_model.py +82 -177
- camel/models/stub_model.py +2 -2
- camel/models/togetherai_model.py +37 -43
- camel/models/vllm_model.py +43 -50
- camel/models/zhipuai_model.py +33 -27
- camel/retrievers/auto_retriever.py +28 -10
- camel/retrievers/vector_retriever.py +72 -44
- camel/societies/babyagi_playing.py +6 -3
- camel/societies/role_playing.py +17 -3
- camel/storages/__init__.py +2 -0
- camel/storages/graph_storages/__init__.py +2 -0
- camel/storages/graph_storages/graph_element.py +3 -5
- camel/storages/graph_storages/nebula_graph.py +547 -0
- camel/storages/key_value_storages/json.py +6 -1
- camel/tasks/task.py +11 -4
- camel/tasks/task_prompt.py +4 -0
- camel/toolkits/__init__.py +28 -24
- camel/toolkits/arxiv_toolkit.py +155 -0
- camel/toolkits/ask_news_toolkit.py +653 -0
- camel/toolkits/base.py +2 -3
- camel/toolkits/code_execution.py +6 -7
- camel/toolkits/dalle_toolkit.py +6 -6
- camel/toolkits/{openai_function.py → function_tool.py} +34 -11
- camel/toolkits/github_toolkit.py +9 -10
- camel/toolkits/google_maps_toolkit.py +7 -14
- camel/toolkits/google_scholar_toolkit.py +146 -0
- camel/toolkits/linkedin_toolkit.py +7 -10
- camel/toolkits/math_toolkit.py +8 -8
- camel/toolkits/open_api_toolkit.py +5 -8
- camel/toolkits/reddit_toolkit.py +7 -10
- camel/toolkits/retrieval_toolkit.py +5 -9
- camel/toolkits/search_toolkit.py +9 -9
- camel/toolkits/slack_toolkit.py +11 -14
- camel/toolkits/twitter_toolkit.py +377 -454
- camel/toolkits/weather_toolkit.py +6 -6
- camel/toolkits/whatsapp_toolkit.py +177 -0
- camel/types/__init__.py +6 -1
- camel/types/enums.py +43 -85
- camel/types/openai_types.py +3 -0
- camel/types/unified_model_type.py +104 -0
- camel/utils/__init__.py +0 -2
- camel/utils/async_func.py +7 -7
- camel/utils/commons.py +40 -4
- camel/utils/token_counting.py +38 -214
- camel/workforce/__init__.py +6 -6
- camel/workforce/base.py +9 -5
- camel/workforce/prompts.py +179 -0
- camel/workforce/role_playing_worker.py +181 -0
- camel/workforce/{single_agent_node.py → single_agent_worker.py} +49 -23
- camel/workforce/task_channel.py +7 -8
- camel/workforce/utils.py +20 -50
- camel/workforce/{worker_node.py → worker.py} +15 -12
- camel/workforce/workforce.py +456 -19
- camel_ai-0.2.3.dist-info/LICENSE +201 -0
- {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/METADATA +40 -65
- {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/RECORD +98 -86
- {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/WHEEL +1 -1
- camel/models/open_source_model.py +0 -170
- camel/workforce/manager_node.py +0 -299
- camel/workforce/role_playing_node.py +0 -168
- camel/workforce/workforce_prompt.py +0 -125
|
@@ -11,12 +11,10 @@
|
|
|
11
11
|
# See the License for the specific language governing permissions and
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
-
from typing import Optional, Union
|
|
14
|
+
from typing import TYPE_CHECKING, Optional, Union
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
17
|
from unstructured.documents.elements import Element
|
|
18
|
-
except ImportError:
|
|
19
|
-
Element = None
|
|
20
18
|
|
|
21
19
|
from camel.agents import ChatAgent
|
|
22
20
|
from camel.messages import BaseMessage
|
|
@@ -144,13 +142,13 @@ class KnowledgeGraphAgent(ChatAgent):
|
|
|
144
142
|
|
|
145
143
|
def run(
|
|
146
144
|
self,
|
|
147
|
-
element:
|
|
145
|
+
element: "Element",
|
|
148
146
|
parse_graph_elements: bool = False,
|
|
149
147
|
) -> Union[str, GraphElement]:
|
|
150
148
|
r"""Run the agent to extract node and relationship information.
|
|
151
149
|
|
|
152
150
|
Args:
|
|
153
|
-
element (
|
|
151
|
+
element (Element): The input element.
|
|
154
152
|
parse_graph_elements (bool, optional): Whether to parse into
|
|
155
153
|
`GraphElement`. Defaults to `False`.
|
|
156
154
|
|
camel/bots/__init__.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
+
from .discord_app import DiscordApp
|
|
15
|
+
from .slack.models import (
|
|
16
|
+
SlackAppMentionEventBody,
|
|
17
|
+
SlackAppMentionEventProfile,
|
|
18
|
+
SlackAuthProfile,
|
|
19
|
+
SlackEventBody,
|
|
20
|
+
SlackEventProfile,
|
|
21
|
+
)
|
|
22
|
+
from .slack.slack_app import SlackApp
|
|
23
|
+
from .telegram_bot import TelegramBot
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
'DiscordApp',
|
|
27
|
+
'SlackApp',
|
|
28
|
+
'SlackAppMentionEventBody',
|
|
29
|
+
'SlackAppMentionEventProfile',
|
|
30
|
+
'SlackAuthProfile',
|
|
31
|
+
'SlackEventBody',
|
|
32
|
+
'SlackEventProfile',
|
|
33
|
+
'TelegramBot',
|
|
34
|
+
]
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
17
|
+
|
|
18
|
+
from camel.utils import dependencies_required
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from discord import Message
|
|
22
|
+
|
|
23
|
+
logging.basicConfig(level=logging.INFO)
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DiscordApp:
|
|
28
|
+
r"""A class representing a Discord app that uses the `discord.py` library
|
|
29
|
+
to interact with Discord servers.
|
|
30
|
+
|
|
31
|
+
This bot can respond to messages in specific channels and only reacts to
|
|
32
|
+
messages that mention the bot.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
channel_ids (Optional[List[int]]): A list of allowed channel IDs. If
|
|
36
|
+
provided, the bot will only respond to messages in these channels.
|
|
37
|
+
token (Optional[str]): The Discord bot token used for authentication.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@dependencies_required('discord')
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
channel_ids: Optional[List[int]] = None,
|
|
44
|
+
token: Optional[str] = None,
|
|
45
|
+
) -> None:
|
|
46
|
+
r"""Initialize the DiscordApp instance by setting up the Discord client
|
|
47
|
+
and event handlers.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
channel_ids (Optional[List[int]]): A list of allowed channel IDs.
|
|
51
|
+
The bot will only respond to messages in these channels if
|
|
52
|
+
provided.
|
|
53
|
+
token (Optional[str]): The Discord bot token for authentication.
|
|
54
|
+
If not provided, the token will be retrieved from the
|
|
55
|
+
environment variable `DISCORD_TOKEN`.
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
ValueError: If the `DISCORD_TOKEN` is not found in environment
|
|
59
|
+
variables.
|
|
60
|
+
"""
|
|
61
|
+
self.token = token or os.getenv('DISCORD_TOKEN')
|
|
62
|
+
self.channel_ids = channel_ids
|
|
63
|
+
|
|
64
|
+
if not self.token:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
"`DISCORD_TOKEN` not found in environment variables. Get it"
|
|
67
|
+
" here: `https://discord.com/developers/applications`."
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
import discord
|
|
71
|
+
|
|
72
|
+
intents = discord.Intents.default()
|
|
73
|
+
intents.message_content = True
|
|
74
|
+
self._client = discord.Client(intents=intents)
|
|
75
|
+
|
|
76
|
+
# Register event handlers
|
|
77
|
+
self._client.event(self.on_ready)
|
|
78
|
+
self._client.event(self.on_message)
|
|
79
|
+
|
|
80
|
+
async def start(self):
|
|
81
|
+
r"""Asynchronously start the Discord bot using its token.
|
|
82
|
+
|
|
83
|
+
This method starts the bot and logs into Discord asynchronously using
|
|
84
|
+
the provided token. It should be awaited when used in an async
|
|
85
|
+
environment.
|
|
86
|
+
"""
|
|
87
|
+
await self._client.start(self.token)
|
|
88
|
+
|
|
89
|
+
def run(self) -> None:
|
|
90
|
+
r"""Start the Discord bot using its token.
|
|
91
|
+
|
|
92
|
+
This method starts the bot and logs into Discord synchronously using
|
|
93
|
+
the provided token. It blocks execution and keeps the bot running.
|
|
94
|
+
"""
|
|
95
|
+
self._client.run(self.token) # type: ignore[arg-type]
|
|
96
|
+
|
|
97
|
+
async def on_ready(self) -> None:
|
|
98
|
+
r"""Event handler that is called when the bot has successfully
|
|
99
|
+
connected to the Discord server.
|
|
100
|
+
|
|
101
|
+
When the bot is ready and logged into Discord, it prints a message
|
|
102
|
+
displaying the bot's username.
|
|
103
|
+
"""
|
|
104
|
+
logger.info(f'We have logged in as {self._client.user}')
|
|
105
|
+
|
|
106
|
+
async def on_message(self, message: 'Message') -> None:
|
|
107
|
+
r"""Event handler for processing incoming messages.
|
|
108
|
+
|
|
109
|
+
This method is called whenever a new message is received by the bot. It
|
|
110
|
+
will ignore messages sent by the bot itself, only respond to messages
|
|
111
|
+
in allowed channels (if specified), and only to messages that mention
|
|
112
|
+
the bot.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
message (discord.Message): The message object received from
|
|
116
|
+
Discord.
|
|
117
|
+
"""
|
|
118
|
+
# If the message author is the bot itself,
|
|
119
|
+
# do not respond to this message
|
|
120
|
+
if message.author == self._client.user:
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
# If allowed channel IDs are provided,
|
|
124
|
+
# only respond to messages in those channels
|
|
125
|
+
if self.channel_ids and message.channel.id not in self.channel_ids:
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
# Only respond to messages that mention the bot
|
|
129
|
+
if not self._client.user or not self._client.user.mentioned_in(
|
|
130
|
+
message
|
|
131
|
+
):
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
logger.info(f"Received message: {message.content}")
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def client(self):
|
|
138
|
+
return self._client
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
+
from .models import (
|
|
15
|
+
SlackAppMentionEventBody,
|
|
16
|
+
SlackAppMentionEventProfile,
|
|
17
|
+
SlackAuthProfile,
|
|
18
|
+
SlackEventBody,
|
|
19
|
+
SlackEventProfile,
|
|
20
|
+
)
|
|
21
|
+
from .slack_app import SlackApp
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
'SlackApp',
|
|
25
|
+
'SlackAppMentionEventBody',
|
|
26
|
+
'SlackAppMentionEventProfile',
|
|
27
|
+
'SlackAuthProfile',
|
|
28
|
+
'SlackEventBody',
|
|
29
|
+
'SlackEventProfile',
|
|
30
|
+
]
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
+
from typing import Optional
|
|
15
|
+
|
|
16
|
+
from pydantic import BaseModel
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SlackAuthProfile(BaseModel):
|
|
20
|
+
r"""Represents the authorization profile within a Slack event.
|
|
21
|
+
|
|
22
|
+
Events will contain a single, compact authorizations field that shows one
|
|
23
|
+
installation of your app that the event is visible to.
|
|
24
|
+
In other words, lists of authorizations will be truncated to one element.
|
|
25
|
+
|
|
26
|
+
If there's more than one installing party that your app is keeping track
|
|
27
|
+
of, it's best not to rely on the single party listed in authorizations to
|
|
28
|
+
be any particular one.
|
|
29
|
+
|
|
30
|
+
To get a full list of who can see events, call the apps.event.
|
|
31
|
+
authorizations.list method after obtaining an app-level token. Read more on
|
|
32
|
+
the changes here; they have taken effect for existing apps as of
|
|
33
|
+
February 24, 2021.
|
|
34
|
+
|
|
35
|
+
References:
|
|
36
|
+
|
|
37
|
+
- https://api.slack.com/apis/events-api#authorizations
|
|
38
|
+
- https://api.slack.com/changelog/2020-09-15-events-api-truncate-authed-users#no_context
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
enterprise_id: Optional[str] = None
|
|
42
|
+
"""The ID of the enterprise associated with the authorization."""
|
|
43
|
+
|
|
44
|
+
team_id: str
|
|
45
|
+
"""The ID of the team associated with the authorization."""
|
|
46
|
+
|
|
47
|
+
user_id: str
|
|
48
|
+
"""The ID of the user associated with the authorization."""
|
|
49
|
+
|
|
50
|
+
is_bot: bool
|
|
51
|
+
"""Whether the authorized user is a bot."""
|
|
52
|
+
|
|
53
|
+
is_enterprise_install: bool
|
|
54
|
+
"""Whether the authorization is for an enterprise installation."""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SlackEventProfile(BaseModel):
|
|
58
|
+
r"""Represents the detailed profile of a Slack event, including user,
|
|
59
|
+
message, and context data.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
user: str
|
|
63
|
+
"""The ID of the user associated with the event."""
|
|
64
|
+
|
|
65
|
+
type: str
|
|
66
|
+
"""The type of the event (e.g., 'message')."""
|
|
67
|
+
|
|
68
|
+
ts: str
|
|
69
|
+
"""A timestamp representing when the event was triggered."""
|
|
70
|
+
|
|
71
|
+
thread_ts: Optional[str] = None
|
|
72
|
+
"""The timestamp of the parent message in a thread."""
|
|
73
|
+
|
|
74
|
+
client_msg_id: str
|
|
75
|
+
"""A unique ID generated by the client for the message (if available)."""
|
|
76
|
+
|
|
77
|
+
text: str
|
|
78
|
+
"""The message content text."""
|
|
79
|
+
|
|
80
|
+
team: str
|
|
81
|
+
"""The ID of the team that the event is associated with."""
|
|
82
|
+
|
|
83
|
+
blocks: list
|
|
84
|
+
"""The list of message blocks, providing structured information."""
|
|
85
|
+
|
|
86
|
+
channel: str
|
|
87
|
+
"""The ID of the Slack channel where the event happened."""
|
|
88
|
+
|
|
89
|
+
event_ts: str
|
|
90
|
+
"""The event-specific timestamp when it occurred."""
|
|
91
|
+
|
|
92
|
+
channel_type: Optional[str]
|
|
93
|
+
"""The type of Slack channel (e.g., 'channel', 'im')."""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class SlackEventBody(BaseModel):
|
|
97
|
+
r"""Represents the entire body of a Slack event, including the event
|
|
98
|
+
profile, authorization, and context.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
token: str
|
|
102
|
+
"""The token to verify the source of the event."""
|
|
103
|
+
|
|
104
|
+
team_id: str
|
|
105
|
+
"""The ID of the team where the event is happening."""
|
|
106
|
+
|
|
107
|
+
context_team_id: Optional[str]
|
|
108
|
+
"""The team ID for the shared channel context, if applicable."""
|
|
109
|
+
|
|
110
|
+
context_enterprise_id: Optional[str] = None
|
|
111
|
+
"""The enterprise ID for the shared channel context, if applicable."""
|
|
112
|
+
|
|
113
|
+
api_app_id: str
|
|
114
|
+
"""The unique identifier for the Slack app that received the event."""
|
|
115
|
+
|
|
116
|
+
event: SlackEventProfile
|
|
117
|
+
"""A detailed profile of the event"""
|
|
118
|
+
|
|
119
|
+
type: str
|
|
120
|
+
"""The overall type of event received (e.g., 'event_callback')."""
|
|
121
|
+
|
|
122
|
+
event_id: str
|
|
123
|
+
"""A unique identifier assigned to this event by Slack."""
|
|
124
|
+
|
|
125
|
+
event_time: int
|
|
126
|
+
"""The timestamp (in seconds) representing when the event was triggered."""
|
|
127
|
+
|
|
128
|
+
authorizations: Optional[list[SlackAuthProfile]] = None
|
|
129
|
+
"""An optional list of authorizations that describe which installation can
|
|
130
|
+
see the event."""
|
|
131
|
+
|
|
132
|
+
is_ext_shared_channel: bool
|
|
133
|
+
"""Indicates if the event is part of a shared channel between different
|
|
134
|
+
organizations."""
|
|
135
|
+
|
|
136
|
+
event_context: str
|
|
137
|
+
"""A unique string representing the context of the event."""
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class SlackAppMentionEventProfile(SlackEventProfile):
|
|
141
|
+
r"""Represents the detailed profile of a Slack event where the app was
|
|
142
|
+
mentioned in a message.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
channel_type: Optional[str] = None
|
|
146
|
+
"""The type of Slack channel. it's None for app mentions."""
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class SlackAppMentionEventBody(SlackEventBody):
|
|
150
|
+
r"""Represents the entire body of a Slack event where the app was mentioned
|
|
151
|
+
in a message.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
context_team_id: Optional[str] = None
|
|
155
|
+
"""A detailed profile of the event. it's None for app mentions."""
|
|
156
|
+
|
|
157
|
+
event: SlackAppMentionEventProfile
|
|
158
|
+
"""A detailed profile of the event"""
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the “License”);
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an “AS IS” BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
17
|
+
|
|
18
|
+
from slack_sdk.oauth.installation_store.async_installation_store import (
|
|
19
|
+
AsyncInstallationStore,
|
|
20
|
+
)
|
|
21
|
+
from starlette import requests, responses
|
|
22
|
+
|
|
23
|
+
from camel.bots.slack.models import (
|
|
24
|
+
SlackAppMentionEventBody,
|
|
25
|
+
SlackAppMentionEventProfile,
|
|
26
|
+
SlackEventBody,
|
|
27
|
+
SlackEventProfile,
|
|
28
|
+
)
|
|
29
|
+
from camel.utils import dependencies_required
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from slack_bolt.context.async_context import AsyncBoltContext
|
|
33
|
+
from slack_bolt.context.say.async_say import AsyncSay
|
|
34
|
+
from slack_sdk.web.async_client import AsyncWebClient
|
|
35
|
+
|
|
36
|
+
logging.basicConfig(level=logging.INFO)
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SlackApp:
|
|
41
|
+
r"""Represents a Slack app that is powered by a Slack Bolt `AsyncApp`.
|
|
42
|
+
|
|
43
|
+
This class is responsible for initializing and managing the Slack
|
|
44
|
+
application by setting up event handlers, running the app server, and
|
|
45
|
+
handling events such as messages and mentions from Slack.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
token (Optional[str]): Slack API token for authentication.
|
|
49
|
+
scopes (Optional[str]): Slack app scopes for permissions.
|
|
50
|
+
signing_secret (Optional[str]): Signing secret for verifying Slack
|
|
51
|
+
requests.
|
|
52
|
+
client_id (Optional[str]): Slack app client ID.
|
|
53
|
+
client_secret (Optional[str]): Slack app client secret.
|
|
54
|
+
redirect_uri_path (str): The URI path for OAuth redirect, defaults to
|
|
55
|
+
"/slack/oauth_redirect".
|
|
56
|
+
installation_store (Optional[AsyncInstallationStore]): The installation
|
|
57
|
+
store for handling OAuth installations.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
@dependencies_required('slack_bolt')
|
|
61
|
+
def __init__(
|
|
62
|
+
self,
|
|
63
|
+
token: Optional[str] = None,
|
|
64
|
+
scopes: Optional[str] = None,
|
|
65
|
+
signing_secret: Optional[str] = None,
|
|
66
|
+
client_id: Optional[str] = None,
|
|
67
|
+
client_secret: Optional[str] = None,
|
|
68
|
+
redirect_uri_path: str = "/slack/oauth_redirect",
|
|
69
|
+
installation_store: Optional[AsyncInstallationStore] = None,
|
|
70
|
+
) -> None:
|
|
71
|
+
r"""Initializes the SlackApp instance by setting up the Slack Bolt app
|
|
72
|
+
and configuring event handlers and OAuth settings.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
token (Optional[str]): The Slack API token.
|
|
76
|
+
scopes (Optional[str]): The scopes for Slack app permissions.
|
|
77
|
+
signing_secret (Optional[str]): The signing secret for verifying
|
|
78
|
+
requests.
|
|
79
|
+
client_id (Optional[str]): The Slack app client ID.
|
|
80
|
+
client_secret (Optional[str]): The Slack app client secret.
|
|
81
|
+
redirect_uri_path (str): The URI path for handling OAuth redirects
|
|
82
|
+
(default is "/slack/oauth_redirect").
|
|
83
|
+
installation_store (Optional[AsyncInstallationStore]): An optional
|
|
84
|
+
installation store for OAuth installations.
|
|
85
|
+
"""
|
|
86
|
+
from slack_bolt.adapter.starlette.async_handler import (
|
|
87
|
+
AsyncSlackRequestHandler,
|
|
88
|
+
)
|
|
89
|
+
from slack_bolt.app.async_app import AsyncApp
|
|
90
|
+
from slack_bolt.oauth.async_oauth_settings import AsyncOAuthSettings
|
|
91
|
+
|
|
92
|
+
self.token: Optional[str] = token or os.getenv("SLACK_TOKEN")
|
|
93
|
+
self.scopes: Optional[str] = scopes or os.getenv("SLACK_SCOPES")
|
|
94
|
+
self.signing_secret: Optional[str] = signing_secret or os.getenv(
|
|
95
|
+
"SLACK_SIGNING_SECRET"
|
|
96
|
+
)
|
|
97
|
+
self.client_id: Optional[str] = client_id or os.getenv(
|
|
98
|
+
"SLACK_CLIENT_ID"
|
|
99
|
+
)
|
|
100
|
+
self.client_secret: Optional[str] = client_secret or os.getenv(
|
|
101
|
+
"SLACK_CLIENT_SECRET"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if not all([self.token, self.scopes, self.signing_secret]):
|
|
105
|
+
raise ValueError(
|
|
106
|
+
"`SLACK_TOKEN`, `SLACK_SCOPES`, and `SLACK_SIGNING_SECRET` "
|
|
107
|
+
"environment variables must be set. Get it here: "
|
|
108
|
+
"`https://api.slack.com/apps`."
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Setup OAuth settings if client ID and secret are provided
|
|
112
|
+
if self.client_id and self.client_secret:
|
|
113
|
+
self._app = AsyncApp(
|
|
114
|
+
oauth_settings=AsyncOAuthSettings(
|
|
115
|
+
client_id=self.client_id,
|
|
116
|
+
client_secret=self.client_secret,
|
|
117
|
+
scopes=self.scopes,
|
|
118
|
+
redirect_uri_path=redirect_uri_path,
|
|
119
|
+
),
|
|
120
|
+
logger=logger,
|
|
121
|
+
signing_secret=self.signing_secret,
|
|
122
|
+
installation_store=installation_store,
|
|
123
|
+
token=self.token,
|
|
124
|
+
)
|
|
125
|
+
else:
|
|
126
|
+
# Initialize Slack Bolt AsyncApp with settings
|
|
127
|
+
self._app = AsyncApp(
|
|
128
|
+
logger=logger,
|
|
129
|
+
signing_secret=self.signing_secret,
|
|
130
|
+
installation_store=installation_store,
|
|
131
|
+
token=self.token,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
self._handler = AsyncSlackRequestHandler(self._app)
|
|
135
|
+
self.setup_handlers()
|
|
136
|
+
|
|
137
|
+
def setup_handlers(self) -> None:
|
|
138
|
+
r"""Sets up the event handlers for Slack events, such as `app_mention`
|
|
139
|
+
and `message`.
|
|
140
|
+
|
|
141
|
+
This method registers the `app_mention` and `on_message` event handlers
|
|
142
|
+
with the Slack Bolt app to respond to Slack events.
|
|
143
|
+
"""
|
|
144
|
+
self._app.event("app_mention")(self.app_mention)
|
|
145
|
+
self._app.event("message")(self.on_message)
|
|
146
|
+
|
|
147
|
+
def run(
|
|
148
|
+
self,
|
|
149
|
+
port: int = 3000,
|
|
150
|
+
path: str = "/slack/events",
|
|
151
|
+
host: Optional[str] = None,
|
|
152
|
+
) -> None:
|
|
153
|
+
r"""Starts the Slack Bolt app server to listen for incoming Slack
|
|
154
|
+
events.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
port (int): The port on which the server should run (default is
|
|
158
|
+
3000).
|
|
159
|
+
path (str): The endpoint path for receiving Slack events (default
|
|
160
|
+
is "/slack/events").
|
|
161
|
+
host (Optional[str]): The hostname to bind the server (default is
|
|
162
|
+
None).
|
|
163
|
+
"""
|
|
164
|
+
self._app.start(port=port, path=path, host=host)
|
|
165
|
+
|
|
166
|
+
async def handle_request(
|
|
167
|
+
self, request: requests.Request
|
|
168
|
+
) -> responses.Response:
|
|
169
|
+
r"""Handles incoming requests from Slack through the request handler.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
request (Request): A Starlette request object representing the
|
|
173
|
+
incoming request.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
The response generated by the Slack Bolt handler.
|
|
177
|
+
"""
|
|
178
|
+
return await self._handler.handle(request)
|
|
179
|
+
|
|
180
|
+
async def app_mention(
|
|
181
|
+
self,
|
|
182
|
+
context: "AsyncBoltContext",
|
|
183
|
+
client: "AsyncWebClient",
|
|
184
|
+
event: Dict[str, Any],
|
|
185
|
+
body: Dict[str, Any],
|
|
186
|
+
say: "AsyncSay",
|
|
187
|
+
) -> None:
|
|
188
|
+
r"""Event handler for `app_mention` events.
|
|
189
|
+
|
|
190
|
+
This method is triggered when someone mentions the app in Slack.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
context (AsyncBoltContext): The Slack Bolt context for the event.
|
|
194
|
+
client (AsyncWebClient): The Slack Web API client.
|
|
195
|
+
event (Dict[str, Any]): The event data for the app mention.
|
|
196
|
+
body (Dict[str, Any]): The full request body from Slack.
|
|
197
|
+
say (AsyncSay): A function to send a response back to the channel.
|
|
198
|
+
"""
|
|
199
|
+
event_profile = SlackAppMentionEventProfile(**event)
|
|
200
|
+
event_body = SlackAppMentionEventBody(**body)
|
|
201
|
+
|
|
202
|
+
logger.info(f"app_mention, context: {context}")
|
|
203
|
+
logger.info(f"app_mention, client: {client}")
|
|
204
|
+
logger.info(f"app_mention, event_profile: {event_profile}")
|
|
205
|
+
logger.info(f"app_mention, event_body: {event_body}")
|
|
206
|
+
logger.info(f"app_mention, say: {say}")
|
|
207
|
+
|
|
208
|
+
async def on_message(
|
|
209
|
+
self,
|
|
210
|
+
context: "AsyncBoltContext",
|
|
211
|
+
client: "AsyncWebClient",
|
|
212
|
+
event: Dict[str, Any],
|
|
213
|
+
body: Dict[str, Any],
|
|
214
|
+
say: "AsyncSay",
|
|
215
|
+
) -> None:
|
|
216
|
+
r"""Event handler for `message` events.
|
|
217
|
+
|
|
218
|
+
This method is triggered when the app receives a message in Slack.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
context (AsyncBoltContext): The Slack Bolt context for the event.
|
|
222
|
+
client (AsyncWebClient): The Slack Web API client.
|
|
223
|
+
event (Dict[str, Any]): The event data for the message.
|
|
224
|
+
body (Dict[str, Any]): The full request body from Slack.
|
|
225
|
+
say (AsyncSay): A function to send a response back to the channel.
|
|
226
|
+
"""
|
|
227
|
+
await context.ack()
|
|
228
|
+
|
|
229
|
+
event_profile = SlackEventProfile(**event)
|
|
230
|
+
event_body = SlackEventBody(**body)
|
|
231
|
+
|
|
232
|
+
logger.info(f"on_message, context: {context}")
|
|
233
|
+
logger.info(f"on_message, client: {client}")
|
|
234
|
+
logger.info(f"on_message, event_profile: {event_profile}")
|
|
235
|
+
logger.info(f"on_message, event_body: {event_body}")
|
|
236
|
+
logger.info(f"on_message, say: {say}")
|
|
237
|
+
|
|
238
|
+
logger.info(f"Received message: {event_profile.text}")
|
|
239
|
+
|
|
240
|
+
def mention_me(
|
|
241
|
+
self, context: "AsyncBoltContext", body: SlackEventBody
|
|
242
|
+
) -> bool:
|
|
243
|
+
r"""Check if the bot is mentioned in the message.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
context (AsyncBoltContext): The Slack Bolt context for the event.
|
|
247
|
+
body (SlackEventBody): The body of the Slack event.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
bool: True if the bot is mentioned in the message, False otherwise.
|
|
251
|
+
"""
|
|
252
|
+
message = body.event.text
|
|
253
|
+
bot_user_id = context.bot_user_id
|
|
254
|
+
mention = f"<@{bot_user_id}>"
|
|
255
|
+
return mention in message
|