pycityagent 2.0.0a52__cp312-cp312-macosx_11_0_arm64.whl → 2.0.0a54__cp312-cp312-macosx_11_0_arm64.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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(
|