pycityagent 2.0.0a14__tar.gz → 2.0.0a15__tar.gz

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.
Files changed (71) hide show
  1. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/PKG-INFO +1 -1
  2. pycityagent-2.0.0a15/pycityagent/__init__.py +22 -0
  3. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/agent.py +44 -31
  4. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/simulator.py +5 -4
  5. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/memory/memory.py +8 -7
  6. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/memory/memory_base.py +6 -4
  7. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/message/messager.py +8 -7
  8. pycityagent-2.0.0a15/pycityagent/simulation/agentgroup.py +273 -0
  9. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/simulation/simulation.py +157 -78
  10. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/utils/__init__.py +2 -2
  11. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/utils/avro_schema.py +26 -1
  12. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/workflow/tool.py +0 -3
  13. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pyproject.toml +1 -1
  14. pycityagent-2.0.0a14/pycityagent/__init__.py +0 -8
  15. pycityagent-2.0.0a14/pycityagent/simulation/agentgroup.py +0 -189
  16. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/README.md +0 -0
  17. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/economy/__init__.py +0 -0
  18. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/economy/econ_client.py +0 -0
  19. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/__init__.py +0 -0
  20. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/interact/__init__.py +0 -0
  21. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/interact/interact.py +0 -0
  22. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/message/__init__.py +0 -0
  23. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sence/__init__.py +0 -0
  24. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sence/static.py +0 -0
  25. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sidecar/__init__.py +0 -0
  26. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sidecar/sidecarv2.py +0 -0
  27. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/__init__.py +0 -0
  28. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/aoi_service.py +0 -0
  29. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/client.py +0 -0
  30. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/clock_service.py +0 -0
  31. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/economy_services.py +0 -0
  32. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/lane_service.py +0 -0
  33. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/light_service.py +0 -0
  34. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/person_service.py +0 -0
  35. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/road_service.py +0 -0
  36. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/sim_env.py +0 -0
  37. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/sim/social_service.py +0 -0
  38. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/utils/__init__.py +0 -0
  39. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/utils/base64.py +0 -0
  40. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/utils/const.py +0 -0
  41. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/utils/geojson.py +0 -0
  42. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/utils/grpc.py +0 -0
  43. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/utils/map_utils.py +0 -0
  44. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/utils/port.py +0 -0
  45. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/environment/utils/protobuf.py +0 -0
  46. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/llm/__init__.py +0 -0
  47. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/llm/embedding.py +0 -0
  48. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/llm/llm.py +0 -0
  49. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/llm/llmconfig.py +0 -0
  50. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/llm/utils.py +0 -0
  51. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/memory/__init__.py +0 -0
  52. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/memory/const.py +0 -0
  53. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/memory/profile.py +0 -0
  54. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/memory/self_define.py +0 -0
  55. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/memory/state.py +0 -0
  56. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/memory/utils.py +0 -0
  57. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/message/__init__.py +0 -0
  58. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/simulation/__init__.py +0 -0
  59. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/survey/__init__.py +0 -0
  60. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/survey/manager.py +0 -0
  61. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/survey/models.py +0 -0
  62. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/utils/decorators.py +0 -0
  63. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/utils/parsers/__init__.py +0 -0
  64. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/utils/parsers/code_block_parser.py +0 -0
  65. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/utils/parsers/json_parser.py +0 -0
  66. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/utils/parsers/parser_base.py +0 -0
  67. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/utils/survey_util.py +0 -0
  68. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/workflow/__init__.py +0 -0
  69. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/workflow/block.py +0 -0
  70. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/workflow/prompt.py +0 -0
  71. {pycityagent-2.0.0a14 → pycityagent-2.0.0a15}/pycityagent/workflow/trigger.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pycityagent
3
- Version: 2.0.0a14
3
+ Version: 2.0.0a15
4
4
  Summary: LLM-based城市环境agent构建库
5
5
  License: MIT
6
6
  Author: Yuwei Yan
