pycityagent 2.0.0a73__cp39-cp39-macosx_11_0_arm64.whl → 2.0.0a75__cp39-cp39-macosx_11_0_arm64.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.
@@ -8,6 +8,7 @@ from typing import Any, Optional
8
8
 
9
9
  from mosstool.util.format_converter import dict2pb
10
10
  from pycityproto.city.person.v2 import person_pb2 as person_pb2
11
+ import ray
11
12
 
12
13
  from ..economy import EconomyClient
13
14
  from ..environment import Simulator
@@ -120,7 +121,7 @@ class CitizenAgent(Agent):
120
121
  dict_person[_key] = _value
121
122
  except KeyError as e:
122
123
  continue
123
- resp = await simulator.add_person(dict2pb(dict_person, person_pb2.Person()))
124
+ resp = await simulator.add_person(dict_person)
124
125
  person_id = resp["person_id"]
125
126
  await status.update("id", person_id, protect_llm_read_only_fields=False)
126
127
  logger.debug(f"Binding to Person `{person_id}` just added to Simulator")
@@ -280,7 +281,7 @@ class InstitutionAgent(Agent):
280
281
  _id = random.randint(100000, 999999)
281
282
  self._agent_id = _id
282
283
  self.status.set_agent_id(_id)
283
- map_header = self.simulator.map.header
284
+ map_header = ray.get(self.simulator.map.get_map_header.remote())
284
285
  # TODO: remove random position assignment
285
286
  await self.status.update(
286
287
  "position",
@@ -19,7 +19,7 @@ from ..environment import Simulator
19
19
  from ..environment.sim.person_service import PersonService
20
20
  from ..llm import LLM
21
21
  from ..memory import Memory
22
- from ..message import MessageInterceptor, Messager
22
+ from ..message import Messager
23
23
  from ..utils import DIALOG_SCHEMA, SURVEY_SCHEMA, process_survey_for_llm
24
24
  from ..workflow import Block
25
25
 
@@ -673,6 +673,47 @@ class Agent(ABC):
673
673
  )
674
674
  print(f"Sent payback message to {self._exp_id}")
675
675
 
676
+ async def save_agent_thought(self, thought: str):
677
+ """
678
+ Save the agent's thought to the memory.
679
+
680
+ - **Args**:
681
+ - `thought` (`str`): The thought data to be saved.
682
+
683
+ - **Description**:
684
+ - Saves the thought data to the memory.
685
+ """
686
+ _date_time = datetime.now(timezone.utc)
687
+ _thought_dict = {
688
+ "id": self._uuid,
689
+ "day": await self.simulator.get_simulator_day(),
690
+ "t": await self.simulator.get_simulator_second_from_start_of_day(),
691
+ "type": 0,
692
+ "speaker": "",
693
+ "content": thought,
694
+ "created_at": int(_date_time.timestamp() * 1000),
695
+ }
696
+ auros = [_thought_dict]
697
+ pg_list = [(_thought_dict, _date_time)]
698
+ # Avro
699
+ if self._avro_file is not None:
700
+ with open(self._avro_file["dialog"], "a+b") as f:
701
+ fastavro.writer(f, DIALOG_SCHEMA, auros, codec="snappy")
702
+ # Pg
703
+ if self._pgsql_writer is not None:
704
+ if self._last_asyncio_pg_task is not None:
705
+ await self._last_asyncio_pg_task
706
+ _keys = ["id", "day", "t", "type", "speaker", "content", "created_at"]
707
+ _data = [
708
+ tuple([_dict[k] if k != "created_at" else _date_time for k in _keys])
709
+ for _dict, _date_time in pg_list
710
+ ]
711
+ self._last_asyncio_pg_task = (
712
+ self._pgsql_writer.async_write_dialog.remote( # type:ignore
713
+ _data
714
+ )
715
+ )
716
+
676
717
  async def process_agent_chat_response(self, payload: dict) -> str:
