Skip to content

Commit 6037f37

Browse files
authored
Feat(Group chat channels) (#120)
2 parents f945ae1 + 1ee282b commit 6037f37

File tree

14 files changed

+632
-4
lines changed

14 files changed

+632
-4
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,13 @@ To discover how to create profiles for large-scale users, as well as how to visu
249249

250250
### Latest Updates
251251

252-
📢 Support Interview Action for asking agents specific questions and getting answers. - 📆 June 2, 2025
252+
📢 Add features for creating group chats, sending messages in group chats, and leaving group chats. - 📆 June 6, 2025
253253

254+
- Support Interview Action for asking agents specific questions and getting answers. - 📆 June 2, 2025
254255
- Support customization of each agent's models, tools, and prompts; refactor the interface to follow the PettingZoo style. - 📆 May 22, 2025
255256
- Refactor into the OASIS environment, publish camel-oasis on PyPI, and release the documentation. - 📆 April 24, 2025
256257
- Support OPENAI Embedding model for Twhin-Bert Recommendation System. - 📆 March 25, 2025
257-
- Updated social media links and QR codes in the README! Join OASIS & CAMEL on WeChat, X, Reddit, and Discord. - 📆 March 24, 2025
258-
- Add multi-threading support to speed up LLM inference by 13x - 📆 March 4, 2025
258+
...
259259
- Slightly refactoring the database to add Quote Action and modify Repost Action - 📆 January 13, 2025
260260
- Added the demo video and oasis's star history in the README - 📆 January 5, 2025
261261
- Introduced an Electronic Mall on the Reddit platform - 📆 December 5, 2024

docs/key_modules/actions.mdx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ OASIS provides a comprehensive set of actions that simulate real social media be
108108
| `DO_NOTHING` | Perform no action (pass the turn) |
109109
| `PURCHASE_PRODUCT` | Purchase a product (for e-commerce simulations) |
110110
| `INTERVIEW` | Interview a user and record the interview result in the database |
111+
| `CREATE_GROUP` | Create a new group with a given name |
112+
| `JOIN_GROUP` | Join a group by group ID |
113+
| `LEAVE_GROUP` | Leave a group by group ID |
114+
| `SEND_TO_GROUP` | Send a message to a group |
115+
| `LISTEN_FROM_GROUP` | Listen for messages from groups |
111116

112117
### Platform-Specific Actions
113118

@@ -302,7 +307,7 @@ action = ManualAction(
302307
)
303308
```
304309

305-
### REFRESH
310+
#### REFRESH
306311
```python
307312
action = ManualAction(
308313
action=ActionType.REFRESH,
@@ -333,3 +338,43 @@ action = ManualAction(
333338
args={"prompt": "What is your name?"}
334339
)
335340
```
341+
342+
#### CREATE_GROUP
343+
```python
344+
action = ManualAction(
345+
action=ActionType.CREATE_GROUP,
346+
args={"group_name": "OASIS Fans"}
347+
)
348+
```
349+
350+
#### JOIN_GROUP
351+
```python
352+
action = ManualAction(
353+
action=ActionType.JOIN_GROUP,
354+
args={"group_id": 1}
355+
)
356+
```
357+
358+
#### LEAVE_GROUP
359+
```python
360+
action = ManualAction(
361+
action=ActionType.LEAVE_GROUP,
362+
args={"group_id": 1}
363+
)
364+
```
365+
366+
#### SEND_TO_GROUP
367+
```python
368+
action = ManualAction(
369+
action=ActionType.SEND_TO_GROUP,
370+
args={"group_id": 1, "message": "Hello, OASIS fans!"}
371+
)
372+
```
373+
374+
#### LISTEN_FROM_GROUP
375+
```python
376+
action = ManualAction(
377+
action=ActionType.LISTEN_FROM_GROUP,
378+
args={}
379+
)
380+
```

examples/group_chat_simulation.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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 asyncio
15+
import os
16+
17+
from camel.models import ModelFactory
18+
from camel.types import ModelPlatformType
19+
20+
import oasis
21+
from oasis import (ActionType, LLMAction, ManualAction,
22+
generate_twitter_agent_graph)
23+
24+
25+
async def main():
26+
openai_model = ModelFactory.create(
27+
model_platform=ModelPlatformType.DEEPSEEK,
28+
model_type="deepseek-chat",
29+
url="https://api.deepseek.com/v1",
30+
)
31+
32+
# Define the available actions for the agents
33+
available_actions = [
34+
ActionType.JOIN_GROUP,
35+
ActionType.LEAVE_GROUP,
36+
ActionType.LISTEN_FROM_GROUP,
37+
ActionType.SEND_TO_GROUP,
38+
ActionType.LIKE_POST,
39+
ActionType.UNLIKE_POST,
40+
ActionType.REPOST,
41+
ActionType.QUOTE_POST,
42+
]
43+
44+
agent_graph = await generate_twitter_agent_graph(
45+
profile_path=(
46+
"data/twitter_dataset/anonymous_topic_200_1h/False_Business_0.csv"
47+
),
48+
model=openai_model,
49+
available_actions=available_actions,
50+
)
51+
52+
# Define the path to the database
53+
db_path = "./data/twitter_simulation.db"
54+
55+
# Delete the old database
56+
if os.path.exists(db_path):
57+
os.remove(db_path)
58+
59+
# Make the environment
60+
env = oasis.make(
61+
agent_graph=agent_graph,
62+
platform=oasis.DefaultPlatformType.TWITTER,
63+
database_path=db_path,
64+
)
65+
66+
# Run the environment
67+
await env.reset()
68+
69+
group_result = await env.platform.create_group(1, "AI Group")
70+
group_id = group_result["group_id"]
71+
72+
actions_1 = {}
73+
74+
actions_1[env.agent_graph.get_agent(0)] = ManualAction(
75+
action_type=ActionType.JOIN_GROUP, action_args={"group_id": group_id})
76+
await env.step(actions_1)
77+
78+
actions_2 = {
79+
agent: LLMAction()
80+
# Activate 5 agents with id 1, 3, 5, 7, 9
81+
for _, agent in env.agent_graph.get_agents([1, 3, 5, 7, 9])
82+
}
83+
84+
await env.step(actions_2)
85+
86+
actions_3 = {}
87+
88+
actions_3[env.agent_graph.get_agent(1)] = ManualAction(
89+
action_type=ActionType.SEND_TO_GROUP,
90+
action_args={
91+
"group_id": group_id,
92+
"message": "DeepSeek is amazing!"
93+
},
94+
)
95+
await env.step(actions_3)
96+
97+
actions_4 = {
98+
agent: LLMAction()
99+
for _, agent in env.agent_graph.get_agents()
100+
}
101+
await env.step(actions_4)
102+
103+
# Close the environment
104+
await env.close()
105+
106+
107+
if __name__ == "__main__":
108+
asyncio.run(main())

oasis/social_agent/agent_action.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ def get_openai_function_list(self) -> list[FunctionTool]:
5151
self.unmute,
5252
self.purchase_product,
5353
self.interview,
54+
self.join_group,
55+
self.leave_group,
56+
self.send_to_group,
57+
self.create_group,
5458
]
5559
]
5660

@@ -665,3 +669,60 @@ async def interview(self, prompt: str):
665669
}
666670
"""
667671
return await self.perform_action(prompt, ActionType.INTERVIEW.value)
672+
673+
async def create_group(self, group_name: str):
674+
r"""Creates a new group on the platform.
675+
676+
Args:
677+
group_name (str): The name of the group to be created.
678+
679+
Returns:
680+
dict: Platform response indicating success or failure,
681+
e.g.{"success": True, "group_id": 1}
682+
"""
683+
return await self.perform_action(group_name,
684+
ActionType.CREATE_GROUP.value)
685+
686+
async def join_group(self, group_id: int):
687+
r"""Joins a group with the specified ID.
688+
689+
Args:
690+
group_id (int): The ID of the group to join.
691+
692+
Returns:
693+
dict: Platform response indicating success or failure,
694+
e.g. {"success": True}
695+
"""
696+
return await self.perform_action(group_id, ActionType.JOIN_GROUP.value)
697+
698+
async def leave_group(self, group_id: int):
699+
r"""Leaves a group with the specified ID.
700+
701+
Args:
702+
group_id (int): The ID of the group to leave.
703+
704+
Returns:
705+
dict: Platform response indicating success or failure, e.g.
706+
{"success": True}
707+
"""
708+
return await self.perform_action(group_id,
709+
ActionType.LEAVE_GROUP.value)
710+
711+
async def send_to_group(self, group_id: int, message: str):
712+
r"""Sends a message to a specific group.
713+
714+
Args:
715+
group_id (int): The ID of the target group.
716+
message (str): The content of the message to send.
717+
718+
Returns:
719+
dict: Platform response indicating success or failure, e.g.
720+
{"success": True, "message_id": 123}
721+
"""
722+
return await self.perform_action((group_id, message),
723+
ActionType.SEND_TO_GROUP.value)
724+
725+
async def listen_from_group(self):
726+
r"""Listen messages from groups"""
727+
return await self.perform_action(self.agent_id,
728+
ActionType.LISTEN_FROM_GROUP.value)

oasis/social_agent/agent_environment.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,18 @@ class SocialEnvironment(Environment):
3434

3535
posts_env_template = Template(
3636
"After refreshing, you see some posts $posts")
37+
38+
groups_env_template = Template(
39+
"And there are many group chat channels $all_groups\n"
40+
"And You are already in some groups $joined_groups\n"
41+
"You receive some messages from them $messages\n"
42+
"You can join the groups you are interested, "
43+
"leave the groups you already in, send messages to the group "
44+
"you already in.\n"
45+
"You must make sure you can only send messages to the group you "
46+
"are already in")
3747
env_template = Template(
48+
"$groups_env\n"
3849
"$posts_env\npick one you want to perform action that best "
3950
"reflects your current inclination based on your profile and "
4051
"posts content. Do not limit your action in just `like` to like posts")
@@ -60,6 +71,21 @@ async def get_follows_env(self) -> str:
6071
# TODO: Implement follows env
6172
return self.follows_env_template.substitute(num_follows=0)
6273

74+
async def get_group_env(self) -> str:
75+
groups = await self.action.listen_from_group()
76+
if groups["success"]:
77+
all_groups = json.dumps(groups["all_groups"])
78+
joined_groups = json.dumps(groups["joined_groups"])
79+
messages = json.dumps(groups["messages"])
80+
groups_env = self.groups_env_template.substitute(
81+
all_groups=all_groups,
82+
joined_groups=joined_groups,
83+
messages=messages,
84+
)
85+
else:
86+
groups_env = "No groups."
87+
return groups_env
88+
6389
async def to_text_prompt(
6490
self,
6591
include_posts: bool = True,
@@ -76,4 +102,5 @@ async def to_text_prompt(
76102
followers_env=followers_env,
77103
follows_env=follows_env,
78104
posts_env=posts_env,
105+
groups_env=await self.get_group_env(),
79106
)

oasis/social_platform/database.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
COMMENT_LIKE_SCHEMA_SQL = "comment_like.sql"
3535
COMMENT_DISLIKE_SCHEMA_SQL = "comment_dislike.sql"
3636
PRODUCT_SCHEMA_SQL = "product.sql"
37+
GROUP_SCHEMA_SQL = "chat_group.sql"
38+
GROUP_MEMBER_SCHEMA_SQL = "group_member.sql"
39+
GROUP_MESSAGE_SCHEMA_SQL = "group_message.sql"
3740

3841
TABLE_NAMES = {
3942
"user",
@@ -48,6 +51,9 @@
4851
"comment_like.sql",
4952
"comment_dislike.sql",
5053
"product.sql",
54+
"group",
55+
"group_member",
56+
"group_message",
5157
}
5258

5359

@@ -154,6 +160,24 @@ def create_db(db_path: str | None = None):
154160
product_sql_script = sql_file.read()
155161
cursor.executescript(product_sql_script)
156162

163+
# Read and execute the group table SQL script:
164+
group_sql_path = osp.join(schema_dir, GROUP_SCHEMA_SQL)
165+
with open(group_sql_path, "r") as sql_file:
166+
group_sql_script = sql_file.read()
167+
cursor.executescript(group_sql_script)
168+
169+
# Read and execute the group_member table SQL script:
170+
group_member_sql_path = osp.join(schema_dir, GROUP_MEMBER_SCHEMA_SQL)
171+
with open(group_member_sql_path, "r") as sql_file:
172+
group_member_sql_script = sql_file.read()
173+
cursor.executescript(group_member_sql_script)
174+
175+
# Read and execute the group_message table SQL script:
176+
group_message_sql_path = osp.join(schema_dir, GROUP_MESSAGE_SCHEMA_SQL)
177+
with open(group_message_sql_path, "r") as sql_file:
178+
group_message_sql_script = sql_file.read()
179+
cursor.executescript(group_message_sql_script)
180+
157181
# Commit the changes:
158182
conn.commit()
159183

0 commit comments

Comments
 (0)