@@ -0,0 +1,22 @@
1
+ """
2
+ Pycityagent: 城市智能体构建框架
3
+ """
4
+
5
+ from .agent import Agent, CitizenAgent, InstitutionAgent
6
+ from .environment import Simulator
7
+ import logging
8
+
9
+ # 创建一个 pycityagent 记录器
10
+ logger = logging.getLogger("pycityagent")
11
+ logger.setLevel(logging.WARNING) # 默认级别
12
+
13
+ # 如果没有处理器,则添加一个
14
+ if not logger.hasHandlers():
15
+ handler = logging.StreamHandler()
16
+ formatter = logging.Formatter(
17
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
18
+ )
19
+ handler.setFormatter(formatter)
20
+ logger.addHandler(handler)
21
+
22
+ __all__ = ["Agent", "Simulator", "CitizenAgent", "InstitutionAgent"]
@@ -2,11 +2,9 @@
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  import asyncio
5
- import json
6
5
  from uuid import UUID
7
6
  from copy import deepcopy
8
7
  from datetime import datetime
9
- import time
10
8
  from enum import Enum
11
9
  import logging
12
10
  import random
@@ -28,6 +26,8 @@ from .environment import Simulator
28
26
  from .llm import LLM
29
27
  from .memory import Memory
30
28
 
29
+ logger = logging.getLogger("pycityagent")
30
+
31
31
 
32
32
  class AgentType(Enum):
33
33
  """
@@ -73,7 +73,7 @@ class Agent(ABC):
73
73
  """
74
74
  self._name = name
75
75
  self._type = type
76
- self._uuid = uuid.uuid4()
76
+ self._uuid = str(uuid.uuid4())
77
77
  self._llm_client = llm_client
78
78
  self._economy_client = economy_client
79
79
  self._messager = messager
@@ -123,12 +123,18 @@ class Agent(ABC):
123
123
  """
124
124
  self._memory = memory
125
125
 
126
- def set_exp_id(self, exp_id: str|UUID):
126
+ def set_exp_id(self, exp_id: str):
127
127
  """
128
128
  Set the exp_id of the agent.
129
129
  """
130
130
  self._exp_id = exp_id
131
131
 
132
+ def set_avro_file(self, avro_file: Dict[str, str]):
133
+ """
134
+ Set the avro file of the agent.
135
+ """
136
+ self._avro_file = avro_file
137
+
132
138
  @property
133
139
  def uuid(self):
134
140
  """The Agent's UUID"""
@@ -214,8 +220,10 @@ class Agent(ABC):
214
220
 
215
221
  async def _process_survey(self, survey: dict):
216
222
  survey_response = await self.generate_user_survey_response(survey)
