pycityagent 2.0.0a57__cp310-cp310-macosx_11_0_arm64.whl → 2.0.0a59__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.
@@ -161,6 +161,7 @@ class MindBlock(Block):
161
161
  await self.cognitionBlock.forward()
162
162
 
163
163
  class SocietyAgent(CitizenAgent):
164
+ update_with_sim = UpdateWithSimulator()
164
165
  mindBlock: MindBlock
165
166
  planAndActionBlock: PlanAndActionBlock
166
167
  update_with_sim = UpdateWithSimulator()
@@ -65,6 +65,7 @@ class Simulator:
65
65
  sim_addr=config["simulator"].get("server", None),
66
66
  )
67
67
  self.server_addr = sim_env.sim_addr
68
+ config["simulator"]["server"] = self.server_addr
68
69
  # using local client
69
70
  self._client = CityClient(sim_env.sim_addr, secure=False)
70
71
  """
@@ -72,10 +73,8 @@ class Simulator:
72
73
  - grpc client of simulator
73
74
  """
74
75
  else:
75
- raise ValueError(
76
- f"Passing Traffic Simulation address is not supported!"
77
- )
78
76
  self._client = CityClient(config["simulator"]["server"], secure=False)
77
+ self.server_addr = config["simulator"]["server"]
79
78
  else:
80
79
  self.server_addr = None
81
80
  logger.warning(
@@ -34,10 +34,7 @@ class AgentGroup:
34
34
  self,
35
35
  agent_class: Union[type[Agent], list[type[Agent]]],
36
36
  number_of_agents: Union[int, list[int]],
37
- memory_config_function_group: Union[
38
- Callable[[], tuple[dict, dict, dict]],
39
- list[Callable[[], tuple[dict, dict, dict]]],
40
- ],
37
+ memory_config_function_group: dict[type[Agent], Callable],
41
38
  config: dict,
42
39
  exp_name: str,
43
40
  exp_id: str | UUID,
@@ -49,14 +46,12 @@ class AgentGroup:
49
46
  mlflow_run_id: str,
50
47
  embedding_model: Embeddings,
51
48
  logging_level: int,
52
- agent_config_file: Optional[Union[str, list[str]]] = None,
49
+ agent_config_file: Optional[dict[type[Agent], str]] = None,
53
50
  ):
54
51
  logger.setLevel(logging_level)
55
52
  self._uuid = str(uuid.uuid4())
56
53
  if not isinstance(agent_class, list):
57
54
  agent_class = [agent_class]
58
- if not isinstance(memory_config_function_group, list):
59
- memory_config_function_group = [memory_config_function_group]
60
55
  if not isinstance(number_of_agents, list):
61
56
  number_of_agents = [number_of_agents]
62
57
  self.agent_class = agent_class
@@ -123,13 +118,10 @@ class AgentGroup:
123
118
  self.projector = pyproj.Proj(self.simulator.map.header["projection"])
124
119
 
125
120
  # prepare Economy client
126
- if "economy" in config["simulator_request"]:
127
- logger.info(f"-----Creating Economy client in AgentGroup {self._uuid} ...")
128
- self.economy_client = EconomyClient(
129
- config["simulator_request"]["economy"]["server"]
130
- )
131
- else:
132
- self.economy_client = None
121
+ logger.info(f"-----Creating Economy client in AgentGroup {self._uuid} ...")
122
+ self.economy_client = EconomyClient(
123
+ config["simulator_request"]["simulator"]["server"]
124
+ )
133
125
 
134
126
  # set FaissQuery
135
127
  if self.embedding_model is not None:
@@ -142,7 +134,7 @@ class AgentGroup:
142
134
  agent_class_i = agent_class[i]
143
135
  number_of_agents_i = number_of_agents[i]
144
136
  for j in range(number_of_agents_i):
145
- memory_config_function_group_i = memory_config_function_group[i]
137
+ memory_config_function_group_i = memory_config_function_group[agent_class_i]
146
138
  extra_attributes, profile, base = memory_config_function_group_i()
147
139
  memory = Memory(config=extra_attributes, profile=profile, base=base)
