pycityagent 2.0.0a33__py3-none-any.whl → 2.0.0a35__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.
- pycityagent/agent.py +1 -0
- pycityagent/message/messager.py +22 -8
- pycityagent/simulation/agentgroup.py +12 -8
- pycityagent/simulation/simulation.py +1 -8
- pycityagent/workflow/block.py +31 -3
- {pycityagent-2.0.0a33.dist-info → pycityagent-2.0.0a35.dist-info}/METADATA +1 -1
- {pycityagent-2.0.0a33.dist-info → pycityagent-2.0.0a35.dist-info}/RECORD +8 -8
- {pycityagent-2.0.0a33.dist-info → pycityagent-2.0.0a35.dist-info}/WHEEL +0 -0
pycityagent/agent.py
CHANGED
@@ -687,6 +687,7 @@ class InstitutionAgent(Agent):
|
|
687
687
|
# TODO: More general id generation
|
688
688
|
_id = random.randint(100000, 999999)
|
689
689
|
self._agent_id = _id
|
690
|
+
self.memory.set_agent_id(_id)
|
690
691
|
await self.memory.update("id", _id, protect_llm_read_only_fields=False)
|
691
692
|
try:
|
692
693
|
await self._economy_client.remove_orgs([self._agent_id])
|
pycityagent/message/messager.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import asyncio
|
2
|
-
from collections import defaultdict
|
3
2
|
import json
|
4
3
|
import logging
|
5
4
|
import math
|
5
|
+
from typing import Any, List, Union
|
6
6
|
from aiomqtt import Client
|
7
7
|
|
8
8
|
logger = logging.getLogger("pycityagent")
|
@@ -17,6 +17,10 @@ class Messager:
|
|
17
17
|
self.connected = False # 是否已连接标志
|
18
18
|
self.message_queue = asyncio.Queue() # 用于存储接收到的消息
|
19
19
|
self.subscribers = {} # 订阅者信息,topic -> Agent 映射
|
20
|
+
self.receive_messages_task = None
|
21
|
+
|
22
|
+
async def __aexit__(self, exc_type, exc_value, traceback):
|
23
|
+
await self.stop()
|
20
24
|
|
21
25
|
async def connect(self):
|
22
26
|
try:
|
@@ -36,15 +40,20 @@ class Messager:
|
|
36
40
|
"""检查是否成功连接到 Broker"""
|
37
41
|
return self.connected
|
38
42
|
|
39
|
-
async def subscribe(self,
|
43
|
+
async def subscribe(self, topics: Union[str, List[str]], agents: Union[Any, List[Any]]):
|
40
44
|
if not self.is_connected():
|
41
45
|
logger.error(
|
42
|
-
f"Cannot subscribe to {
|
46
|
+
f"Cannot subscribe to {topics} because not connected to the Broker."
|
43
47
|
)
|
44
48
|
return
|
45
|
-
|
46
|
-
|
47
|
-
|
49
|
+
if not isinstance(topics, list):
|
50
|
+
topics = [topics]
|
51
|
+
if not isinstance(agents, list):
|
52
|
+
agents = [agents]
|
53
|
+
for topic, agent in zip(topics, agents):
|
54
|
+
self.subscribers[topic] = agent
|
55
|
+
logger.info(f"Subscribed to {topic} for Agent {agent._uuid}")
|
56
|
+
await self.client.subscribe(topics, qos=1)
|
48
57
|
|
49
58
|
async def receive_messages(self):
|
50
59
|
"""监听并将消息存入队列"""
|
@@ -61,12 +70,17 @@ class Messager:
|
|
61
70
|
async def send_message(self, topic: str, payload: dict):
|
62
71
|
"""通过 Messager 发送消息"""
|
63
72
|
message = json.dumps(payload, default=str)
|
64
|
-
await self.client.publish(topic, message)
|
73
|
+
await self.client.publish(topic=topic, payload=message, qos=1)
|
65
74
|
logger.info(f"Message sent to {topic}: {message}")
|
66
75
|
|
67
76
|
async def start_listening(self):
|
68
77
|
"""启动消息监听任务"""
|
69
78
|
if self.is_connected():
|
70
|
-
asyncio.create_task(self.receive_messages())
|
79
|
+
self.receive_messages_task = asyncio.create_task(self.receive_messages())
|
71
80
|
else:
|
72
81
|
logger.error("Cannot start listening because not connected to the Broker.")
|
82
|
+
|
83
|
+
async def stop(self):
|
84
|
+
self.receive_messages_task.cancel()
|
85
|
+
await asyncio.gather(self.receive_messages_task, return_exceptions=True)
|
86
|
+
await self.disconnect()
|
@@ -69,6 +69,7 @@ class AgentGroup:
|
|
69
69
|
username=config["simulator_request"]["mqtt"].get("username", None),
|
70
70
|
password=config["simulator_request"]["mqtt"].get("password", None),
|
71
71
|
)
|
72
|
+
self.message_dispatch_task = None
|
72
73
|
self._pgsql_writer = pgsql_writer
|
73
74
|
self._last_asyncio_pg_task = None # 将SQL写入的IO隐藏到计算任务后
|
74
75
|
self.initialized = False
|
@@ -132,6 +133,10 @@ class AgentGroup:
|
|
132
133
|
if self.embedding_model is not None:
|
133
134
|
agent.memory.set_embedding_model(self.embedding_model)
|
134
135
|
|
136
|
+
async def __aexit__(self, exc_type, exc_value, traceback):
|
137
|
+
self.message_dispatch_task.cancel()
|
138
|
+
await asyncio.gather(self.message_dispatch_task, return_exceptions=True)
|
139
|
+
|
135
140
|
async def init_agents(self):
|
136
141
|
logger.debug(f"-----Initializing Agents in AgentGroup {self._uuid} ...")
|
137
142
|
logger.debug(f"-----Binding Agents to Simulator in AgentGroup {self._uuid} ...")
|
@@ -142,16 +147,14 @@ class AgentGroup:
|
|
142
147
|
await self.messager.connect()
|
143
148
|
if self.messager.is_connected():
|
144
149
|
await self.messager.start_listening()
|
150
|
+
topics = []
|
151
|
+
agents = []
|
145
152
|
for agent in self.agents:
|
146
153
|
agent.set_messager(self.messager)
|
147
|
-
topic = f"exps/{self.exp_id}/agents/{agent._uuid}
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
topic = f"exps/{self.exp_id}/agents/{agent._uuid}/user-survey"
|
152
|
-
await self.messager.subscribe(topic, agent)
|
153
|
-
topic = f"exps/{self.exp_id}/agents/{agent._uuid}/gather"
|
154
|
-
await self.messager.subscribe(topic, agent)
|
154
|
+
topic = (f"exps/{self.exp_id}/agents/{agent._uuid}/#", 1)
|
155
|
+
topics.append(topic)
|
156
|
+
agents.append(agent)
|
157
|
+
await self.messager.subscribe(topics, agents)
|
155
158
|
self.message_dispatch_task = asyncio.create_task(self.message_dispatch())
|
156
159
|
if self.enable_avro:
|
157
160
|
logger.debug(f"-----Creating Avro files in AgentGroup {self._uuid} ...")
|
@@ -225,6 +228,7 @@ class AgentGroup:
|
|
225
228
|
logger.warning(
|
226
229
|
"Messager is not connected. Skipping message processing."
|
227
230
|
)
|
231
|
+
break
|
228
232
|
|
229
233
|
# Step 1: 获取消息
|
230
234
|
messages = await self.messager.fetch_messages()
|
@@ -74,7 +74,6 @@ class AgentSimulation:
|
|
74
74
|
username=config["simulator_request"]["mqtt"].get("username", None),
|
75
75
|
password=config["simulator_request"]["mqtt"].get("password", None),
|
76
76
|
)
|
77
|
-
asyncio.create_task(self._messager.connect())
|
78
77
|
|
79
78
|
# storage
|
80
79
|
_storage_config: dict[str, Any] = config.get("storage", {})
|
@@ -202,6 +201,7 @@ class AgentSimulation:
|
|
202
201
|
group_size: 每个组的智能体数量,每一个组为一个独立的ray actor
|
203
202
|
memory_config_func: 返回Memory配置的函数,需要返回(EXTRA_ATTRIBUTES, PROFILE, BASE)元组, 如果为列表,则每个元素表示一个智能体类创建的Memory配置函数
|
204
203
|
"""
|
204
|
+
await self._messager.connect()
|
205
205
|
if not isinstance(agent_count, list):
|
206
206
|
agent_count = [agent_count]
|
207
207
|
|
@@ -535,13 +535,6 @@ class AgentSimulation:
|
|
535
535
|
try:
|
536
536
|
if self.enable_pgsql:
|
537
537
|
worker: ray.ObjectRef = self._pgsql_writers[0] # type:ignore
|
538
|
-
# if self._last_asyncio_pg_task is not None:
|
539
|
-
# await self._last_asyncio_pg_task
|
540
|
-
# self._last_asyncio_pg_task = (
|
541
|
-
# worker.async_update_exp_info.remote( # type:ignore
|
542
|
-
# pg_exp_info
|
543
|
-
# )
|
544
|
-
# )
|
545
538
|
pg_exp_info = {k: v for k, v in self._exp_info.items()}
|
546
539
|
pg_exp_info["created_at"] = self._exp_created_time
|
547
540
|
pg_exp_info["updated_at"] = self._exp_updated_time
|
pycityagent/workflow/block.py
CHANGED
@@ -168,16 +168,44 @@ class Block:
|
|
168
168
|
def llm(
|
169
169
|
self,
|
170
170
|
) -> LLM:
|
171
|
-
|
171
|
+
if self._llm is None:
|
172
|
+
raise RuntimeError(f"LLM access before assignment, please `set_llm` first!")
|
173
|
+
return self._llm
|
172
174
|
|
173
175
|
@property
|
174
176
|
def memory(
|
175
177
|
self,
|
176
178
|
) -> Memory:
|
177
|
-
|
179
|
+
if self._memory is None:
|
180
|
+
raise RuntimeError(
|
181
|
+
f"Memory access before assignment, please `set_memory` first!"
|
182
|
+
)
|
183
|
+
return self._memory
|
178
184
|
|
179
185
|
@property
|
180
186
|
def simulator(
|
181
187
|
self,
|
182
188
|
) -> Simulator:
|
183
|
-
|
189
|
+
if self._simulator is None:
|
190
|
+
raise RuntimeError(
|
191
|
+
f"Simulator access before assignment, please `set_simulator` first!"
|
192
|
+
)
|
193
|
+
return self._simulator
|
194
|
+
|
195
|
+
def set_llm_client(self, llm: LLM):
|
196
|
+
"""
|
197
|
+
Set the llm_client of the block.
|
198
|
+
"""
|
199
|
+
self._llm = llm
|
200
|
+
|
201
|
+
def set_simulator(self, simulator: Simulator):
|
202
|
+
"""
|
203
|
+
Set the simulator of the block.
|
204
|
+
"""
|
205
|
+
self._simulator = simulator
|
206
|
+
|
207
|
+
def set_memory(self, memory: Memory):
|
208
|
+
"""
|
209
|
+
Set the memory of the block.
|
210
|
+
"""
|
211
|
+
self._memory = memory
|
@@ -1,5 +1,5 @@
|
|
1
1
|
pycityagent/__init__.py,sha256=fv0mzNGbHBF6m550yYqnuUpB8iQPWS-7EatYRK7DO4s,693
|
2
|
-
pycityagent/agent.py,sha256=
|
2
|
+
pycityagent/agent.py,sha256=r1uG4ib6fAmqzZXqGUiwswYdIOS8ybTFdbVqJWjhwuI,29047
|
3
3
|
pycityagent/economy/__init__.py,sha256=aonY4WHnx-6EGJ4WKrx4S-2jAkYNLtqUA04jp6q8B7w,75
|
4
4
|
pycityagent/economy/econ_client.py,sha256=GuHK9ZBnhqW3Z7F8ViDJn_iN73yOBbbwFyJv1wLEBDk,12211
|
5
5
|
pycityagent/environment/__init__.py,sha256=awHxlOud-btWbk0FCS4RmGJ13W84oVCkbGfcrhKqihA,240
|
@@ -45,13 +45,13 @@ pycityagent/memory/self_define.py,sha256=vpZ6CIxR2grNXEIOScdpsSc59FBg0mOKelwQuTE
|
|
45
45
|
pycityagent/memory/state.py,sha256=TYItiyDtehMEQaSBN7PpNrnNxdDM5jGppr9R9Ufv3kA,5134
|
46
46
|
pycityagent/memory/utils.py,sha256=oJWLdPeJy_jcdKcDTo9JAH9kDZhqjoQhhv_zT9qWC0w,877
|
47
47
|
pycityagent/message/__init__.py,sha256=TCjazxqb5DVwbTu1fF0sNvaH_EPXVuj2XQ0p6W-QCLU,55
|
48
|
-
pycityagent/message/messager.py,sha256=
|
48
|
+
pycityagent/message/messager.py,sha256=gz-EZOGakgwQH8ZKabAGr1pT43E8B9z-s4dbNx-mxr4,3167
|
49
49
|
pycityagent/metrics/__init__.py,sha256=X08PaBbGVAd7_PRGLREXWxaqm7nS82WBQpD1zvQzcqc,128
|
50
50
|
pycityagent/metrics/mlflow_client.py,sha256=g_tHxWkWTDijtbGL74-HmiYzWVKb1y8-w12QrY9jL30,4449
|
51
51
|
pycityagent/metrics/utils/const.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
52
|
pycityagent/simulation/__init__.py,sha256=P5czbcg2d8S0nbbnsQXFIhwzO4CennAhZM8OmKvAeYw,194
|
53
|
-
pycityagent/simulation/agentgroup.py,sha256=
|
54
|
-
pycityagent/simulation/simulation.py,sha256=
|
53
|
+
pycityagent/simulation/agentgroup.py,sha256=mZXznSf7VEHNjU0KM5TuPB-Z09DqMZ6VeQib7ciTLpo,23914
|
54
|
+
pycityagent/simulation/simulation.py,sha256=OVflF_Z_kZfE6c4z_6tHTGD53jq89Hewg8Yc5KflRI0,23008
|
55
55
|
pycityagent/simulation/storage/pg.py,sha256=qGrYzJIAzjv8-d3-cle0rY0AN6XB6MgnHkFLBoLmKWU,7251
|
56
56
|
pycityagent/survey/__init__.py,sha256=rxwou8U9KeFSP7rMzXtmtp2fVFZxK4Trzi-psx9LPIs,153
|
57
57
|
pycityagent/survey/manager.py,sha256=S5IkwTdelsdtZETChRcfCEczzwSrry_Fly9MY4s3rbk,1681
|
@@ -66,10 +66,10 @@ pycityagent/utils/parsers/parser_base.py,sha256=KBKO4zLZPNdGjPAGqIus8LseZ8W3Tlt2
|
|
66
66
|
pycityagent/utils/pg_query.py,sha256=h5158xcrxjUTR0nKwAaG1neFfTHPbN5guLmaXpC8yvs,1918
|
67
67
|
pycityagent/utils/survey_util.py,sha256=Be9nptmu2JtesFNemPgORh_2GsN7rcDYGQS9Zfvc5OI,2169
|
68
68
|
pycityagent/workflow/__init__.py,sha256=QNkUV-9mACMrR8c0cSKna2gC1mMZdxXbxWzjE-Uods0,621
|
69
|
-
pycityagent/workflow/block.py,sha256=
|
69
|
+
pycityagent/workflow/block.py,sha256=C2aWdVRffb3LknP955GvPcBMsm3VPXN9ZuAtCgITFTo,7181
|
70
70
|
pycityagent/workflow/prompt.py,sha256=6jI0Rq54JLv3-IXqZLYug62vse10wTI83xvf4ZX42nk,2929
|
71
71
|
pycityagent/workflow/tool.py,sha256=xADxhNgVsjNiMxlhdwn3xGUstFOkLEG8P67ez8VmwSI,8555
|
72
72
|
pycityagent/workflow/trigger.py,sha256=Df-MOBEDWBbM-v0dFLQLXteLsipymT4n8vqexmK2GiQ,5643
|
73
|
-
pycityagent-2.0.
|
74
|
-
pycityagent-2.0.
|
75
|
-
pycityagent-2.0.
|
73
|
+
pycityagent-2.0.0a35.dist-info/METADATA,sha256=8uify9sg-cHlR4Vuw_N7ySp46ZoTmV1czHaD51Ng-7k,8033
|
74
|
+
pycityagent-2.0.0a35.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
75
|
+
pycityagent-2.0.0a35.dist-info/RECORD,,
|
File without changes
|