223
+ if self._avro_file is None:
224
+ return
217
225
  response_to_avro = [{
218
- "id": str(self._uuid),
226
+ "id": self._uuid,
219
227
  "day": await self._simulator.get_simulator_day(),
220
228
  "t": await self._simulator.get_simulator_second_from_start_of_day(),
221
229
  "survey_id": survey["id"],
@@ -264,7 +272,7 @@ class Agent(ABC):
264
272
 
265
273
  async def _process_interview(self, payload: dict):
266
274
  auros = [{
267
- "id": str(self._uuid),
275
+ "id": self._uuid,
268
276
  "day": await self._simulator.get_simulator_day(),
269
277
  "t": await self._simulator.get_simulator_second_from_start_of_day(),
270
278
  "type": 2,
@@ -275,7 +283,7 @@ class Agent(ABC):
275
283
  question = payload["content"]
276
284
  response = await self.generate_user_chat_response(question)
277
285
  auros.append({
278
- "id": str(self._uuid),
286
+ "id": self._uuid,
279
287
  "day": await self._simulator.get_simulator_day(),
280
288
  "t": await self._simulator.get_simulator_second_from_start_of_day(),
281
289
  "type": 2,
@@ -283,15 +291,17 @@ class Agent(ABC):
283
291
  "content": response,
284
292
  "created_at": int(datetime.now().timestamp() * 1000),
285
293
  })
294
+ if self._avro_file is None:
295
+ return
286
296
  with open(self._avro_file["dialog"], "a+b") as f:
287
297
  fastavro.writer(f, DIALOG_SCHEMA, auros, codec="snappy")
288
298
 
289
299
  async def process_agent_chat_response(self, payload: dict) -> str:
290
- logging.info(f"Agent {self._uuid} received agent chat response: {payload}")
300
+ logger.info(f"Agent {self._uuid} received agent chat response: {payload}")
291
301
 
292
302
  async def _process_agent_chat(self, payload: dict):
293
303
  auros = [{
294
- "id": str(self._uuid),
304
+ "id": self._uuid,
295
305
  "day": payload["day"],
296
306
  "t": payload["t"],
297
307
  "type": 1,
@@ -300,6 +310,8 @@ class Agent(ABC):
300
310
  "created_at": int(datetime.now().timestamp() * 1000),
301
311
  }]
302
312
  asyncio.create_task(self.process_agent_chat_response(payload))
313
+ if self._avro_file is None:
314
+ return
303
315
  with open(self._avro_file["dialog"], "a+b") as f:
304
316
  fastavro.writer(f, DIALOG_SCHEMA, auros, codec="snappy")
305
317
 
@@ -307,19 +319,19 @@ class Agent(ABC):
307
319
  async def handle_agent_chat_message(self, payload: dict):
308
320
  """处理收到的消息,识别发送者"""
309
321
  # 从消息中解析发送者 ID 和消息内容
310
- logging.info(f"Agent {self._uuid} received agent chat message: {payload}")
322
+ logger.info(f"Agent {self._uuid} received agent chat message: {payload}")
311
323
  asyncio.create_task(self._process_agent_chat(payload))
312
324
 
313
325
  async def handle_user_chat_message(self, payload: dict):
314
326
  """处理收到的消息,识别发送者"""
315
327
  # 从消息中解析发送者 ID 和消息内容
316
- logging.info(f"Agent {self._uuid} received user chat message: {payload}")
328
+ logger.info(f"Agent {self._uuid} received user chat message: {payload}")
317
329
  asyncio.create_task(self._process_interview(payload))
318
330
 
319
331
  async def handle_user_survey_message(self, payload: dict):
320
332
  """处理收到的消息,识别发送者"""
321
333
  # 从消息中解析发送者 ID 和消息内容
322
- logging.info(f"Agent {self._uuid} received user survey message: {payload}")
334
+ logger.info(f"Agent {self._uuid} received user survey message: {payload}")
323
335
  asyncio.create_task(self._process_survey(payload["data"]))
324
336
 
325
337
  async def handle_gather_message(self, payload: str):
@@ -327,7 +339,7 @@ class Agent(ABC):
327
339
 
328
340
  # MQTT send message
329
341
  async def _send_message(
330
- self, to_agent_uuid: UUID, payload: dict, sub_topic: str
342
+ self, to_agent_uuid: str, payload: dict, sub_topic: str
331
343
  ):
332
344
  """通过 Messager 发送消息"""
333
345
  if self._messager is None:
@@ -336,7 +348,7 @@ class Agent(ABC):
336
348
  await self._messager.send_message(topic, payload)
337
349
 
338
350
  async def send_message_to_agent(
339
- self, to_agent_uuid: UUID, content: str
351
+ self, to_agent_uuid: str, content: str
340
352
  ):
341
353
  """通过 Messager 发送消息"""
342
354
  if self._messager is None:
@@ -350,14 +362,16 @@ class Agent(ABC):
350
362
  }
351
363
  await self._send_message(to_agent_uuid, payload, "agent-chat")
352
364
  auros = [{
353
- "id": str(self._uuid),
365
+ "id": self._uuid,
354
366
  "day": await self._simulator.get_simulator_day(),
355
367
  "t": await self._simulator.get_simulator_second_from_start_of_day(),
356
368
  "type": 1,
357
- "speaker": str(self._uuid),
369
+ "speaker": self._uuid,
358
370
  "content": content,
359
371
  "created_at": int(datetime.now().timestamp() * 1000),
360
372
  }]
373
+ if self._avro_file is None:
374
+ return
361
375
  with open(self._avro_file["dialog"], "a+b") as f:
362
376
  fastavro.writer(f, DIALOG_SCHEMA, auros, codec="snappy")
363
377
 
@@ -414,7 +428,7 @@ class CitizenAgent(Agent):
414
428
  person_template (dict, optional): The person template in dict format. Defaults to PersonService.default_dict_person().
415
429
  """
416
430
  if self._simulator is None:
417
- logging.warning("Simulator is not set")
431
+ logger.warning("Simulator is not set")
418
432
  return
419
433
  if not self._has_bound_to_simulator:
420
434
  FROM_MEMORY_KEYS = {
@@ -432,7 +446,7 @@ class CitizenAgent(Agent):
432
446
  # ATTENTION:模拟器分配的id从0开始
433
447
  if person_id >= 0:
434
448
  await simulator.get_person(person_id)
435
- logging.debug(f"Binding to Person `{person_id}` already in Simulator")
449
+ logger.debug(f"Binding to Person `{person_id}` already in Simulator")
436
450
  else:
437
451
  dict_person = deepcopy(self._person_template)
438
452
  for _key in FROM_MEMORY_KEYS:
@@ -447,7 +461,7 @@ class CitizenAgent(Agent):
447
461
  )
448
462
  person_id = resp["person_id"]
449
463
  await memory.update("id", person_id, protect_llm_read_only_fields=False)
450
- logging.debug(
464
+ logger.debug(
451
465
  f"Binding to Person `{person_id}` just added to Simulator"
452
466
  )
453
467
  # 防止模拟器还没有到prepare阶段导致get_person出错
@@ -456,7 +470,7 @@ class CitizenAgent(Agent):
456
470
 
457
471
  async def _bind_to_economy(self):
458
472
  if self._economy_client is None:
459
- logging.warning("Economy client is not set")
473
+ logger.warning("Economy client is not set")
460
474
  return
461
475
  if not self._has_bound_to_economy:
462
476
  if self._has_bound_to_simulator:
@@ -473,7 +487,7 @@ class CitizenAgent(Agent):
473
487
  )
474
488
  self._has_bound_to_economy = True
475
489
  else:
476
- logging.debug(
490
+ logger.debug(
477
491
  f"Binding to Economy before binding to Simulator, skip binding to Economy Simulator"
478
492
  )
479
493
 
@@ -523,7 +537,7 @@ class InstitutionAgent(Agent):
523
537
 
524
538
  async def _bind_to_economy(self):
525
539
  if self._economy_client is None:
526
- logging.debug("Economy client is not set")
540
+ logger.debug("Economy client is not set")
527
541
  return
528
542
  if not self._has_bound_to_economy:
529
543
  # TODO: More general id generation
@@ -599,7 +613,7 @@ class InstitutionAgent(Agent):
599
613
  }
600
614
  )
601
615
  except Exception as e:
602
- logging.error(f"Failed to bind to Economy: {e}")
616
+ logger.error(f"Failed to bind to Economy: {e}")
603
617
  self._has_bound_to_economy = True
604
618
 
605
619
  async def handle_gather_message(self, payload: dict):
@@ -615,11 +629,11 @@ class InstitutionAgent(Agent):
615
629
  "content": content,
616
630
  })
617
631
 
618
- async def gather_messages(self, agent_ids: list[UUID], target: str) -> List[dict]:
632
+ async def gather_messages(self, agent_uuids: list[str], target: str) -> List[dict]:
619
633
  """从多个智能体收集消息
620
634
 
621
635
  Args:
622
- agent_ids: 目标智能体ID列表
636
+ agent_uuids: 目标智能体UUID列表
623
637
  target: 要收集的信息类型
624
638
 
625
639
  Returns:
@@ -627,18 +641,17 @@ class InstitutionAgent(Agent):
627
641
  """
628
642
  # 为每个agent创建Future
629
643
  futures = {}
630
- for agent_id in agent_ids:
631
- response_key = str(agent_id)
632
- futures[response_key] = asyncio.Future()
633
- self._gather_responses[response_key] = futures[response_key]
644
+ for agent_uuid in agent_uuids:
645
+ futures[agent_uuid] = asyncio.Future()
646
+ self._gather_responses[agent_uuid] = futures[agent_uuid]
634
647
 
635
648
  # 发送gather请求
636
649
  payload = {
637
650
  "from": self._uuid,
638
651
  "target": target,
639
652
  }
640
- for agent_id in agent_ids:
641
- await self._send_message(agent_id, payload, "gather")
653
+ for agent_uuid in agent_uuids:
654
+ await self._send_message(agent_uuid, payload, "gather")
642
655
 
643
656
  try:
644
657
  # 等待所有响应
@@ -20,6 +20,7 @@ from shapely.strtree import STRtree
20
20
  from .sim import CityClient, ControlSimEnv
21
21
  from .utils.const import *
22
22
 
23
+ logger = logging.getLogger("pycityagent")
23
24
 
24
25
  class Simulator:
25
26
  """
@@ -72,7 +73,7 @@ class Simulator:
72
73
  else:
73
74
  self._client = CityClient(config["simulator"]["server"], secure=False)
74
75
  else:
75
- logging.warning(
76
+ logger.warning(
76
77
  "No simulator config found, no simulator client will be used"
77
78
  )
78
79
  self.map = SimMap(
@@ -285,7 +286,7 @@ class Simulator:
285
286
  reset_position["aoi_position"] = {"aoi_id": aoi_id}
286
287
  if poi_id is not None:
287
288
  reset_position["aoi_position"]["poi_id"] = poi_id
288
- logging.debug(
289
+ logger.debug(
289
290
  f"Setting person {person_id} pos to AoiPosition {reset_position}"
290
291
  )
291
292
  await self._client.person_service.ResetPersonPosition(
@@ -298,14 +299,14 @@ class Simulator:
298
299
  }
299
300
  if s is not None:
300
301
  reset_position["lane_position"]["s"] = s
301
- logging.debug(
302
+ logger.debug(
302
303
  f"Setting person {person_id} pos to LanePosition {reset_position}"
303
304
  )
304
305
  await self._client.person_service.ResetPersonPosition(
305
306
  {"person_id": person_id, "position": reset_position}
306
307
  )
307
308
  else:
308
- logging.debug(
309
+ logger.debug(
309
310
  f"Neither aoi or lane pos provided for person {person_id} position reset!!"
310
311
  )
311
312
 
@@ -13,6 +13,7 @@ from .profile import ProfileMemory
13
13
  from .self_define import DynamicMemory
14
14
  from .state import StateMemory
15
15
 
16
+ logger = logging.getLogger("pycityagent")
16
17
 
17
18
  class Memory:
18
19
  """
@@ -83,7 +84,7 @@ class Memory:
83
84
  _type.extend(_value)
84
85
  _value = deepcopy(_type)
85
86
  else:
86
- logging.warning(f"type `{_type}` is not supported!")
87
+ logger.warning(f"type `{_type}` is not supported!")
87
88
  pass
88
89
  except TypeError as e:
89
90
  pass
@@ -99,7 +100,7 @@ class Memory:
99
100
  or k in STATE_ATTRIBUTES
100
101
  or k == TIME_STAMP_KEY
101
102
  ):
102
- logging.warning(f"key `{k}` already declared in memory!")
103
+ logger.warning(f"key `{k}` already declared in memory!")
103
104
  continue
104
105
 
105
106
  _dynamic_config[k] = deepcopy(_value)
@@ -112,19 +113,19 @@ class Memory:
112
113
  if profile is not None:
113
114
  for k, v in profile.items():
114
115
  if k not in PROFILE_ATTRIBUTES:
115
- logging.warning(f"key `{k}` is not a correct `profile` field!")
116
+ logger.warning(f"key `{k}` is not a correct `profile` field!")
116
117
  continue
117
118
  _profile_config[k] = v
118
119
  if motion is not None:
119
120
  for k, v in motion.items():
120
121
  if k not in STATE_ATTRIBUTES:
121
- logging.warning(f"key `{k}` is not a correct `motion` field!")
122
+ logger.warning(f"key `{k}` is not a correct `motion` field!")
122
123
  continue
123
124
  _state_config[k] = v
124
125
  if base is not None:
125
126
  for k, v in base.items():
126
127
  if k not in STATE_ATTRIBUTES:
127
- logging.warning(f"key `{k}` is not a correct `base` field!")
128
+ logger.warning(f"key `{k}` is not a correct `base` field!")
128
129
  continue
129
130
  _state_config[k] = v
130
131
  self._state = StateMemory(
@@ -182,7 +183,7 @@ class Memory:
182
183
  """更新记忆值并在必要时更新embedding"""
183
184
  if protect_llm_read_only_fields:
184
185
  if any(key in _attrs for _attrs in [STATE_ATTRIBUTES]):
185
- logging.warning(f"Trying to write protected key `{key}`!")
186
+ logger.warning(f"Trying to write protected key `{key}`!")
186
187
  return
187
188
  for _mem in [self._state, self._profile, self._dynamic]:
188
189
  try:
@@ -208,7 +209,7 @@ class Memory:
208
209
  elif isinstance(original_value, deque):
209
210
  original_value.extend(deque(value))
210
211
  else:
211
- logging.debug(
212
+ logger.debug(
212
213
  f"Type of {type(original_value)} does not support mode `merge`, using `replace` instead!"
213
214
  )
214
215
  await _mem.update(key, value, store_snapshot)
@@ -10,6 +10,8 @@ from typing import Any, Callable, Dict, Optional, Sequence, Union
10
10
 
11
11
  from .const import *
12
12
 
13
+ logger = logging.getLogger("pycityagent")
14
+
13
15
 
14
16
  class MemoryUnit:
15
17
  def __init__(
@@ -57,7 +59,7 @@ class MemoryUnit:
57
59
  orig_v = self._content[k]
58
60
  orig_type, new_type = type(orig_v), type(v)
59
61
  if not orig_type == new_type:
60
- logging.debug(
62
+ logger.debug(
61
63
  f"Type warning: The type of the value for key '{k}' is changing from `{orig_type.__name__}` to `{new_type.__name__}`!"
62
64
  )
63
65
  self._content.update(content)
@@ -82,7 +84,7 @@ class MemoryUnit:
82
84
  await self._lock.acquire()
83
85
  values = self._content[key]
84
86
  if not isinstance(values, Sequence):
85
- logging.warning(
87
+ logger.warning(
86
88
  f"the value stored in key `{key}` is not `sequence`, return value `{values}` instead!"
87
89
  )
88
90
  return values
@@ -93,7 +95,7 @@ class MemoryUnit:
93
95
  )
94
96
  top_k = len(values) if top_k is None else top_k
95
97
  if len(_sorted_values_with_idx) < top_k:
96
- logging.debug(
98
+ logger.debug(
97
99
  f"Length of values {len(_sorted_values_with_idx)} is less than top_k {top_k}, returning all values."
98
100
  )
99
101
  self._lock.release()
@@ -149,7 +151,7 @@ class MemoryBase(ABC):
149
151
  if recent_n is None:
150
152
  return _list_units
151
153
  if len(_memories) < recent_n:
152
- logging.debug(
154
+ logger.debug(
153
155
  f"Length of memory {len(_memories)} is less than recent_n {recent_n}, returning all available memories."
154
156
  )
155
157
  return _list_units[-recent_n:]
@@ -5,6 +5,7 @@ import logging
5
5
  import math
6
6
  from aiomqtt import Client
7
7
 
8
+ logger = logging.getLogger("pycityagent")
8
9
 
9
10
  class Messager:
10
11
  def __init__(
@@ -21,15 +22,15 @@ class Messager:
21
22
  try:
22
23
  await self.client.__aenter__()
23
24
  self.connected = True
24
- logging.info("Connected to MQTT Broker")
25
+ logger.info("Connected to MQTT Broker")
25
26
  except Exception as e:
26
27
  self.connected = False
27
- logging.error(f"Failed to connect to MQTT Broker: {e}")
28
+ logger.error(f"Failed to connect to MQTT Broker: {e}")
28
29
 
29
30
  async def disconnect(self):
30
31
  await self.client.__aexit__(None, None, None)
31
32
  self.connected = False
32
- logging.info("Disconnected from MQTT Broker")
33
+ logger.info("Disconnected from MQTT Broker")
33
34
 
34
35
  def is_connected(self):
35
36
  """检查是否成功连接到 Broker"""
@@ -37,13 +38,13 @@ class Messager:
37
38
 
38
39
  async def subscribe(self, topic, agent):
39
40
  if not self.is_connected():
40
- logging.error(
41
+ logger.error(
41
42
  f"Cannot subscribe to {topic} because not connected to the Broker."
42
43
  )
43
44
  return
44
45
  await self.client.subscribe(topic)
45
46
  self.subscribers[topic] = agent
46
- logging.info(f"Subscribed to {topic} for Agent {agent._uuid}")
47
+ logger.info(f"Subscribed to {topic} for Agent {agent._uuid}")
47
48
 
48
49
  async def receive_messages(self):
49
50
  """监听并将消息存入队列"""
@@ -61,11 +62,11 @@ class Messager:
61
62
  """通过 Messager 发送消息"""
62
63
  message = json.dumps(payload, default=str)
63
64
  await self.client.publish(topic, message)
64
- logging.info(f"Message sent to {topic}: {message}")
65
+ logger.info(f"Message sent to {topic}: {message}")
65
66
 
66
67
  async def start_listening(self):
67
68
  """启动消息监听任务"""
68
69
  if self.is_connected():
69
70
  asyncio.create_task(self.receive_messages())
70
71
  else:
71
- logging.error("Cannot start listening because not connected to the Broker.")
72
+ logger.error("Cannot start listening because not connected to the Broker.")