pycityagent 2.0.0a87__cp310-cp310-macosx_11_0_arm64.whl → 2.0.0a89__cp310-cp310-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.
@@ -167,16 +167,19 @@ class MonthPlanBlock(Block):
167
167
  "UBI",
168
168
  "num_labor_hours",
169
169
  "productivity_per_labor",
170
+ "time_diff"
170
171
  ]
171
172
  default_values = {
172
173
  "UBI": 0,
173
174
  "num_labor_hours": 168,
174
175
  "productivity_per_labor": 1,
176
+ "time_diff": 30 * 24 * 60 * 60
175
177
  }
176
178
  fields_description = {
177
179
  "UBI": "Universal Basic Income",
178
180
  "num_labor_hours": "Number of labor hours per month",
179
181
  "productivity_per_labor": "Productivity per labor hour",
182
+ "time_diff": "Time difference between two triggers"
180
183
  }
181
184
 
182
185
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator, economy_client: EconomyClient):
@@ -184,14 +187,13 @@ class MonthPlanBlock(Block):
184
187
  self.economy_client = economy_client
185
188
  self.llm_error = 0
186
189
  self.last_time_trigger = None
187
- self.time_diff = 30 * 24 * 60 * 60
188
190
  self.forward_times = 0
189
191
 
190
192
  # configurable fields
191
193
  self.UBI = 0
192
194
  self.num_labor_hours = 168
193
195
  self.productivity_per_labor = 1
194
-
196
+ self.time_diff = 30 * 24 * 60 * 60
195
197
 
196
198
  async def month_trigger(self):
197
199
  now_time = await self.simulator.get_time()
@@ -254,11 +256,11 @@ class MonthPlanBlock(Block):
254
256
  Any other output words are NOT allowed.