148
140
  agent = agent_class_i(
@@ -161,8 +153,8 @@ class AgentGroup:
161
153
  agent.set_avro_file(self.avro_file) # type: ignore
162
154
  if self.enable_pgsql:
163
155
  agent.set_pgsql_writer(self._pgsql_writer)
164
- if self.agent_config_file is not None and self.agent_config_file[i]:
165
- agent.load_from_file(self.agent_config_file[i])
156
+ if self.agent_config_file is not None and self.agent_config_file[agent_class_i]:
157
+ agent.load_from_file(self.agent_config_file[agent_class_i])
166
158
  if self._message_interceptor is not None:
167
159
  agent.set_message_interceptor(self._message_interceptor)
168
160
  self.agents.append(agent)
@@ -3,24 +3,24 @@ import json
3
3
  import logging
4
4
  import time
5
5
  import uuid
6
- from collections.abc import Callable, Sequence
6
+ from collections.abc import Callable
7
7
  from datetime import datetime, timezone
8
8
  from pathlib import Path
9
- from typing import Any, Optional, Type, Union
9
+ from typing import Any, Literal, Optional, Type, Union
10
10
 
11
11
  import ray
12
12
  import yaml
13
13
  from langchain_core.embeddings import Embeddings
14
14
 
15
15
  from ..agent import Agent, InstitutionAgent
16
- from ..cityagent import (BankAgent, FirmAgent, GovernmentAgent, NBSAgent,
17
- SocietyAgent, memory_config_bank, memory_config_firm,
16
+ from ..cityagent import (BankAgent, FirmAgent, GovernmentAgent, NBSAgent,SocietyAgent,
17
+ memory_config_bank, memory_config_firm,
18
18
  memory_config_government, memory_config_nbs,
19
19
  memory_config_societyagent)
20
20
  from ..cityagent.initial import bind_agent_info, initialize_social_network
21
21
  from ..environment import Simulator
22
- from ..llm import LLM, LLMConfig, SimpleEmbedding
23
- from ..memory import Memory
22
+ from ..economy.econ_client import EconomyClient
23
+ from ..llm import SimpleEmbedding
24
24
  from ..message import (MessageBlockBase, MessageBlockListenerBase,
25
25
  MessageInterceptor, Messager)
26
26
  from ..metrics import init_mlflow_connection
@@ -42,7 +42,7 @@ class AgentSimulation:
42
42
  agent_class: Union[None, type[Agent], list[type[Agent]]] = None,
43
43
  agent_config_file: Optional[dict] = None,
44
44
  metric_extractor: Optional[list[tuple[int, Callable]]] = None,
45
- enable_economy: bool = True,
45
+ enable_institution: bool = True,
46
46
  agent_prefix: str = "agent_",
47
47
  exp_name: str = "default_experiment",
48
48
  logging_level: int = logging.WARNING,
@@ -58,7 +58,7 @@ class AgentSimulation:
58
58
  if isinstance(agent_class, list):
59
59
  self.agent_class = agent_class
60
60
  elif agent_class is None:
61
- if enable_economy:
61
+ if enable_institution:
62
62
  self.agent_class = [
63
63
  SocietyAgent,
64
64
  FirmAgent,
@@ -66,24 +66,30 @@ class AgentSimulation:
66
66
  NBSAgent,
67
67
  GovernmentAgent,
68
68
  ]
69
- self.default_memory_config_func = [
70
- memory_config_societyagent,
71
- memory_config_firm,
72
- memory_config_bank,
73
- memory_config_nbs,
74
- memory_config_government,
75
- ]
69
+ self.default_memory_config_func = {
70
+ SocietyAgent: memory_config_societyagent,
71
+ FirmAgent: memory_config_firm,
72
+ BankAgent: memory_config_bank,
73
+ NBSAgent: memory_config_nbs,
74
+ GovernmentAgent: memory_config_government,
75
+ }
76
76
  else:
77
77
  self.agent_class = [SocietyAgent]
78
- self.default_memory_config_func = [memory_config_societyagent]
78
+ self.default_memory_config_func = {SocietyAgent: memory_config_societyagent}
79
79
  else:
80
80
  self.agent_class = [agent_class]
81
81
  self.agent_config_file = agent_config_file
82
82
  self.logging_level = logging_level
83
83
  self.config = config
84
84
  self.exp_name = exp_name
85
+ _simulator_config = config["simulator_request"].get("simulator", {})
86
+ if "server" in _simulator_config:
87
+ raise ValueError(f"Passing Traffic Simulation address is not supported!")
85
88
  self._simulator = Simulator(config["simulator_request"])
86
- if enable_economy:
89
+ self._economy_client = EconomyClient(
90
+ config["simulator_request"]["simulator"]["server"]
91
+ )
92
+ if enable_institution:
87
93
  self._economy_addr = economy_addr = self._simulator.server_addr
88
94
  if economy_addr is None:
89
95
  raise ValueError(
@@ -202,9 +208,11 @@ class AgentSimulation:
202
208
  """Directly run from config file
203
209
  Basic config file should contain:
204
210
  - simulation_config: file_path
211
+ - enable_institution: bool, default is True
205
212
  - agent_config:
206
- - agent_config_file: Optional[dict]
207
- - memory_config_func: Optional[Union[Callable, list[Callable]]]
213
+ - agent_config_file: Optional[dict[type[Agent], str]]
214
+ - memory_config_func: Optional[dict[type[Agent], Callable]]
215
+ - metric_extractor: Optional[list[tuple[int, Callable]]]
208
216
  - init_func: Optional[list[Callable[AgentSimulation, None]]]
209
217
  - group_size: Optional[int]
210
218
  - embedding_model: Optional[EmbeddingModel]
@@ -217,7 +225,7 @@ class AgentSimulation:
217
225
  - list[Step]
218
226
  - Step:
219
227
  - type: str, "step", "run", "interview", "survey", "intervene", "pause", "resume"
220
- - day: int if type is "run", else None
228
+ - days: int if type is "run", else None
221
229
  - times: int if type is "step", else None
222
230
  - description: Optional[str], description of the step
223
231
  - func: Optional[Callable[AgentSimulation, None]], only used when type is "interview", "survey" and "intervene"
@@ -240,6 +248,8 @@ class AgentSimulation:
240
248
  simulation = cls(
241
249
  config=simulation_config,
242
250
  agent_config_file=config["agent_config"].get("agent_config_file", None),
251
+ metric_extractor=config["agent_config"].get("metric_extractor", None),
252
+ enable_institution=config.get("enable_institution", True),
243
253
  exp_name=config.get("exp_name", "default_experiment"),
244
254
  logging_level=config.get("logging_level", logging.WARNING),
245
255
  )
@@ -272,7 +282,7 @@ class AgentSimulation:
272
282
  if step["type"] not in ["run", "step", "interview", "survey", "intervene"]:
273
283
  raise ValueError(f"Invalid step type: {step['type']}")
274
284
  if step["type"] == "run":
275
- await simulation.run(step.get("day", 1))
285
+ await simulation.run(step.get("days", 1))
276
286
  elif step["type"] == "step":
277
287
  times = step.get("times", 1)
278
288
  for _ in range(times):
@@ -302,6 +312,10 @@ class AgentSimulation:
302
312
  self,
303
313
  ) -> Path:
304
314
  return self._avro_path # type:ignore
315
+
316
+ @property
317
+ def economy_client(self):
318
+ return self._economy_client
305
319
 
306
320
  @property
307
321
  def groups(self):
@@ -406,7 +420,7 @@ class AgentSimulation:
406
420
  social_black_list: Optional[list[tuple[str, str]]] = None,
407
421
  message_listener: Optional[MessageBlockListenerBase] = None,
408
422
  embedding_model: Embeddings = SimpleEmbedding(),
409
- memory_config_func: Optional[Union[Callable, list[Callable]]] = None,
423
+ memory_config_func: Optional[dict[type[Agent], Callable]] = None,
410
424
  ) -> None:
411
425
  """初始化智能体
412
426
 
@@ -415,7 +429,7 @@ class AgentSimulation:
415
429
  group_size: 每个组的智能体数量,每一个组为一个独立的ray actor
416
430
  pg_sql_writers: 独立的PgSQL writer数量
417
431
  message_interceptors: message拦截器数量
418
- memory_config_func: 返回Memory配置的函数,需要返回(EXTRA_ATTRIBUTES, PROFILE, BASE)元组, 如果为列表,则每个元素表示一个智能体类创建的Memory配置函数
432
+ memory_config_func: 返回Memory配置的函数,需要返回(EXTRA_ATTRIBUTES, PROFILE, BASE)元组, 每个元素表示一个智能体类创建的Memory配置函数
419
433
  """
420
434
  if not isinstance(agent_count, list):
421
435
  agent_count = [agent_count]
@@ -426,17 +440,6 @@ class AgentSimulation:
426
440
  if memory_config_func is None:
427
441
  memory_config_func = self.default_memory_config_func
428
442
 
429
- elif not isinstance(memory_config_func, list):
430
- logger.warning(
431
- "memory_config_func is not a list, using specific memory config function"
432
- )
433
- memory_config_func = [memory_config_func]
434
-
435
- if len(memory_config_func) != len(agent_count):
436
- logger.warning(
437
- "The length of memory_config_func and agent_count does not match, using default memory_config"
438
- )
439
- memory_config_func = self.default_memory_config_func
440
443
  # 使用线程池并行创建 AgentGroup
441
444
  group_creation_params = []
442
445
 
@@ -448,7 +451,7 @@ class AgentSimulation:
448
451
  for i in range(len(self.agent_class)):
449
452
  agent_class = self.agent_class[i]
450
453
  agent_count_i = agent_count[i]
451
- memory_config_func_i = memory_config_func[i]
454
+ memory_config_func_i = memory_config_func.get(agent_class, self.default_memory_config_func[agent_class])
452
455
 
453
456
  if self.agent_config_file is not None:
454
457
  config_file = self.agent_config_file.get(agent_class, None)
@@ -478,8 +481,8 @@ class AgentSimulation:
478
481
 
479
482
  agent_classes = []
480
483
  agent_counts = []
481
- memory_config_funcs = []
482
- config_files = []
484
+ memory_config_funcs = {}
485
+ config_files = {}
483
486
 
484
487
  # 分配每种类型的机构智能体到当前组
485
488
  curr_start = start_idx
@@ -487,8 +490,8 @@ class AgentSimulation:
487
490
  if curr_start < count:
488
491
  agent_classes.append(agent_class)
489
492
  agent_counts.append(min(count - curr_start, number_of_agents))
490
- memory_config_funcs.append(mem_func)
491
- config_files.append(conf_file)
493
+ memory_config_funcs[agent_class] = mem_func
494
+ config_files[agent_class] = conf_file
492
495
  curr_start = max(0, curr_start - count)
493
496
 
494
497
  group_creation_params.append(
@@ -513,8 +516,8 @@ class AgentSimulation:
513
516
 
514
517
  agent_classes = []
515
518
  agent_counts = []
516
- memory_config_funcs = []
517
- config_files = []
519
+ memory_config_funcs = {}
520
+ config_files = {}
518
521
 
519
522
  # 分配每种类型的普通智能体到当前组
520
523
  curr_start = start_idx
@@ -522,8 +525,8 @@ class AgentSimulation:
522
525
  if curr_start < count:
523
526
  agent_classes.append(agent_class)
524
527
  agent_counts.append(min(count - curr_start, number_of_agents))
525
- memory_config_funcs.append(mem_func)
526
- config_files.append(conf_file)
528
+ memory_config_funcs[agent_class] = mem_func
529
+ config_files[agent_class] = conf_file
527
530
  curr_start = max(0, curr_start - count)
528
531
 
529
532
  group_creation_params.append(
@@ -685,14 +688,20 @@ class AgentSimulation:
685
688
  group = self._agent_uuid2group[target_agent_uuid]
686
689
  await group.update.remote(target_agent_uuid, target_key, content)
687
690
 
691
+ async def economy_update(self, target_agent_id: int, target_key: str, content: Any, mode: Literal["replace", "merge"] = "replace"):
692
+ """更新指定智能体的经济数据"""
693
+ self.economy_client.update(
694
+ id = target_agent_id,
695
+ key = target_key,
696
+ value=content,
697
+ mode=mode
698
+ )
699
+
688
700
  async def send_survey(
689
- self, survey: Survey, agent_uuids: Optional[list[str]] = None
701
+ self, survey: Survey, agent_uuids: list[str] = None
690
702
  ):
691
703
  """发送问卷"""
692
- await self.messager.connect.remote() # type:ignore
693
704
  survey_dict = survey.to_dict()
694
- if agent_uuids is None:
695
- agent_uuids = self._agent_uuids
696
705
  _date_time = datetime.now(timezone.utc)
697
706
  payload = {
698
707
  "from": SURVEY_SENDER_UUID,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pycityagent
3
- Version: 2.0.0a57
3
+ Version: 2.0.0a59
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
@@ -22,9 +22,9 @@ pycityagent/memory/utils.py,sha256=oJWLdPeJy_jcdKcDTo9JAH9kDZhqjoQhhv_zT9qWC0w,8
22
22
  pycityagent/memory/const.py,sha256=6zpJPJXWoH9-yf4RARYYff586agCoud9BRn7sPERB1g,932
23
23
  pycityagent/memory/faiss_query.py,sha256=V3rIw6d1_xcpNqZBbAYz3qfjVNE7NfJ7xOS5SibPtVU,13180
24
24
  pycityagent/memory/state.py,sha256=TYItiyDtehMEQaSBN7PpNrnNxdDM5jGppr9R9Ufv3kA,5134
25
- pycityagent/simulation/simulation.py,sha256=XOhHYx4zvDv6zgPBagY9Ao109wSZPSbdCH-amsw2pHo,33441
25
+ pycityagent/simulation/simulation.py,sha256=sbrX-e8gV2Wwi8k6ir7TVgQhzejpUZ85SfuiXO4E39I,33983
26
26
  pycityagent/simulation/__init__.py,sha256=P5czbcg2d8S0nbbnsQXFIhwzO4CennAhZM8OmKvAeYw,194
27
- pycityagent/simulation/agentgroup.py,sha256=UnWUQ3Ev4qDQCOasb-EN08aUQi4SWHmM6aNSdBc850Q,31693
27
+ pycityagent/simulation/agentgroup.py,sha256=V-ChLJtJUWJZnmTGllXqGk4XW4iDz9h3V89RgW0e4gg,31376
28
28
  pycityagent/simulation/storage/pg.py,sha256=xRshSOGttW-p0re0fNBOjOpb-nQ5msIE2LsdT79_E_Y,8425
29
29
  pycityagent/message/message_interceptor.py,sha256=w8XTyZStQtMjILpeAX3VMhAWcYAuaxCgSMwXQU1OryM,8951
30
30
  pycityagent/message/__init__.py,sha256=f5QH7DKPqEAMyfSlBMnl3uouOKlsoel909STlIe7nUk,276
@@ -47,7 +47,7 @@ pycityagent/workflow/prompt.py,sha256=6jI0Rq54JLv3-IXqZLYug62vse10wTI83xvf4ZX42n
47
47
  pycityagent/workflow/block.py,sha256=4QufS8XnyP6SYp8g1gDODW-H0nAHA7lvivrPGUq1p-w,9922
48
48
  pycityagent/workflow/trigger.py,sha256=Df-MOBEDWBbM-v0dFLQLXteLsipymT4n8vqexmK2GiQ,5643
49
49
  pycityagent/environment/__init__.py,sha256=MyZBwsweDIHOKSX2iSZs748foNtaiyEcyg6sc747T2g,263
50
- pycityagent/environment/simulator.py,sha256=d2meQZewFFSvUZa7BkgCHgOkhFf6G_TQyIz_QBFgDI0,12437
50
+ pycityagent/environment/simulator.py,sha256=8imXFHhNxVhritO7YEZqM59x4VkKS8rc0Dwu6SHaREo,12439
51
51
  pycityagent/environment/message/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
52
  pycityagent/environment/utils/port.py,sha256=3OM6kSUt3PxvDUOlgyiendBtETaWU8Mzk_8H0TzTmYg,295
53
53
  pycityagent/environment/utils/grpc.py,sha256=6EJwKXXktIWb1NcUiJzIRmfrY0S03QAXXGcCDHqAT00,1998
@@ -81,7 +81,7 @@ pycityagent/cityagent/__init__.py,sha256=gcBQ-a50XegFtjigQ7xDXRBZrywBKqifiQFSRnE
81
81
  pycityagent/cityagent/firmagent.py,sha256=UVlNN0lpa4cC4PZVqYzQhbc5VJ2oGsA1731mhbCjnR8,4109
82
82
  pycityagent/cityagent/nbsagent.py,sha256=WIXW__6dZ5IrqBqDCjvGbrCshpXzuFRV3Ww6gkYw7p4,4387
83
83
  pycityagent/cityagent/initial.py,sha256=7hgCt_tGdnVTXGfEQOn1GTW5dAs1b-ru_FwXxRLI6tM,4549
84
- pycityagent/cityagent/societyagent.py,sha256=vvFGAr8wlZ82f4qHvpawa1VsHungDegjKn1E51bDUnc,24767
84
+ pycityagent/cityagent/societyagent.py,sha256=DKkPtCPXTpHs83hi3bvXxkQYxKqZI0YldYCyFu4yjsk,24811
85
85
  pycityagent/cityagent/message_intercept.py,sha256=HiNOtBXKq40LCmvsw2KMLxMsbmXSVcbRytAO-eI-1sU,3279
86
86
  pycityagent/cityagent/governmentagent.py,sha256=HJLuhvEmllu_1KnFEJsYCIasaBJT0BV9Cn_4Y2QGPqg,2791
87
87
  pycityagent/cityagent/blocks/dispatcher.py,sha256=mEa1r3tRS3KI1BMZR_w_sbUGzOj6aUJuiUrsHv1n2n0,2943
@@ -97,9 +97,9 @@ pycityagent/cityagent/blocks/mobility_block.py,sha256=xWbARMfJ3-6fddrW3VOUKJrXfM
97
97
  pycityagent/survey/models.py,sha256=YE50UUt5qJ0O_lIUsSY6XFCGUTkJVNu_L1gAhaCJ2fs,3546
98
98
  pycityagent/survey/__init__.py,sha256=rxwou8U9KeFSP7rMzXtmtp2fVFZxK4Trzi-psx9LPIs,153
99
99
  pycityagent/survey/manager.py,sha256=S5IkwTdelsdtZETChRcfCEczzwSrry_Fly9MY4s3rbk,1681
100
- pycityagent-2.0.0a57.dist-info/RECORD,,
101
- pycityagent-2.0.0a57.dist-info/LICENSE,sha256=n2HPXiupinpyHMnIkbCf3OTYd3KMqbmldu1e7av0CAU,1084
102
- pycityagent-2.0.0a57.dist-info/WHEEL,sha256=ezfKMaDztqf77C8lvQ0NCnZxkTaOaKLprqJ8q932MhU,109
103
- pycityagent-2.0.0a57.dist-info/entry_points.txt,sha256=BZcne49AAIFv-hawxGnPbblea7X3MtAtoPyDX8L4OC4,132
104
- pycityagent-2.0.0a57.dist-info/top_level.txt,sha256=yOmeu6cSXmiUtScu53a3s0p7BGtLMaV0aff83EHCTic,43
105
- pycityagent-2.0.0a57.dist-info/METADATA,sha256=yn1syqalcmvcSdrXMOmcXa7Cj3-IHDE4AIL20UvDaEs,9110
100
+ pycityagent-2.0.0a59.dist-info/RECORD,,
101
+ pycityagent-2.0.0a59.dist-info/LICENSE,sha256=n2HPXiupinpyHMnIkbCf3OTYd3KMqbmldu1e7av0CAU,1084
102
+ pycityagent-2.0.0a59.dist-info/WHEEL,sha256=ezfKMaDztqf77C8lvQ0NCnZxkTaOaKLprqJ8q932MhU,109
103
+ pycityagent-2.0.0a59.dist-info/entry_points.txt,sha256=BZcne49AAIFv-hawxGnPbblea7X3MtAtoPyDX8L4OC4,132
104
+ pycityagent-2.0.0a59.dist-info/top_level.txt,sha256=yOmeu6cSXmiUtScu53a3s0p7BGtLMaV0aff83EHCTic,43
105
+ pycityagent-2.0.0a59.dist-info/METADATA,sha256=RMs-AgvrIOTGBqaoqMei0bF2eJRRqIwQn-r3UGBzwr0,9110