677
718
  """
678
719
  Log the reception of an agent chat response.
@@ -931,5 +972,4 @@ class Agent(ABC):
931
972
  if self._messager is not None:
932
973
  await self._messager.ping.remote() # type:ignore
933
974
  if not self._blocked:
934
- async with self.llm.semaphore:
935
- await self.forward()
975
+ return await self.forward()
@@ -199,7 +199,8 @@ class CognitionBlock(Block):
199
199
  thought = str(response["thought"])
200
200
  await self.memory.status.update("thought", thought)
201
201
  await self.memory.stream.add_cognition(description=thought)
202
- return
202
+
203
+ return thought
203
204
 
204
205
  async def end_of_day(self):
205
206
  """Cognition - end of day workflow"""
@@ -36,7 +36,7 @@ class WorkBlock(Block):
36
36
  """WorkPlace Block"""
37
37
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
38
38
  super().__init__("WorkBlock", llm=llm, memory=memory, simulator=simulator)
39
- self.description = "Do work"
39
+ self.description = "Do work related tasks"
40
40
  self.guidance_prompt = FormatPrompt(template=TIME_ESTIMATE_PROMPT)
41
41
  async def forward(self, step, context):
42
42
  self.guidance_prompt.format(
@@ -157,7 +157,7 @@ class EconomyBlock(Block):
157
157
 
158
158
  async def forward(self, step, context):
159
159
  self.trigger_time += 1
160
- selected_block = self.consumption_block
160
+ selected_block = await self.dispatcher.dispatch(step)
161
161
  result = await selected_block.forward(step, context) # type: ignore
162
162
  return result
163
163
 
@@ -168,7 +168,7 @@ class MonthPlanBlock(Block):
168
168
  self.economy_client = economy_client
169
169
  self.llm_error = 0
170
170
  self.last_time_trigger = None
171
- self.time_diff = month_days * 24 * 60 * 60
171
+ self.time_diff = 30 * 24 * 60 * 60
172
172
  self.forward_times = 0
173
173
 
174
174
  async def month_trigger(self):
@@ -1,6 +1,8 @@
1
1
  import math
2
2
  from typing import List
3
3
 
4
+ import ray
5
+
4
6
  from pycityagent.environment.simulator import Simulator
5
7
  from pycityagent.llm import LLM
6
8
  from pycityagent.memory import Memory
@@ -50,7 +52,6 @@ Return only the integer number without any additional text or explanation."""
50
52
 
51
53
 
52
54
  def gravity_model(pois):
53
- N = len(pois)
54
55
  pois_Dis = {
55
56
  "1k": [],
56
57
  "2k": [],
@@ -133,21 +134,22 @@ class PlaceSelectionBlock(Block):
133
134
  )
134
135
  self.radiusPrompt = FormatPrompt(RADIUS_PROMPT)
135
136
  # configurable fields
136
- self.search_limit = 10000
137
+ self.search_limit = 100
137
138
 
138
139
  async def forward(self, step, context):
140
+ poi_cate = self.simulator.get_poi_cate()
139
141
  self.typeSelectionPrompt.format(
140
142
  plan=context["plan"],
141
143
  intention=step["intention"],
142
- poi_category=list(self.simulator.poi_cate.keys()),
144
+ poi_category=list(poi_cate.keys()),
143
145
  )
144
146
  levelOneType = await self.llm.atext_request(self.typeSelectionPrompt.to_dialog()) # type: ignore
145
147
  try:
146
- sub_category = self.simulator.poi_cate[levelOneType]
148
+ sub_category = poi_cate[levelOneType]
147
149
  except Exception as e:
148
150
  logger.warning(f"Wrong type of poi, raw response: {levelOneType}")
149
- levelOneType = random.choice(list(self.simulator.poi_cate.keys()))
150
- sub_category = self.simulator.poi_cate[levelOneType]
151
+ levelOneType = random.choice(list(poi_cate.keys()))
152
+ sub_category = poi_cate[levelOneType]
151
153
  self.secondTypeSelectionPrompt.format(
152
154
  plan=context["plan"], intention=step["intention"], poi_category=sub_category
153
155
  )
@@ -162,21 +164,21 @@ class PlaceSelectionBlock(Block):
162
164
  )
