pycityagent 2.0.0a52__cp39-cp39-macosx_11_0_arm64.whl → 2.0.0a54__cp39-cp39-macosx_11_0_arm64.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. pycityagent/agent/agent.py +83 -62
  2. pycityagent/agent/agent_base.py +81 -54
  3. pycityagent/cityagent/bankagent.py +5 -7
  4. pycityagent/cityagent/blocks/__init__.py +0 -2
  5. pycityagent/cityagent/blocks/cognition_block.py +149 -172
  6. pycityagent/cityagent/blocks/economy_block.py +90 -129
  7. pycityagent/cityagent/blocks/mobility_block.py +56 -29
  8. pycityagent/cityagent/blocks/needs_block.py +163 -145
  9. pycityagent/cityagent/blocks/other_block.py +17 -9
  10. pycityagent/cityagent/blocks/plan_block.py +45 -57
  11. pycityagent/cityagent/blocks/social_block.py +70 -51
  12. pycityagent/cityagent/blocks/utils.py +2 -0
  13. pycityagent/cityagent/firmagent.py +6 -7
  14. pycityagent/cityagent/governmentagent.py +7 -9
  15. pycityagent/cityagent/memory_config.py +48 -48
  16. pycityagent/cityagent/message_intercept.py +99 -0
  17. pycityagent/cityagent/nbsagent.py +6 -29
  18. pycityagent/cityagent/societyagent.py +325 -127
  19. pycityagent/cli/wrapper.py +4 -0
  20. pycityagent/economy/econ_client.py +0 -2
  21. pycityagent/environment/__init__.py +7 -1
  22. pycityagent/environment/sim/client.py +10 -1
  23. pycityagent/environment/sim/clock_service.py +2 -2
  24. pycityagent/environment/sim/pause_service.py +61 -0
  25. pycityagent/environment/sim/sim_env.py +34 -46
  26. pycityagent/environment/simulator.py +18 -14
  27. pycityagent/llm/embeddings.py +0 -24
  28. pycityagent/llm/llm.py +18 -10
  29. pycityagent/memory/faiss_query.py +29 -26
  30. pycityagent/memory/memory.py +733 -247
  31. pycityagent/message/__init__.py +8 -1
  32. pycityagent/message/message_interceptor.py +322 -0
  33. pycityagent/message/messager.py +42 -11
  34. pycityagent/pycityagent-sim +0 -0
  35. pycityagent/simulation/agentgroup.py +137 -96
  36. pycityagent/simulation/simulation.py +184 -38
  37. pycityagent/simulation/storage/pg.py +2 -2
  38. pycityagent/tools/tool.py +7 -9
  39. pycityagent/utils/__init__.py +7 -2
  40. pycityagent/utils/pg_query.py +1 -0
  41. pycityagent/utils/survey_util.py +26 -23
  42. pycityagent/workflow/block.py +14 -7
  43. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/METADATA +2 -2
  44. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/RECORD +48 -46
  45. pycityagent/cityagent/blocks/time_block.py +0 -116
  46. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/LICENSE +0 -0
  47. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/WHEEL +0 -0
  48. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/entry_points.txt +0 -0
  49. {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,7 @@ from .road_service import RoadService
10
10
  from .social_service import SocialService
11
11
  from .economy_services import EconomyPersonService, EconomyOrgService
12
12
  from .light_service import LightService
13
-
13
+ from .pause_service import PauseService
14
14
  from ..utils.grpc import create_aio_channel
15
15
 
16
16
  __all__ = ["CityClient"]
@@ -44,6 +44,7 @@ class CityClient:
44
44
  self._economy_person_service = EconomyPersonService(aio_channel)
45
45
  self._economy_org_service = EconomyOrgService(aio_channel)
46
46
  self._light_service = LightService(aio_channel)
47
+ self._pause_service = PauseService(aio_channel)
47
48
 
48
49
  @staticmethod
49
50
  def from_sidecar(sidecar: OnlyClientSidecar, name: str = NAME):
@@ -61,6 +62,14 @@ class CityClient:
61
62
  """
62
63
  return self._clock_service
63
64
 
65
+ @property
66
+ def pause_service(self):
67
+ """
68
+ 模拟器暂停服务子模块
69
+ Simulator pause service submodule
70
+ """
71
+ return self._pause_service
72
+
64
73
  @property
65
74
  def lane_service(self):
66
75
  """
@@ -30,10 +30,10 @@ class ClockService:
30
30
  Getting current simulation clock
31
31
 
32
32
  Args:
33
- - req (dict): https://cityproto.sim.fiblab.net/#city.clock.v1.NowRequest
33
+ - req (dict): https://cityproto.readthedocs.io/en/latest/docs.html#nowrequest
34
34
 
35
35
  Returns:
36
- - https://cityproto.sim.fiblab.net/#city.clock.v1.NowResponse
36
+ - https://cityproto.readthedocs.io/en/latest/docs.html#nowresponse
37
37
  """
38
38
  if type(req) != clock_service.NowRequest:
39
39
  req = ParseDict(req, clock_service.NowRequest())
@@ -0,0 +1,61 @@
1
+ from collections.abc import Awaitable, Coroutine
2
+ from typing import Any, Dict, Union, cast
3
+
4
+ import grpc
5
+ from google.protobuf.json_format import ParseDict
6
+ from pycityproto.city.pause.v1 import pause_service_pb2 as pause_service
7
+ from pycityproto.city.pause.v1 import pause_service_pb2_grpc as pause_grpc
8
+
9
+ from ..utils.protobuf import async_parse
10
+
11
+ __all__ = ["PauseService"]
12
+
13
+
14
+ class PauseService:
15
+ """
16
+ 城市模拟暂停服务
17
+ City simulation pause service
18
+ """
19
+
20
+ def __init__(self, aio_channel: grpc.aio.Channel):
21
+ self._aio_stub = pause_grpc.PauseServiceStub(aio_channel)
22
+
23
+ async def pause(
24
+ self,
25
+ ) -> Coroutine[Any, Any, Union[Dict[str, Any], pause_service.PauseResponse]]:
26
+ """
27
+ 暂停模拟
28
+ Pause the simulation
29
+
30
+ Args:
31
+ - req (dict): https://cityproto.readthedocs.io/en/latest/docs.html#pauserequest
32
+
33
+ Returns:
34
+ - https://cityproto.readthedocs.io/en/latest/docs.html#pauseresponse
35
+ """
36
+ req = pause_service.PauseRequest()
37
+ res = cast(
38
+ Awaitable[pause_service.PauseResponse],
39
+ self._aio_stub.Pause(req),
40
+ )
41
+ return
42
+
43
+ async def resume(
44
+ self,
45
+ ) -> Coroutine[Any, Any, Union[Dict[str, Any], pause_service.ResumeResponse]]:
46
+ """
47
+ 恢复模拟
48
+ Resume the simulation
49
+
50
+ Args:
51
+ - req (dict): https://cityproto.readthedocs.io/en/latest/docs.html#resumerequest
52
+
53
+ Returns:
54
+ - https://cityproto.readthedocs.io/en/latest/docs.html#resumeresponse
55
+ """
56
+ req = pause_service.ResumeRequest()
57
+ res = cast(
58
+ Awaitable[pause_service.ResumeResponse],
59
+ self._aio_stub.Resume(req),
60
+ )
61
+ return
@@ -1,7 +1,8 @@
1
+ import atexit
1
2
  import logging
2
3
  import os
3
4
  import time
4
- import atexit
5
+ import warnings
5
6
  from subprocess import DEVNULL, Popen
6
7
  from typing import Optional
7
8
 
@@ -51,7 +52,7 @@ class ControlSimEnv:
51
52
  log_dir: str,
52
53
  min_step_time: int = 1000,
53
54
  timeout: int = 5,
54
- simuletgo_addr: Optional[str] = None,
55
+ sim_addr: Optional[str] = None,
55
56
  ):
56
57
  self._task_name = task_name
57
58
  self._map_file = map_file
@@ -66,47 +67,38 @@ class ControlSimEnv:
66
67
  地图数据
67
68
  """
68
69
 
69
- # 检查二进制文件是否存在
70
- # simulet-go: ~/.local/bin/simulet-go
71
- self._simuletgo_path = os.path.expanduser("~/.local/bin/simulet-go")
72
- if not os.path.exists(os.path.expanduser(self._simuletgo_path)):
73
- raise FileNotFoundError("simulet-go not found, please install it first")
74
-
75
- self._simuletgo_config = _generate_yaml_config(map_file, start_step, total_step)
76
- self.simuletgo_port = None
77
- self._simuletgo_proc = None
78
- self._traffic_client = None
79
-
70
+ self._sim_config = _generate_yaml_config(map_file, start_step, total_step)
71
+ # sim
72
+ self.sim_port = None
73
+ self._sim_proc = None
80
74
  os.makedirs(log_dir, exist_ok=True)
81
75
 
82
- self.simuletgo_addr = self.reset(simuletgo_addr)
76
+ self.sim_addr = self.reset(sim_addr)
83
77
 
84
78
  def reset(
85
79
  self,
86
- simuletgo_addr: Optional[str] = None,
80
+ sim_addr: Optional[str] = None,
87
81
  ):
88
82
  """
89
83
  Args:
90
- - simuletgo_addr: str, simulet-go的地址。如果为None,则启动一个新的simulet-go
84
+ - sim_addr: str, pycityagent-sim的地址。如果为None,则启动一个新的pycityagent-sim
91
85
  """
92
-
93
- # 三个地址必须同时为None或者同时不为None
94
- if simuletgo_addr is None:
95
- # 1. 启动simulet-go
96
- # ./simulet-go -config-data configbase64 -job test -syncer http://localhost:53001 -listen :51102
97
- assert self.simuletgo_port is None
98
- assert self._simuletgo_proc is None
99
- self.simuletgo_port = find_free_port()
100
- config_base64 = encode_to_base64(self._simuletgo_config)
101
- self._simuletgo_proc = Popen(
86
+ if sim_addr is None:
87
+ # 启动pycityagent-sim
88
+ # pycityagent-sim -config-data configbase64 -job test -listen :51102
89
+ assert self.sim_port is None
90
+ assert self._sim_proc is None
91
+ self.sim_port = find_free_port()
92
+ config_base64 = encode_to_base64(self._sim_config)
93
+ self._sim_proc = Popen(
102
94
  [
103
- self._simuletgo_path,
95
+ "pycityagent-sim",
104
96
  "-config-data",
105
97
  config_base64,
106
98
  "-job",
107
99
  self._task_name,
108
100
  "-listen",
109
- f":{self.simuletgo_port}",
101
+ f":{self.sim_port}",
110
102
  "-run.min_step_time",
111
103
  f"{self._min_step_time}",
112
104
  "-output",
@@ -120,26 +112,22 @@ class ControlSimEnv:
120
112
  # stdout=DEVNULL,
121
113
  )
122
114
  logging.info(
123
- f"start simulet-go at localhost:{self.simuletgo_port}, PID={self._simuletgo_proc.pid}"
115
+ f"start pycityagent-sim at localhost:{self.sim_port}, PID={self._sim_proc.pid}"
124
116
  )
125
- simuletgo_addr = f"http://localhost:{self.simuletgo_port}"
117
+ sim_addr = f"http://localhost:{self.sim_port}"
126
118
  atexit.register(self.close)
127
- time.sleep(1)
128
- elif simuletgo_addr is not None:
129
- pass
119
+ time.sleep(0.3)
130
120
  else:
131
- # raise ValueError(
132
- # "simuletgo_addr, syncer_addr, routing_addr must be all None or all not None"
133
- # )
134
- pass
135
- return simuletgo_addr
121
+ warnings.warn("单独启动模拟器模拟将被弃用", DeprecationWarning)
136
122
 
137
- def close(self):
138
- if self._simuletgo_proc is not None:
139
- self._simuletgo_proc.terminate()
140
- simuletgo_code = self._simuletgo_proc.wait()
141
- logging.info(f"simulet-go exit with code {simuletgo_code}")
123
+ return sim_addr
142
124
 
143
- self.simuletgo_port = None
144
- self._simuletgo_proc = None
145
- self._traffic_client = None
125
+ def close(self):
126
+ if self._sim_proc is not None:
127
+ self._sim_proc.terminate()
128
+ sim_code = self._sim_proc.wait()
129
+ logging.info(f"pycityagent-sim exit with code {sim_code}")
130
+
131
+ # sim
132
+ self.sim_port = None
133
+ self._sim_proc = None
@@ -60,11 +60,11 @@ class Simulator:
60
60
  total_step=2147000000,
61
61
  log_dir=config["simulator"].get("log_dir", "./log"),
62
62
  min_step_time=config["simulator"].get("min_step_time", 1000),
63
- simuletgo_addr=config["simulator"].get("server", None),
63
+ sim_addr=config["simulator"].get("server", None),
64
64
  )
65
65
 
66
66
  # using local client
67
- self._client = CityClient(sim_env.simuletgo_addr, secure=False)
67
+ self._client = CityClient(sim_env.sim_addr, secure=False)
68
68
  """
69
69
  - 模拟器grpc客户端
70
70
  - grpc client of simulator
@@ -162,35 +162,39 @@ class Simulator:
162
162
  Returns:
163
163
  - time Union[int, str]: 时间 time in second(int) or formatted time(str)
164
164
  """
165
- t_sec = await self._client.clock_service.Now({})
166
- t_sec = cast(dict[str, int], t_sec)
167
- self.time = t_sec["t"]
165
+ now = await self._client.clock_service.Now({})
166
+ now = cast(dict[str, int], now)
167
+ self.time = now["t"]
168
168
  if format_time:
169
169
  current_date = datetime.now().date()
170
170
  start_of_day = datetime.combine(current_date, datetime.min.time())
171
- current_time = start_of_day + timedelta(seconds=t_sec["t"])
171
+ current_time = start_of_day + timedelta(seconds=now["t"])
172
172
  formatted_time = current_time.strftime(format)
173
173
  return formatted_time
174
174
  else:
175
- # BUG: 返回的time是float类型
176
- return t_sec["t"]
175
+ return int(now["t"])
176
+ async def pause(self):
177
+ await self._client.pause_service.pause()
178
+
179
+ async def resume(self):
180
+ await self._client.pause_service.resume()
177
181
 
178
182
  async def get_simulator_day(self) -> int:
179
183
  """
180
184
  获取模拟器到第几日
181
185
  """
182
- t_sec = await self._client.clock_service.Now({})
183
- t_sec = cast(dict[str, int], t_sec)
184
- day = t_sec["t"] // 86400
186
+ now = await self._client.clock_service.Now({})
187
+ now = cast(dict[str, int], now)
188
+ day = now["day"]
185
189
  return day
186
190
 
187
191
  async def get_simulator_second_from_start_of_day(self) -> int:
188
192
  """
189
193
  获取模拟器从00:00:00到当前的秒数
190
194
  """
191
- t_sec = await self._client.clock_service.Now({})
192
- t_sec = cast(dict[str, int], t_sec)
193
- return t_sec["t"] % 86400
195
+ now = await self._client.clock_service.Now({})
196
+ now = cast(dict[str, int], now)
197
+ return now["t"] % 86400
194
198
 
195
199
  async def get_person(self, person_id: int) -> dict:
196
200
  return await self._client.person_service.GetPerson(
@@ -196,30 +196,6 @@ class SimpleEmbedding(Embeddings):
196
196
  """Embed query text."""
197
197
  return self._embed(text)
198
198
 
199
- # def save(self, file_path: str):
200
- # """保存模型"""
201
- # state = {
202
- # "vector_dim": self.vector_dim,
203
- # "cache_size": self.cache_size,
204
- # "vocab": self._vocab,
205
- # "idf": self._idf,
206
- # "doc_count": self._doc_count,
207
- # }
208
- # with open(file_path, "w") as f:
209
- # json.dump(state, f)
210
-
211
- # def load(self, file_path: str):
212
- # """加载模型"""
213
- # with open(file_path, "r") as f:
214
- # state = json.load(f)
215
- # self.vector_dim = state["vector_dim"]
216
- # self.cache_size = state["cache_size"]
217
- # self._vocab = state["vocab"]
218
- # self._idf = state["idf"]
219
- # self._doc_count = state["doc_count"]
220
- # self._cache = {} # 清空缓存
221
-
222
-
223
199
  if __name__ == "__main__":
224
200
  # se = SentenceEmbedding(
225
201
  # pretrained_model_name_or_path="ignore/BAAI--bge-m3", cache_dir="ignore"
pycityagent/llm/llm.py CHANGED
@@ -1,25 +1,27 @@
1
1
  """UrbanLLM: 智能能力类及其定义"""
2
2
 
3
3
  import json
4
- from openai import OpenAI, AsyncOpenAI, APIConnectionError, OpenAIError
5
- from zhipuai import ZhipuAI
6
4
  import logging
7
5
 
6
+ from openai import APIConnectionError, AsyncOpenAI, OpenAI, OpenAIError
7
+ from zhipuai import ZhipuAI
8
+
8
9
  logging.getLogger("zhipuai").setLevel(logging.WARNING)
9
10
 
10
11
  import asyncio
12
+ import os
11
13
  from http import HTTPStatus
14
+ from io import BytesIO
15
+ from typing import Any, Optional, Union
16
+
12
17
  import dashscope
13
18
  import requests
14
19
  from dashscope import ImageSynthesis
15
20
  from PIL import Image
16
- from io import BytesIO
17
- from typing import Any, Optional, Union
21
+
18
22
  from .llmconfig import *
19
23
  from .utils import *
20
24
 
21
- import os
22
-
23
25
  os.environ["GRPC_VERBOSITY"] = "ERROR"
24
26
 
25
27
 
@@ -38,13 +40,13 @@ class LLM:
38
40
  self.request_number = 0
39
41
  self.semaphore = None
40
42
  self._current_client_index = 0
41
-
43
+
42
44
  api_keys = self.config.text["api_key"]
43
45
  if not isinstance(api_keys, list):
44
46
  api_keys = [api_keys]
45
-
47
+
46
48
  self._aclients = []
47
-
49
+
48
50
  for api_key in api_keys:
49
51
  if self.config.text["request_type"] == "openai":
50
52
  client = AsyncOpenAI(api_key=api_key, timeout=300)
@@ -62,6 +64,10 @@ class LLM:
62
64
  )
63
65
  elif self.config.text["request_type"] == "zhipuai":
64
66
  client = ZhipuAI(api_key=api_key, timeout=300)
67
+ else:
68
+ raise ValueError(
69
+ f"Unsupported `request_type` {self.config.text['request_type']}!"
70
+ )
65
71
  self._aclients.append(client)
66
72
 
67
73
  def set_semaphore(self, number_of_coroutine: int):
@@ -118,7 +124,9 @@ Token Usage:
118
124
  def _get_next_client(self):
119
125
  """获取下一个要使用的客户端"""
120
126
  client = self._aclients[self._current_client_index]
121
- self._current_client_index = (self._current_client_index + 1) % len(self._aclients)
127
+ self._current_client_index = (self._current_client_index + 1) % len(
128
+ self._aclients
129
+ )
122
130
  return client
123
131
 
124
132
  async def atext_request(
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import warnings
2
3
  from collections.abc import Sequence
3
4
  from typing import Any, Literal, Optional, Union
4
5
 
@@ -116,32 +117,34 @@ class FaissQuery:
116
117
  }
117
118
  if filter is not None:
118
119
  _filter.update(filter)
119
- if return_score_type == "L2-distance":
120
- _result = await self.vectors_store.asimilarity_search_with_score(
121
- query=query,
122
- k=k,
123
- filter=_filter,
124
- fetch_k=fetch_k,
125
- )
126
- return [(r.page_content, s, r.metadata) for r, s in _result]
127
- elif return_score_type == "none":
128
- _result = await self.vectors_store.asimilarity_search(
129
- query=query,
130
- k=k,
131
- filter=_filter,
132
- fetch_k=fetch_k,
133
- )
134
- return [(r.page_content, r.metadata) for r in _result]
135
- elif return_score_type == "similarity_score":
136
- _result = await self.vectors_store.asimilarity_search_with_relevance_scores(
137
- query=query,
138
- k=k,
139
- filter=_filter,
140
- fetch_k=fetch_k,
141
- )
142
- return [(r.page_content, s, r.metadata) for r, s in _result]
143
- else:
144
- raise ValueError(f"Invalid `return_score_type` {return_score_type}!")
120
+ with warnings.catch_warnings():
121
+ warnings.simplefilter("ignore")
122
+ if return_score_type == "L2-distance":
123
+ _result = await self.vectors_store.asimilarity_search_with_score(
124
+ query=query,
125
+ k=k,
126
+ filter=_filter,
127
+ fetch_k=fetch_k,
128
+ )
129
+ return [(r.page_content, s, r.metadata) for r, s in _result]
130
+ elif return_score_type == "none":
131
+ _result = await self.vectors_store.asimilarity_search(
132
+ query=query,
133
+ k=k,
134
+ filter=_filter,
135
+ fetch_k=fetch_k,
136
+ )
137
+ return [(r.page_content, r.metadata) for r in _result]
138
+ elif return_score_type == "similarity_score":
139
+ _result = await self.vectors_store.asimilarity_search_with_relevance_scores(
140
+ query=query,
141
+ k=k,
142
+ filter=_filter,
143
+ fetch_k=fetch_k,
144
+ )
145
+ return [(r.page_content, s, r.metadata) for r, s in _result]
146
+ else:
147
+ raise ValueError(f"Invalid `return_score_type` {return_score_type}!")
145
148
 
146
149
  @lock_decorator
147
150
  async def similarity_search_by_embedding(