255
257
  '''
256
258
  obs_prompt = prettify_document(obs_prompt)
257
- await self.memory.status.update('dialog_queue', [{'role': 'user', 'content': obs_prompt}], mode='merge')
258
- dialog_queue = await self.memory.status.get('dialog_queue')
259
- content = await self.llm.atext_request(list(dialog_queue), timeout=300)
260
- await self.memory.status.update('dialog_queue', [{'role': 'assistant', 'content': content}], mode='merge')
261
259
  try:
260
+ await self.memory.status.update('dialog_queue', [{'role': 'user', 'content': obs_prompt}], mode='merge')
261
+ dialog_queue = await self.memory.status.get('dialog_queue')
262
+ content = await self.llm.atext_request(list(dialog_queue), timeout=300)
263
+ await self.memory.status.update('dialog_queue', [{'role': 'assistant', 'content': content}], mode='merge')
262
264
  propensity_dict = extract_dict_from_string(content)[0]
263
265
  work_propensity, consumption_propensity = propensity_dict['work'], propensity_dict['consumption']
264
266
  if isinstance(work_propensity, numbers.Number) and isinstance(consumption_propensity, numbers.Number):
@@ -46,7 +46,7 @@ Current temperature: {temperature}
46
46
  Your current emotion: {emotion_types}
47
47
  Your current thought: {thought}
48
48
 
49
- Please analyze how these emotions would affect travel willingness and return only a single integer number between 3000-100000 representing the maximum travel radius in meters. A more positive emotional state generally leads to greater willingness to travel further.
49
+ Please analyze how these emotions would affect travel willingness and return only a single integer number between 3000-200000 representing the maximum travel radius in meters. A more positive emotional state generally leads to greater willingness to travel further.
50
50
 
51
51
  Return only the integer number without any additional text or explanation."""
52
52
 
@@ -121,7 +121,7 @@ class PlaceSelectionBlock(Block):
121
121
  """
122
122
 
123
123
  configurable_fields: List[str] = ["search_limit"]
124
- default_values = {"search_limit": 10000}
124
+ default_values = {"search_limit": 1000}
125
125
 
126
126
  def __init__(self, llm: LLM, memory: Memory, simulator: Simulator):
127
127
  super().__init__(
@@ -134,7 +134,7 @@ class PlaceSelectionBlock(Block):
134
134
  )
135
135
  self.radiusPrompt = FormatPrompt(RADIUS_PROMPT)
136
136
  # configurable fields
137
- self.search_limit = 100
137
+ self.search_limit = 1000
138
138
 
139
139
  async def forward(self, step, context):
140
140
  poi_cate = self.simulator.get_poi_cate()
@@ -333,26 +333,30 @@ class NeedsBlock(Block):
333
333
  social_satisfaction=await self.memory.status.get("social_satisfaction"),
334
334
  )
335
335
 
336
- response = await self.llm.atext_request(self.evaluation_prompt.to_dialog())
337
-
338
- try:
339
- new_satisfaction = json.loads(self.clean_json_response(response)) # type: ignore
340
- # 更新所有需求的数值
341
- for need_type, new_value in new_satisfaction.items():
342
- if need_type in [
343
- "hunger_satisfaction",
344
- "energy_satisfaction",
345
- "safety_satisfaction",
346
- "social_satisfaction",
347
- ]:
348
- await self.memory.status.update(need_type, new_value)
349
- except json.JSONDecodeError:
350
- logger.warning(
351
- f"Evaluation response is not a valid JSON format: {response}"
352
- )
353
- except Exception as e:
354
- logger.warning(f"Error processing evaluation response: {str(e)}")
355
- logger.warning(f"Original response: {response}")
336
+ retry = 3
337
+ while retry > 0:
338
+ response = await self.llm.atext_request(self.evaluation_prompt.to_dialog())
339
+ try:
340
+ new_satisfaction = json.loads(self.clean_json_response(response)) # type: ignore
341
+ # 更新所有需求的数值
342
+ for need_type, new_value in new_satisfaction.items():
343
+ if need_type in [
344
+ "hunger_satisfaction",
345
+ "energy_satisfaction",
346
+ "safety_satisfaction",
347
+ "social_satisfaction",
348
+ ]:
349
+ await self.memory.status.update(need_type, new_value)
350
+ return
351
+ except json.JSONDecodeError:
352
+ logger.warning(
353
+ f"Evaluation response is not a valid JSON format: {response}"
354
+ )
355
+ retry -= 1
356
+ except Exception as e:
357
+ logger.warning(f"Error processing evaluation response: {str(e)}")
358
+ logger.warning(f"Original response: {response}")
359
+ retry -= 1
356
360
 
357
361
  def clean_json_response(self, response: str) -> str:
358
362
  """清理LLM响应中的特殊字符"""
@@ -213,13 +213,19 @@ class PlanBlock(Block):
213
213
  response = await self.llm.atext_request(
214
214
  self.guidance_prompt.to_dialog()
215
215
  ) # type: ignore
216
-
217
- try:
218
- result = json.loads(self.clean_json_response(response)) # type: ignore
219
- return result
220
- except Exception as e:
221
- logger.warning(f"Error parsing guidance selection response: {str(e)}")
222
- return None # type: ignore
216
+ retry = 3
217
+ while retry > 0:
218
+ try:
219
+ result = json.loads(self.clean_json_response(response)) # type: ignore
220
+ if "selected_option" not in result or "evaluation" not in result:
221
+ raise ValueError("Invalid guidance selection format")
222
+ if "attitude" not in result["evaluation"] or "subjective_norm" not in result["evaluation"] or "perceived_control" not in result["evaluation"] or "reasoning" not in result["evaluation"]:
223
+ raise ValueError("Evaluation must include attitude, subjective_norm, perceived_control, and reasoning")
224
+ return result
225
+ except Exception as e:
226
+ logger.warning(f"Error parsing guidance selection response: {str(e)}")
227
+ retry -= 1
228
+ return None
223
229
 
224
230
  async def generate_detailed_plan(
225
231
  self, current_need: str, selected_option: str
@@ -252,20 +258,22 @@ class PlanBlock(Block):
252
258
  )
253
259
 
254
260
  response = await self.llm.atext_request(self.detail_prompt.to_dialog())
255
-
256
- try:
257
- result = json.loads(self.clean_json_response(response)) # type: ignore
258
- return result
259
- except Exception as e:
260
- logger.warning(f"Error parsing detailed plan: {str(e)}")
261
- return None # type: ignore
261
+ retry = 3
262
+ while retry > 0:
263
+ try:
264
+ result = json.loads(self.clean_json_response(response)) # type: ignore
265
+ if "plan" not in result or "target" not in result["plan"] or "steps" not in result["plan"]:
266
+ raise ValueError("Invalid plan format")
267
+ for step in result["plan"]["steps"]:
268
+ if "intention" not in step or "type" not in step:
269
+ raise ValueError("Each step must have an intention and a type")
270
+ return result
271
+ except Exception as e:
272
+ logger.warning(f"Error parsing detailed plan: {str(e)}")
273
+ retry -= 1
274
+ return None
262
275
 
263
276
  async def forward(self):
264
- self.trigger_time += 1
265
- consumption_start = (
266
- self.llm.prompt_tokens_used + self.llm.completion_tokens_used
267
- )
268
-
269
277
  # Step 1: Select guidance plan
270
278
  current_need = await self.memory.status.get("current_need")
271
279
  guidance_result = await self.select_guidance(current_need)
@@ -278,7 +286,7 @@ class PlanBlock(Block):
278
286
  )
279
287
 
280
288
  if not detailed_plan or "plan" not in detailed_plan:
281
- await self.memory.status.update("current_plan", [])
289
+ await self.memory.status.update("current_plan", None)
282
290
  await self.memory.status.update(
283
291
  "current_step", {"intention": "", "type": ""}
284
292
  )
@@ -311,9 +319,6 @@ Execution Steps: \n{formated_steps}
311
319
  )
312
320
  await self.memory.status.update("execution_context", {"plan": formated_plan})
313
321
 
314
- consumption_end = self.llm.prompt_tokens_used + self.llm.completion_tokens_used
315
- self.token_consumption += consumption_end - consumption_start
316
-
317
322
  def clean_json_response(self, response: str) -> str:
318
323
  """Clean special characters in LLM response"""
319
324
  response = response.replace("```json", "").replace("```", "")
@@ -142,6 +142,7 @@ async def bind_agent_info(simulation):
142
142
  for bank_uuid in bank_uuids:
143
143
  await simulation.economy_update(uid2agent[bank_uuid], "citizens", citizen_agent_ids)
144
144
  for nbs_uuid in nbs_uuids:
145
+ await simulation.update(nbs_uuid, "citizens", citizen_uuids)
145
146
  await simulation.economy_update(uid2agent[nbs_uuid], "citizens", citizen_agent_ids)
146
147
  logger.info("Agent info binding completed!")
147
148
 
@@ -33,11 +33,11 @@ async def economy_metric(simulation):
33
33
  economy_metric.nbs_uuid = nbs_uuids[0]
34
34
 
35
35
  try:
36
- real_gdp = await simulation.economy_client.get(nbs_id, 'real_gdp')
36
+ real_gdp = await simulation.economy_client.get(economy_metric.nbs_id, 'real_gdp')
37
37
  except:
38
38
  real_gdp = []
39
39
  if len(real_gdp) > 0:
40
- real_gdp = real_gdp[0]
40
+ real_gdp = real_gdp[-1]
41
41
  forward_times_info = await simulation.gather("forward_times", [economy_metric.nbs_uuid])
42
42
  step_count = 0
43
43
  for group_gather in forward_times_info:
@@ -48,5 +48,5 @@ async def economy_metric(simulation):
48
48
  other_metrics = ['prices', 'working_hours', 'depression', 'consumption_currency', 'income_currency']
49
49
  other_metrics_names = ['price', 'working_hours', 'depression', 'consumption', 'income']
50
50
  for metric, metric_name in zip(other_metrics, other_metrics_names):
51
- metric_value = (await simulation.economy_client.get(nbs_id, metric))[-1]
51
+ metric_value = (await simulation.economy_client.get(economy_metric.nbs_id, metric))[-1]
52
52
  await simulation.mlflow_client.log_metric(key=metric_name, value=metric_value, step=step_count)
@@ -73,6 +73,8 @@ class PlanAndActionBlock(Block):
73
73
  async def step_execution(self):
74
74
  """Execute the current step"""
75
75
  current_plan = await self.memory.status.get("current_plan")
76
+ if current_plan is None:
77
+ return
76
78
  execution_context = await self.memory.status.get("execution_context")
77
79
  current_step = await self.memory.status.get("current_step")
78
80
  # check current_step is valid (not empty)
pycityagent/llm/llm.py CHANGED
@@ -203,7 +203,7 @@ class LLM:
203
203
  frequency_penalty: Optional[float] = None,
204
204
  presence_penalty: Optional[float] = None,
205
205
  timeout: int = 300,
206
- retries=3,
206
+ retries=10,
207
207
  tools: Optional[list[dict[str, Any]]] = None,
208
208
  tool_choice: Optional[dict[str, Any]] = None,
209
209
  ):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pycityagent
3
- Version: 2.0.0a87
3
+ Version: 2.0.0a89
4
4
  Summary: LLM-based city environment agent building library
5
5
  Author-email: Yuwei Yan <pinkgranite86@gmail.com>, Junbo Yan <yanjb20thu@gmali.com>, Jun Zhang <zhangjun990222@gmali.com>
6
6
  License: MIT License
@@ -9,7 +9,7 @@ pycityagent/tools/__init__.py,sha256=y7sMVMHf0AbivlczM2h-kr7mkgXK-WAx3S9BXLXkWvw
9
9
  pycityagent/tools/tool.py,sha256=4ZJSHbNM8dfAVwZEw8T0a2_9OuPsPQpKSVL4WxZVBUc,9022
10
10
  pycityagent/llm/llmconfig.py,sha256=6AqCMV4B_JqBD2mb98bLGzpUdlOCnziQKae-Hhxxp-E,469
11
11
  pycityagent/llm/__init__.py,sha256=iWs6FLgrbRVIiqOf4ILS89gkVCTvS7HFC3vG-MWuyko,205
12
- pycityagent/llm/llm.py,sha256=A1Q-dKEXHJKhBnElI7Ci6eIK-A7I-UZNQxC_hEgEa-4,20018
12
+ pycityagent/llm/llm.py,sha256=vMw9AVftrsmbGkhRIwJ7jftFWpzTofhfQmu7TazDCaQ,20019
13
13
  pycityagent/llm/embeddings.py,sha256=3610I-_scAy8HwRNpT8hVJpH9_8_pTLCPptqnzSq10o,11322
14
14
  pycityagent/llm/utils.py,sha256=rSx_fp-_Gh0vZ-x2rqAUqnpS56BVTZ4ChfAMarB8S1A,195
15
15
  pycityagent/memory/memory.py,sha256=5mUweo-BgQYbXmVRAk7HTUXsar6O6iYz79VbLuMAvjo,45063
@@ -66,32 +66,32 @@ pycityagent/environment/sim/social_service.py,sha256=Y4A56aKXsjSv18UFumGPjQoJVMc
66
66
  pycityagent/environment/sim/light_service.py,sha256=q4pKcGrm7WU0h29I1dFIDOz2OV0BM-2s37uC6zokkoA,4290
67
67
  pycityagent/environment/sim/clock_service.py,sha256=4Hly8CToghj0x_XbDgGrIZy1YYAlDH0EUGCiCDYpk_I,1375
68
68
  pycityagent/environment/sim/road_service.py,sha256=Bb1sreO0Knt9tcqH_WJF-I3P3G92bRAlzDBEa--25GE,1297
69
- pycityagent/cityagent/metrics.py,sha256=SdCxEuO-wgsPGo3H6xywnOAfP5Lre-bc7HNAQ-n53mA,2687
69
+ pycityagent/cityagent/metrics.py,sha256=wVxLpk4jvVoPf2baiK-jMIi2rPPMRnFpSjOVyj1k74A,2718
70
70
  pycityagent/cityagent/memory_config.py,sha256=lCySjh8jpE3Sj-_XTCPizxJN8rjfcYD54sed7wi2EDw,11958
71
71
  pycityagent/cityagent/bankagent.py,sha256=I6MNG1fUbxKyWCLxCtvOjeVahong_LhHQKmJdRPhQL8,4097
72
72
  pycityagent/cityagent/__init__.py,sha256=gcBQ-a50XegFtjigQ7xDXRBZrywBKqifiQFSRnEF8gM,572
73
73
  pycityagent/cityagent/firmagent.py,sha256=vZr7kdjnxcCZ5qX7hmUIYuQQWd44GcRPbdi76WGSFy4,3760
74
74
  pycityagent/cityagent/nbsagent.py,sha256=rrzL-Ep-gWB8kvoPA8nBokY1zoEZGpyQjP9oy7ZuVL4,4676
75
- pycityagent/cityagent/initial.py,sha256=0DSOWnVVknMw6xoJWfdZUFBH3yti8y800wnkXXCocxY,6194
76
- pycityagent/cityagent/societyagent.py,sha256=1c6k3RgxQ7AAYCiBXwiSZLDkCDMgCrBzNF6vi6lK2Yc,20069
75
+ pycityagent/cityagent/initial.py,sha256=tVcO9ECGvsFatpxQirZbZf0ESFBxSpSxkm4p0x2kiO0,6263
76
+ pycityagent/cityagent/societyagent.py,sha256=i7V7BeCb_pMJpxwiR5AOUcNHRw0SXHXltt_DtCxE6pE,20121
77
77
  pycityagent/cityagent/message_intercept.py,sha256=dyT1G-nMxKb2prhgtyFFHFz593qBrkk5DnHsHvG1OIc,4418
78
78
  pycityagent/cityagent/governmentagent.py,sha256=XIyggG83FWUTZdOuoqc6ClCP3hhfkxNmtYRu9TFo0dU,3063
79
79
  pycityagent/cityagent/blocks/dispatcher.py,sha256=U5BPeUrjrTyDaznYfT6YUJIW8vfKVRDF4EO0oOn6Td4,2886
80
- pycityagent/cityagent/blocks/needs_block.py,sha256=NYKrGDoYCuXoupMNMuSNhx4Ci1paC_EuGRWvp5-ZSA8,15909
80
+ pycityagent/cityagent/blocks/needs_block.py,sha256=eFWDgsqjh4FYaGeb8wL5R9ndaXN3977ymexoItDUki8,16112
81
81
  pycityagent/cityagent/blocks/cognition_block.py,sha256=yzjB0D_95vytpa5xiVdmTSpGp8H9HXcjWzzFN0OpP0k,15398
82
82
  pycityagent/cityagent/blocks/social_block.py,sha256=eedOlwRTGI47QFELYmfe2a_aj0GuHJweSyDxA6AYXcU,15493
83
83
  pycityagent/cityagent/blocks/__init__.py,sha256=h6si6WBcVVuglIskKQKA8Cxtf_VKen1sNPqOFKI311Q,420
84
- pycityagent/cityagent/blocks/economy_block.py,sha256=ZhNzhkXxzFidQQRLS5jNv5msHSdoASouKBqvAzecJU4,19709
84
+ pycityagent/cityagent/blocks/economy_block.py,sha256=l47x2Iq15Rjj9WsH3p398dtpcejat4aCkC3X160xBlE,19843
85
85
  pycityagent/cityagent/blocks/utils.py,sha256=K--6odjUDUu9YrwrHPaiPIHryo7m_MBmcBqDAy3cV5M,1816
86
86
  pycityagent/cityagent/blocks/other_block.py,sha256=LdtL6248xvMvvRQx6NvdlJrWWZFu8Xusjxb9yEh1M0k,4365
87
- pycityagent/cityagent/blocks/plan_block.py,sha256=A5DvtXIy98MZkRGUQmp26grNI5i0BVbl3aEM_Ebd6Z4,11271
88
- pycityagent/cityagent/blocks/mobility_block.py,sha256=qkRiV0nkGOUUoo9lGIjAFE_Hl0kiyMev12-Zm7JyV7o,15089
87
+ pycityagent/cityagent/blocks/plan_block.py,sha256=AFyjj-RimYNkZ8x6sU-ZMWwB1jqJxSvr1j0vEKmhG4w,11967
88
+ pycityagent/cityagent/blocks/mobility_block.py,sha256=OZoA-swFdX0Ezpzkyp8jJMNPQ533JpeeTCdLupoqRjI,15089
89
89
  pycityagent/survey/models.py,sha256=g3xni4GcA1Py3vlGt6z4ltutjgQ4G0uINYAM8vKRJAw,5225
90
90
  pycityagent/survey/__init__.py,sha256=rxwou8U9KeFSP7rMzXtmtp2fVFZxK4Trzi-psx9LPIs,153
91
91
  pycityagent/survey/manager.py,sha256=tHkdeq4lTfAHwvgf4-udsXri0z2l6E00rEbvwl7SqRs,3439
92
- pycityagent-2.0.0a87.dist-info/RECORD,,
93
- pycityagent-2.0.0a87.dist-info/LICENSE,sha256=n2HPXiupinpyHMnIkbCf3OTYd3KMqbmldu1e7av0CAU,1084
94
- pycityagent-2.0.0a87.dist-info/WHEEL,sha256=ezfKMaDztqf77C8lvQ0NCnZxkTaOaKLprqJ8q932MhU,109
95
- pycityagent-2.0.0a87.dist-info/entry_points.txt,sha256=BZcne49AAIFv-hawxGnPbblea7X3MtAtoPyDX8L4OC4,132
96
- pycityagent-2.0.0a87.dist-info/top_level.txt,sha256=yOmeu6cSXmiUtScu53a3s0p7BGtLMaV0aff83EHCTic,43
97
- pycityagent-2.0.0a87.dist-info/METADATA,sha256=nhB__BcNLDfb1PCA7Wjb5GWYo7mRegc1KPQFypOERlk,9110
92
+ pycityagent-2.0.0a89.dist-info/RECORD,,
93
+ pycityagent-2.0.0a89.dist-info/LICENSE,sha256=n2HPXiupinpyHMnIkbCf3OTYd3KMqbmldu1e7av0CAU,1084
94
+ pycityagent-2.0.0a89.dist-info/WHEEL,sha256=ezfKMaDztqf77C8lvQ0NCnZxkTaOaKLprqJ8q932MhU,109
95
+ pycityagent-2.0.0a89.dist-info/entry_points.txt,sha256=BZcne49AAIFv-hawxGnPbblea7X3MtAtoPyDX8L4OC4,132
96
+ pycityagent-2.0.0a89.dist-info/top_level.txt,sha256=yOmeu6cSXmiUtScu53a3s0p7BGtLMaV0aff83EHCTic,43
97
+ pycityagent-2.0.0a89.dist-info/METADATA,sha256=ePWFzp7BbUYZs_AtmsewHwFGrmPbUyMwamMc1KJQfno,9110