163
165
  radius = int(await self.llm.atext_request(self.radiusPrompt.to_dialog())) # type: ignore
164
166
  try:
165
- pois = self.simulator.map.query_pois(
167
+ pois = ray.get(self.simulator.map.query_pois.remote(
166
168
  center=center,
167
169
  category_prefix=levelTwoType,
168
170
  radius=radius,
169
171
  limit=self.search_limit,
170
- )
172
+ ))
171
173
  except Exception as e:
172
174
  logger.warning(f"Error querying pois: {e}")
173
175
  levelTwoType = random.choice(sub_category)
174
- pois = self.simulator.map.query_pois(
176
+ pois = ray.get(self.simulator.map.query_pois.remote(
175
177
  center=center,
176
178
  category_prefix=levelTwoType,
177
179
  radius=radius,
178
180
  limit=self.search_limit,
179
- )
181
+ ))
180
182
  if len(pois) > 0:
181
183
  pois = gravity_model(pois)
182
184
  probabilities = [item[2] for item in pois]
@@ -198,8 +200,8 @@ class PlaceSelectionBlock(Block):
198
200
  "node_id": node_id,
199
201
  }
200
202
  else:
201
- simmap = self.simulator.map
202
- poi = random.choice(list(simmap.pois.values()))
203
+ pois = ray.get(self.simulator.map.get_poi.remote())
204
+ poi = random.choice(pois)
203
205
  nextPlace = (poi["name"], poi["aoi_id"])
204
206
  # save the destination to context
205
207
  context["next_place"] = nextPlace
@@ -309,11 +311,12 @@ class MoveBlock(Block):
309
311
  )
310
312
  else:
311
313
  while True:
312
- r_aoi = random.choice(list(self.simulator.map.aois.values()))
314
+ aois = ray.get(self.simulator.map.get_aoi.remote())
315
+ r_aoi = random.choice(aois)
313
316
  if len(r_aoi["poi_ids"]) > 0:
314
317
  r_poi = random.choice(r_aoi["poi_ids"])
315
318
  break
316
- poi = self.simulator.map.pois[r_poi]
319
+ poi = ray.get(self.simulator.map.get_poi.remote(r_poi))
317
320
  next_place = (poi["name"], poi["aoi_id"])
318
321
  await self.simulator.set_aoi_schedules(
319
322
  person_id=agent_id,
@@ -372,9 +375,6 @@ class MobilityBlock(Block):
372
375
 
373
376
  async def forward(self, step, context):
374
377
  self.trigger_time += 1
375
- consumption_start = (
376
- self.llm.prompt_tokens_used + self.llm.completion_tokens_used
377
- )
378
378
 
379
379
  # Select the appropriate sub-block using dispatcher
380
380
  selected_block = await self.dispatcher.dispatch(step)
@@ -382,7 +382,4 @@ class MobilityBlock(Block):
382
382
  # Execute the selected sub-block and get the result
383
383
  result = await selected_block.forward(step, context) # type: ignore
384
384
 
385
- consumption_end = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
386
- self.token_consumption += consumption_end - consumption_start
387
-
388
385
  return result
@@ -1,6 +1,8 @@
1
1
  import json
2
2
  import logging
3
3
 
4
+ import ray
5
+
4
6
  from pycityagent import Simulator
5
7
  from pycityagent.llm import LLM
6
8
  from pycityagent.memory import Memory
@@ -3,6 +3,8 @@ import logging
3
3
  import random
4
4
  from typing import Dict, List
5
5
 
6
+ import ray
7
+
6
8
  from pycityagent.environment.simulator import Simulator
7
9
  from pycityagent.llm import LLM
8
10
  from pycityagent.memory import Memory
@@ -196,7 +198,6 @@ class PlanBlock(Block):
196
198
  ):
197
199
  current_location = "At workplace"
198
200
  current_time = await self.simulator.get_time(format_time=True)
199
- environment = await self.memory.status.get("environment")
200
201
  options = self.guidance_options.get(current_need, [])
