pycityagent 2.0.0a52__cp311-cp311-macosx_11_0_arm64.whl → 2.0.0a54__cp311-cp311-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.
- pycityagent/agent/agent.py +83 -62
- pycityagent/agent/agent_base.py +81 -54
- pycityagent/cityagent/bankagent.py +5 -7
- pycityagent/cityagent/blocks/__init__.py +0 -2
- pycityagent/cityagent/blocks/cognition_block.py +149 -172
- pycityagent/cityagent/blocks/economy_block.py +90 -129
- pycityagent/cityagent/blocks/mobility_block.py +56 -29
- pycityagent/cityagent/blocks/needs_block.py +163 -145
- pycityagent/cityagent/blocks/other_block.py +17 -9
- pycityagent/cityagent/blocks/plan_block.py +45 -57
- pycityagent/cityagent/blocks/social_block.py +70 -51
- pycityagent/cityagent/blocks/utils.py +2 -0
- pycityagent/cityagent/firmagent.py +6 -7
- pycityagent/cityagent/governmentagent.py +7 -9
- pycityagent/cityagent/memory_config.py +48 -48
- pycityagent/cityagent/message_intercept.py +99 -0
- pycityagent/cityagent/nbsagent.py +6 -29
- pycityagent/cityagent/societyagent.py +325 -127
- pycityagent/cli/wrapper.py +4 -0
- pycityagent/economy/econ_client.py +0 -2
- pycityagent/environment/__init__.py +7 -1
- pycityagent/environment/sim/client.py +10 -1
- pycityagent/environment/sim/clock_service.py +2 -2
- pycityagent/environment/sim/pause_service.py +61 -0
- pycityagent/environment/sim/sim_env.py +34 -46
- pycityagent/environment/simulator.py +18 -14
- pycityagent/llm/embeddings.py +0 -24
- pycityagent/llm/llm.py +18 -10
- pycityagent/memory/faiss_query.py +29 -26
- pycityagent/memory/memory.py +733 -247
- pycityagent/message/__init__.py +8 -1
- pycityagent/message/message_interceptor.py +322 -0
- pycityagent/message/messager.py +42 -11
- pycityagent/pycityagent-sim +0 -0
- pycityagent/simulation/agentgroup.py +137 -96
- pycityagent/simulation/simulation.py +184 -38
- pycityagent/simulation/storage/pg.py +2 -2
- pycityagent/tools/tool.py +7 -9
- pycityagent/utils/__init__.py +7 -2
- pycityagent/utils/pg_query.py +1 -0
- pycityagent/utils/survey_util.py +26 -23
- pycityagent/workflow/block.py +14 -7
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/METADATA +2 -2
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/RECORD +48 -46
- pycityagent/cityagent/blocks/time_block.py +0 -116
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/LICENSE +0 -0
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/WHEEL +0 -0
- {pycityagent-2.0.0a52.dist-info → pycityagent-2.0.0a54.dist-info}/entry_points.txt +0 -0
- {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.
|
33
|
+
- req (dict): https://cityproto.readthedocs.io/en/latest/docs.html#nowrequest
|
34
34
|
|
35
35
|
Returns:
|
36
|
-
- https://cityproto.
|
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
|
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
|
-
|
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
|
-
#
|
71
|
-
self.
|
72
|
-
|
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.
|
76
|
+
self.sim_addr = self.reset(sim_addr)
|
83
77
|
|
84
78
|
def reset(
|
85
79
|
self,
|
86
|
-
|
80
|
+
sim_addr: Optional[str] = None,
|
87
81
|
):
|
88
82
|
"""
|
89
83
|
Args:
|
90
|
-
-
|
84
|
+
- sim_addr: str, pycityagent-sim的地址。如果为None,则启动一个新的pycityagent-sim
|
91
85
|
"""
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
self.
|
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
|
-
|
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.
|
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
|
115
|
+
f"start pycityagent-sim at localhost:{self.sim_port}, PID={self._sim_proc.pid}"
|
124
116
|
)
|
125
|
-
|
117
|
+
sim_addr = f"http://localhost:{self.sim_port}"
|
126
118
|
atexit.register(self.close)
|
127
|
-
time.sleep(
|
128
|
-
elif simuletgo_addr is not None:
|
129
|
-
pass
|
119
|
+
time.sleep(0.3)
|
130
120
|
else:
|
131
|
-
|
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
|
-
|
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
|
-
|
144
|
-
self.
|
145
|
-
|
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
|
-
|
63
|
+
sim_addr=config["simulator"].get("server", None),
|
64
64
|
)
|
65
65
|
|
66
66
|
# using local client
|
67
|
-
self._client = CityClient(sim_env.
|
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
|
-
|
166
|
-
|
167
|
-
self.time =
|
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=
|
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
|
-
|
176
|
-
|
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
|
-
|
183
|
-
|
184
|
-
day =
|
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
|
-
|
192
|
-
|
193
|
-
return
|
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(
|
pycityagent/llm/embeddings.py
CHANGED
@@ -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
|
-
|
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(
|
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
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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(
|