201
202
  self.guidance_prompt.format(
202
203
  weather=self.simulator.sence("weather"),
@@ -205,7 +206,6 @@ class PlanBlock(Block):
205
206
  options=options,
206
207
  current_location=current_location,
207
208
  current_time=current_time,
208
- environment=environment,
209
209
  emotion_types=await self.memory.status.get("emotion_types"),
210
210
  thought=await self.memory.status.get("thought"),
211
211
  )
@@ -240,14 +240,12 @@ class PlanBlock(Block):
240
240
  ):
241
241
  current_location = "At workplace"
242
242
  current_time = await self.simulator.get_time(format_time=True)
243
- environment = await self.memory.status.get("environment")
244
243
  self.detail_prompt.format(
245
244
  weather=self.simulator.sence("weather"),
246
245
  temperature=self.simulator.sence("temperature"),
247
246
  selected_option=selected_option,
248
247
  current_location=current_location,
249
248
  current_time=current_time,
250
- environment=environment,
251
249
  emotion_types=await self.memory.status.get("emotion_types"),
252
250
  thought=await self.memory.status.get("thought"),
253
251
  max_plan_steps=self.max_plan_steps,
@@ -24,7 +24,6 @@ Please return the result in JSON format (Do not return any other text):
24
24
  """
25
25
 
26
26
  num_labor_hours = 168
27
- month_days = 0.03
28
27
  productivity_per_labor = 1
29
28
  max_price_inflation = 0.1
30
29
  max_wage_inflation = 0.05
@@ -68,8 +68,6 @@ async def initialize_social_network(simulation):
68
68
  await simulation.update(
69
69
  agent_id, "interactions", {friend_id: [] for friend_id in friends}
70
70
  )
71
-
72
- print("Social network initialization completed!")
73
71
  return True
74
72
 
75
73
  except Exception as e:
@@ -28,16 +28,16 @@ def memory_config_societyagent():
28
28
  "type": (str, "citizen"),
29
29
  "city": (str, "New York", True),
30
30
  # Needs Model
31
- "hunger_satisfaction": (float, random.random(), True), # hunger satisfaction
32
- "energy_satisfaction": (float, random.random(), True), # energy satisfaction
33
- "safety_satisfaction": (float, random.random(), True), # safety satisfaction
34
- "social_satisfaction": (float, random.random(), True), # social satisfaction
35
- "current_need": (str, "none", True),
31
+ "hunger_satisfaction": (float, random.random(), False), # hunger satisfaction
32
+ "energy_satisfaction": (float, random.random(), False), # energy satisfaction
33
+ "safety_satisfaction": (float, random.random(), False), # safety satisfaction
34
+ "social_satisfaction": (float, random.random(), False), # social satisfaction
35
+ "current_need": (str, "none", False),
36
36
  # Plan Behavior Model
37
- "current_plan": (list, [], True),
38
- "current_step": (dict, {"intention": "", "type": ""}, True),
39
- "execution_context": (dict, {}, True),
40
- "plan_history": (list, [], True),
37
+ "current_plan": (list, [], False),
38
+ "current_step": (dict, {"intention": "", "type": ""}, False),
39
+ "execution_context": (dict, {}, False),
40
+ "plan_history": (list, [], False),
41
41
  # cognition
42
42
  "emotion": (
43
43
  dict,
@@ -60,40 +60,39 @@ def memory_config_societyagent():
60
60
  random.choice(agent_skills),
61
61
  True,
62
62
  ), # work skill
63
- "tax_paid": (float, 0.0, True), # tax paid
64
- "consumption_currency": (float, 0.0, True), # consumption
65
- "goods_demand": (int, 0, True),
66
- "goods_consumption": (int, 0, True),
67
- "work_propensity": (float, 0.0, True),
68
- "consumption_propensity": (float, 0.0, True),
69
- "income_currency": (float, 0.0, True), # monthly income
70
- "to_income": (float, 0.0, True),
71
- "to_consumption_currency": (float, 0.0, True),
72
- "firm_id": (int, 0, True),
73
- "government_id": (int, 0, True),
74
- "bank_id": (int, 0, True),
75
- "nbs_id": (int, 0, True),
76
- "dialog_queue": (deque(maxlen=3), [], True),
77
- "firm_forward": (int, 0, True),
78
- "bank_forward": (int, 0, True),
79
- "nbs_forward": (int, 0, True),
80
- "government_forward": (int, 0, True),
81
- "forward": (int, 0, True),
82
- "depression": (float, 0.0, True),
83
- "ubi_opinion": (list, [], True),
84
- "working_experience": (list, [], True),
85
- "work_hour_month": (float, 160, True),
86
- "work_hour_finish": (float, 0, True),
63
+ "tax_paid": (float, 0.0, False), # tax paid
64
+ "consumption_currency": (float, 0.0, False), # consumption
65
+ "goods_demand": (int, 0, False),
66
+ "goods_consumption": (int, 0, False),
67
+ "work_propensity": (float, 0.0, False),
68
+ "consumption_propensity": (float, 0.0, False),
69
+ "income_currency": (float, 0.0, False), # monthly income
70
+ "to_income": (float, 0.0, False),
71
+ "to_consumption_currency": (float, 0.0, False),
72
+ "firm_id": (int, 0, False),
73
+ "government_id": (int, 0, False),
74
+ "bank_id": (int, 0, False),
75
+ "nbs_id": (int, 0, False),
76
+ "dialog_queue": (deque(maxlen=3), [], False),
77
+ "firm_forward": (int, 0, False),
78
+ "bank_forward": (int, 0, False),
79
+ "nbs_forward": (int, 0, False),
80
+ "government_forward": (int, 0, False),
81
+ "forward": (int, 0, False),
82
+ "depression": (float, 0.0, False),
83
+ "ubi_opinion": (list, [], False),
84
+ "working_experience": (list, [], False),
85
+ "work_hour_month": (float, 160, False),
86
+ "work_hour_finish": (float, 0, False),
87
87
  # social
88
- "friends": (list, [], True), # friends list
89
- "relationships": (dict, {}, True), # relationship strength with each friend
90
- "relation_types": (dict, {}, True),
91
- "chat_histories": (dict, {}, True), # all chat histories
92
- "interactions": (dict, {}, True), # all interaction records
93
- "to_discuss": (dict, {}, True),
88
+ "friends": (list, [], False), # friends list
89
+ "relationships": (dict, {}, False), # relationship strength with each friend
90
+ "relation_types": (dict, {}, False),
91
+ "chat_histories": (dict, {}, False), # all chat histories
92
+ "interactions": (dict, {}, False), # all interaction records
93
+ "to_discuss": (dict, {}, False),
94
94
  # mobility
95
- "environment": (str, "The environment outside is good", True),
96
- "number_poi_visited": (int, 1, True),
95
+ "number_poi_visited": (int, 1, False),
97
96
  }
98
97
 
99
98
  PROFILE = {
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import json
3
3
  import logging
4
+ import time
4
5
  from typing import Optional
5
6
 
6
7
  from pycityagent import CitizenAgent, Simulator
@@ -66,8 +67,7 @@ class PlanAndActionBlock(Block):
66
67
  async def plan_generation(self):
67
68
  """Generate plan"""
68
69
  current_plan = await self.memory.status.get("current_plan")
69
- current_need = await self.memory.status.get("current_need")
70
- if current_need != "none" and not current_plan:
70
+ if current_plan is None:
71
71
  await self.planBlock.forward()
72
72
 
73
73
  async def step_execution(self):
@@ -152,7 +152,6 @@ class PlanAndActionBlock(Block):
152
152
  # step execution
153
153
  await self.step_execution()
154
154
 
155
-
156
155
  class MindBlock(Block):
157
156
  """Cognition workflow"""
158
157
 
@@ -233,6 +232,7 @@ class SocietyAgent(CitizenAgent):
233
232
 
234
233
  # Main workflow
235
234
  async def forward(self):
235
+ start_time = time.time()
236
236
  self.step_count += 1
237
237
  # sync agent status with simulator
238
238
  await self.update_with_sim()
@@ -242,10 +242,13 @@ class SocietyAgent(CitizenAgent):
242
242
  if not ifpass:
243
243
  return
244
244
 
245
+ # plan and action
245
246
  await self.planAndActionBlock.forward()
246
247
 
248
+ # cognition
247
249
  if self.enable_cognition:
248
250
  await self.mindBlock.forward()
251
+ return time.time() - start_time
249
252
 
250
253
  async def check_and_update_step(self):
251
254
  """Check if the previous step has been completed"""
@@ -257,7 +260,7 @@ class SocietyAgent(CitizenAgent):
257
260
 
258
261
  # Get the previous step information
259
262
  current_step = await self.memory.status.get("current_step")
260
- if current_step["intention"] == "" or current_step["type"] == "":
263
+ if current_step["intention"] == "":
261
264
  # No previous step, return directly
262
265
  return True
263
266
  time_now = int(await self.simulator.get_time())
@@ -304,9 +307,7 @@ class SocietyAgent(CitizenAgent):
304
307
  conclusion = await self.mindBlock.cognitionBlock.emotion_update(
305
308
  incident
306
309
  )
307
- await self.memory.stream.add_cognition(
308
- description=conclusion # type:ignore
309
- )
310
+ await self.save_agent_thought(conclusion)
310
311
  await self.memory.stream.add_cognition_to_memory(
311
312
  current_plan["stream_nodes"], conclusion # type:ignore
312
313
  )
@@ -331,9 +332,7 @@ class SocietyAgent(CitizenAgent):
331
332
  conclusion = await self.mindBlock.cognitionBlock.emotion_update(
332
333
  incident
333
334
  )
334
- await self.memory.stream.add_cognition(
335
- description=conclusion # type:ignore
336
- )
335
+ await self.save_agent_thought(conclusion)
337
336
  await self.memory.stream.add_cognition_to_memory(
338
337
  current_plan["stream_nodes"], conclusion # type:ignore
339
338
  )
@@ -13,7 +13,7 @@ from ..utils import encode_to_base64, find_free_port
13
13
  __all__ = ["ControlSimEnv"]
14
14
 
15
15
 
16
- def _generate_yaml_config(map_file: str, start_step: int, total_step: int) -> str:
16
+ def _generate_yaml_config(map_file: str, max_day: int, start_step: int, total_step: int) -> str:
17
17
  map_file = os.path.abspath(map_file)
18
18
  return f"""
19
19
  input:
@@ -22,6 +22,7 @@ input:
22
22
  file: "{map_file}"
23
23
 
24
24
  control:
25
+ day: {max_day}
25
26
  step:
26
27
  # 模拟器起始步
27
28
  start: {start_step}
@@ -46,6 +47,7 @@ class ControlSimEnv:
46
47
  self,
47
48
  task_name: str,
48
49
  map_file: str,
50
+ max_day: int,
49
51
  start_step: int,
50
52
  total_step: int,
51
53
  log_dir: str,
@@ -63,6 +65,7 @@ class ControlSimEnv:
63
65
  """
64
66
  self._task_name = task_name
65
67
  self._map_file = map_file
68
+ self._max_day = max_day
66
69
  self._start_step = start_step
67
70
  self._total_step = total_step
68
71
  self._log_dir = log_dir
@@ -70,7 +73,7 @@ class ControlSimEnv:
70
73
  self._timeout = timeout
71
74
  self._max_procs = max_process
72
75
 
73
- self._sim_config = _generate_yaml_config(map_file, start_step, total_step)
76
+ self._sim_config = _generate_yaml_config(map_file, max_day, start_step, total_step)
74
77
  # sim
75
78
  self.sim_port = None
76
79
  self._sim_proc = None
@@ -113,6 +116,7 @@ class ControlSimEnv:
113
116
  f":{self.sim_port}",
114
117
  "-run.min_step_time",
115
118
  f"{self._min_step_time}",
119
+ "-run.pause_after_one_day",
116
120
  "-output",
117
121
  self._log_dir,
118
122
  "-cache",