pycityagent 1.0.0__py3-none-any.whl → 1.1.1__py3-none-any.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/cc/cc.py CHANGED
@@ -5,7 +5,7 @@ from .trip import *
5
5
  from .conve import *
6
6
  from .user import *
7
7
 
8
- class Commond:
8
+ class Command:
9
9
  """
10
10
  BrainCommond: 即脑部发出的指令; The commond from Agent's Brain
11
11
 
@@ -18,19 +18,19 @@ class Commond:
18
18
  self.target = target
19
19
  self.user_func = user_func
20
20
 
21
- class CommondController:
21
+ class CommandController:
22
22
  """
23
23
  Commond控制器模组: 连接Brain以及StateTranformer
24
24
  Commond Controller module: Used to connect the Brain module and StateTransformer module
25
25
  """
26
- def __init__(self, agent, config=None) -> None:
26
+ def __init__(self, agent) -> None:
27
27
  '''默认初始化'''
28
28
  # TODO: config配置
29
29
  self._agent = agent
30
30
  self._brain = agent._brain
31
- self.commond_control = {}
31
+ self.command_line = {}
32
32
  """
33
- 控制序列: 与state关联的BrainCommond集合
33
+ 默认控制序列: 与state关联的BrainCommond集合
34
34
  Control Hub: connect the state with a BrainCommond collection
35
35
  Type: dict[str, list[BrainCommond]]
36
36
  Default:
@@ -40,33 +40,37 @@ class CommondController:
40
40
  - conve: [whetherUserControl, whetherStopConverse]
41
41
  - controled: [whetherEndControl]
42
42
  """
43
- if config != None:
44
- pass
45
- else:
46
- self.commond_control['idle'] = [
47
- Commond('gousercontrol', whetherUserControl),
48
- Commond('gotrip', whetherGoTrip),
49
- Commond('goshop', whetherGoShop),
50
- Commond('goconverse', whetherGoConverse)
51
- ]
52
- self.commond_control['trip'] = [
53
- Commond('gousercontrol', whetherUserControl),
54
- Commond('routefailed', whetherRouteFailed),
55
- Commond('arrived', whetherTripArrived)
56
- ]
57
- self.commond_control['shop'] = [
58
- Commond('gousercontrol', whetherUserControl),
59
- Commond('shopdone', whetherShopFinished)
60
- ]
61
- self.commond_control['conve'] = [
62
- Commond('gousercontrol', whetherUserControl),
63
- Commond('convedone', whetherStopConverse)
64
- ]
65
- self.commond_control['controled'] = [
66
- Commond('controlback', whetherEndControl)
67
- ]
68
43
 
69
- def insertCommond(self, target_state:list[str], commond:Commond):
44
+ self.command_line['idle'] = [
45
+ Command('gousercontrol', whetherUserControl),
46
+ Command('gotrip', whetherGoTrip),
47
+ Command('goshop', whetherGoShop),
48
+ Command('goconverse', whetherGoConverse)
49
+ ]
50
+ self.command_line['trip'] = [
51
+ Command('gousercontrol', whetherUserControl),
52
+ Command('routefailed', whetherRouteFailed),
53
+ Command('arrived', whetherTripArrived)
54
+ ]
55
+ self.command_line['shop'] = [
56
+ Command('gousercontrol', whetherUserControl),
57
+ Command('shopdone', whetherShopFinished)
58
+ ]
59
+ self.command_line['conve'] = [
60
+ Command('gousercontrol', whetherUserControl),
61
+ Command('convedone', whetherStopConverse)
62
+ ]
63
+ self.command_line['controled'] = [
64
+ Command('controlback', whetherEndControl)
65
+ ]
66
+
67
+ def reset_cc(self):
68
+ """
69
+ 重置命令控制器
70
+ """
71
+ self.command_line = {}
72
+
73
+ def insert_command(self, target_state:list[str], command:Command):
70
74
  """
71
75
  插入指令
72
76
  Insert Commond: This function will insert the commond to those control sequences show in target_state
@@ -76,10 +80,10 @@ class CommondController:
76
80
  - commond (Comond): the commond to be inserted
77
81
  """
78
82
  for target in target_state:
79
- if target in self.commond_control.keys():
80
- self.commond_control.append(commond)
83
+ if target in self.command_line.keys():
84
+ self.command_line[target].append(command)
81
85
  else:
82
- self.commond_control[target] = [commond]
86
+ self.command_line[target] = [command]
83
87
 
84
88
  async def Run(self):
85
89
  """
@@ -92,9 +96,9 @@ class CommondController:
92
96
  - If there is a 'True' of function, returns the target commond
93
97
  - Else, return 'nothing' commond
94
98
  """
95
- if self._agent.state not in self.commond_control.keys():
99
+ if self._agent.state not in self.command_line.keys():
96
100
  print(f"No commond control line match with current state: {self._agent.state}")
97
- control_line = self.commond_control[self._agent.state] # 获取agent状态对应的控制链
101
+ control_line = self.command_line[self._agent.state] # 获取agent状态对应的控制链
98
102
  for control in control_line:
99
103
  result = await control.user_func(self._agent.Brain.Memory.Working)
100
104
  if result:
@@ -18,21 +18,9 @@ class HubConnector:
18
18
  app_id=app_id,
19
19
  app_secret=app_secret
20
20
  )
21
- if agent == None:
22
- self._agent = None
23
- self._agent_id = None
24
- else:
25
- self._agent = agent
26
- try:
27
- self._agent_id = self._client.bind_person(
28
- self._agent.base['id'],
29
- self._agent.agent_name,
30
- Image.open(profile_img)
31
- )
32
- except Exception as e:
33
- traceback.print_exc()
34
- print(e)
35
- self._agent_id = 7
21
+ self._agent = agent
22
+ self._agent_id = None
23
+ self._profile_img = profile_img
36
24
  self.messageBuffer:list[AgentMessage] = []
37
25
  """
38
26
  用于存储历史前端交互信息
@@ -55,26 +43,29 @@ class HubConnector:
55
43
  app_secret=app_secret
56
44
  )
57
45
 
58
- def BindAgent(self, agent, profile_img:str):
46
+ def BindCitizenAgent(self):
59
47
  """
60
- 绑定Agent
48
+ 将CitizenAgent绑定到绑定Agent
61
49
  Bind an Agent through AppHub
62
-
63
- Args:
64
- - agent (Agent): the Agent needs to be bind with
65
- - profile_img (str) : the path of profile_img, which will be shown in frontend and dashboard
66
50
  """
67
- if self._client == None:
68
- print("Not Connect To AppHub Yet")
69
- return
70
- self._agent = agent
71
51
  self._agent_id = self._client.bind_person(
72
- self._agent.base['id'],
73
- self._agent.agent_name,
74
- Image.open(profile_img)
52
+ self._agent._id,
53
+ self._agent._name,
54
+ Image.open(self._profile_img)
55
+ )
56
+
57
+ def InsertFuncAgent(self):
58
+ """
59
+ 将FuncAgent插入到AppHub中
60
+ Insert the Func Agent to AppHub
61
+ """
62
+ self._agent_id = self._client.bind_func(
63
+ self._agent._id,
64
+ self._agent._name,
65
+ Image.open(self._profile_img)
75
66
  )
76
67
 
77
- def Update(self, messages:Optional[list[AgentMessage]]=None, streetview:Image=None, longlat:list[float]=None, pop:str=None):
68
+ def Update(self, messages:Optional[list[AgentMessage]]=None, streetview:Image.Image=None, longlat:list[float]=None, pop:str=None):
78
69
  """
79
70
  交互更新
80
71
  FrontEnd Related Update
@@ -100,12 +91,13 @@ class HubConnector:
100
91
  print("AppHub: Not Bind Agent Yet")
101
92
  return
102
93
  else:
103
- pop_ = self._agent.agent_name
94
+ pop_ = self._agent._name
104
95
  if pop != None:
105
96
  pop_ = self._agent.agent_name + ": " + pop
106
- longlat_ = [self._agent.motion['position']['longlat_position']['longitude'], self._agent.motion['position']['longlat_position']['latitude']]
107
97
  if longlat != None:
108
98
  longlat_ = longlat
99
+ else:
100
+ longlat_ = [self._agent.motion['position']['longlat_position']['longitude'], self._agent.motion['position']['longlat_position']['latitude']]
109
101
 
110
102
  self._client.update_agent_map(
111
103
  agent_id = self._agent_id,
@@ -1,3 +1,3 @@
1
1
  from .image import *
2
2
 
3
- __all__ = [AgentImage, AgentScratch]
3
+ __all__ = [Image, CitizenImage, Scratch, CitizenScratch]
@@ -2,29 +2,44 @@ from typing import Optional
2
2
  import json
3
3
  from abc import ABC, abstractclassmethod
4
4
 
5
- class ProfileTemplate:
5
+
6
+ class Image:
6
7
  """
7
- profile模板类
8
- The template class of Agent Profile
8
+ Image模块: 智能体画像
9
+ Agent's Image module: Defines the uniqueness of Agent
9
10
  """
10
- def __init__(self) -> None:
11
- pass
11
+ def __init__(self, agent) -> None:
12
+ self._agent = agent
13
+ self.scratch = Scratch()
14
+
15
+ def get_profile(self):
16
+ """
17
+ 获取Agent的Scratch Profile信息
18
+ Get the Scratch Profile message
12
19
 
13
- @abstractclassmethod
14
- def to_dialog(self) -> list[dict]:
20
+ Returns:
21
+ - (str)
15
22
  """
16
- 将Profile转化为LLM可用的dialog的抽象函数
17
- The abstract method that turns Agent's profile to LLM standard dialog
23
+ return self.scratch.get_profile_content()
24
+
25
+ def load_scratch(self, scratch_file:str):
18
26
  """
27
+ 加载基于文件的Profile加载
28
+ Load Profile based on file
19
29
 
20
- class AgentImage:
30
+ Args:
31
+ - scratch_file (str): the path of scratch_file
32
+ """
33
+ self.scratch.load(scratch_file)
34
+
35
+ class CitizenImage(Image):
21
36
  """
22
37
  Agent Image模块: 用户画像控制
23
38
  Agent's Image module: Defines the uniqueness of Agent
24
39
  """
25
40
  def __init__(self, agent, scratch_file:str=None, selfie:bool=False) -> None:
26
41
  self._agent = agent
27
- self.scratch = AgentScratch()
42
+ self.scratch = CitizenScratch()
28
43
  """
29
44
  Agent的Scratch信息: 用于描述Agent的基本属性
30
45
  Scratch: Used to describe the basic info of Agent
@@ -48,7 +63,7 @@ class AgentImage:
48
63
  if scratch_file != None:
49
64
  self.scratch.load(scratch_file)
50
65
  else:
51
- self.scratch.name = self._agent.agent_name
66
+ self.scratch.name = self._agent._name
52
67
  self.scratch.age = profile['age']
53
68
  education = profile['education']
54
69
  if education < 3:
@@ -72,26 +87,6 @@ class AgentImage:
72
87
  # TODO: 补充自拍
73
88
  pass
74
89
 
75
- def get_profile(self):
76
- """
77
- 获取Agent的Scratch Profile信息
78
- Get the Scratch Profile message
79
-
80
- Returns:
81
- - (str)
82
- """
83
- return self.scratch.get_profile_content()
84
-
85
- def load_scratch(self, scratch_file:str):
86
- """
87
- 加载基于文件的Profile加载
88
- Load Profile based on file
89
-
90
- Args:
91
- - scratch_file (str): the path of scratch_file
92
- """
93
- self.scratch.load(scratch_file)
94
-
95
90
  def load_selfie(self, selfie_file:str):
96
91
  """
97
92
  基于图像的Selfie加载
@@ -103,17 +98,8 @@ class AgentImage:
103
98
  """
104
99
  print("Not Implemented")
105
100
 
106
- class AgentScratch():
107
- def __init__(self, scratch:Optional[dict]=None) -> None:
108
- self.name = None
109
- self.age = None
110
- self.education = None
111
- self.gender = None
112
- self.consumption = None
113
- self.innate = ''
114
- self.learned = ''
115
- self.currently = ''
116
- self.lifestyle = ''
101
+ class Scratch:
102
+ def __init__(self, scratch: Optional[dict]=None) -> None:
117
103
  if scratch != None:
118
104
  self.forward(scratch)
119
105
 
@@ -133,6 +119,25 @@ class AgentScratch():
133
119
  json_str = f.read()
134
120
  scratch = json.loads(json_str)
135
121
  self.forward(scratch)
122
+
123
+ def get_profile_content(self):
124
+ text = ""
125
+ for attr_name, attr_value in self.__dict__.items():
126
+ text += f"{attr_name}: {attr_value}\n"
127
+ return text
128
+
129
+ class CitizenScratch(Scratch):
130
+ def __init__(self, scratch:Optional[dict]=None) -> None:
131
+ super().__init__(scratch=scratch)
132
+ self.name = None
133
+ self.age = None
134
+ self.education = None
135
+ self.gender = None
136
+ self.consumption = None
137
+ self.innate = ''
138
+ self.learned = ''
139
+ self.currently = ''
140
+ self.lifestyle = ''
136
141
 
137
142
  def get_str_lifestyle(self):
138
143
  return self.lifestyle
@@ -153,6 +158,5 @@ class AgentScratch():
153
158
  text += f'生活习惯: {self.lifestyle}\n'
154
159
  return text
155
160
 
156
-
157
161
  def __str__(self):
158
162
  return str(self.__dict__)
pycityagent/simulator.py CHANGED
@@ -1,10 +1,11 @@
1
1
  from pycitysim import *
2
- from pycitysim.sidecar import OnlyClientSidecar
2
+ from pycitysim.routing import RoutingClient
3
3
  from pycitysim.sim import CityClient
4
4
  from typing import Optional, Union
5
5
  from datetime import datetime, timedelta
6
6
  import asyncio
7
- from .agent import Agent
7
+ from .agent_citizen import CitizenAgent
8
+ from .agent_func import FuncAgent
8
9
 
9
10
  class SimPerceive:
10
11
  """
@@ -44,6 +45,7 @@ class Simulator:
44
45
  mongo_coll = config['map_request']['mongo_coll'],
45
46
  cache_dir = config['map_request']['cache_dir'],
46
47
  )
48
+ self.routing = RoutingClient(self.config['route_request']['server'], True)
47
49
  self.time = 0
48
50
 
49
51
  # * Agent相关
@@ -72,7 +74,7 @@ class Simulator:
72
74
  resp.motions = motions
73
75
  return resp
74
76
 
75
- async def GetAgent(self, name:str=None, id:int=None):
77
+ async def GetCitizenAgent(self, name:str, id:int):
76
78
  """
77
79
  获取agent
78
80
  Get Agent
@@ -82,46 +84,44 @@ class Simulator:
82
84
  - id int: 即绑定的person的id the id of person that you try to bind with
83
85
 
84
86
  Returns:
85
- - Agent
87
+ - CitizenAgent
86
88
  """
87
89
  await self.GetTime()
88
- name_ = "张三"
89
- if name != None:
90
- name_ = name
91
- if id == None:
92
- base = self._client.person_service.default_person()
93
- agent = Agent(
94
- name_,
95
- self.config['simulator']['server'],
96
- simulator=self,
97
- id=id,
98
- base=base,
99
- )
100
- else:
101
- resp = await self._client.person_service.GetPerson({"person_id": id})
102
- base = resp['base']
103
- motion = resp['motion']
104
- agent = Agent(
105
- name_,
106
- self.config['simulator']['server'],
107
- simulator=self,
108
- id=id,
109
- base=base,
110
- motion=motion,
111
- )
112
- if 'streetview_request' in self.config.keys():
113
- agent.Brain.Sence._streetview_engine = self.config['streetview_request']['engine']
114
- if agent.Brain.Sence._streetview_engine == 'baidumap':
115
- agent.Brain.Sence._streetviewAK = self.config['streetview_request']['mapAK']
116
- elif agent.Brain.Sence._streetview_engine == 'googlemap':
117
- if 'proxy' in self.config['streetview_request'].keys():
118
- agent.Brain.Sence._streetviewProxy = self.config['streetview_request']['proxy']
119
- else:
120
- agent.Brain.Sence._streetview_engine = ""
121
- print("Wrong Streetview Engine")
90
+ resp = await self._client.person_service.GetPerson({"person_id": id})
91
+ base = resp['base']
92
+ motion = resp['motion']
93
+ agent = CitizenAgent(
94
+ name,
95
+ self.config['simulator']['server'],
96
+ simulator=self,
97
+ id=id,
98
+ base=base,
99
+ motion=motion
100
+ )
101
+ agent.set_streetview_config(self.config['streetview_request'])
102
+ return agent
103
+
104
+ async def GetFuncAgent(self, id:int, name:str):
105
+ """
106
+ 获取一个Func Agent模板
107
+
108
+ Args:
109
+ - name (str): the name of your agent
110
+ - id (int): the unique id of agent
111
+
112
+ Returns:
113
+ - FuncAgent
114
+ """
115
+ agent = FuncAgent(
116
+ name,
117
+ id+10000000,
118
+ self.config['simulator']['server'],
119
+ simulator=self
120
+ )
121
+ agent.set_streetview_config(self.config['streetview_request'])
122
122
  return agent
123
123
 
124
- def InsertAgent(self, profile):
124
+ def InsertCitizenAgent(self, profile):
125
125
  """
126
126
  插入agent
127
127
  Insert Agent
pycityagent/st/st.py CHANGED
@@ -3,8 +3,8 @@ from abc import ABC, abstractmethod
3
3
 
4
4
  class StateTransformer:
5
5
  """
6
- State Transformer模块: 控制agent状态转移
7
- The State Transformer module: used to control the transformation of agent's state
6
+ 默认State Transformer模块: 控制agent状态转移
7
+ Default State Transformer module: used to control the transformation of agent's state
8
8
 
9
9
  Doc:
10
10
  - 基于transitions()构建 https://github.com/tyarkoni/transitions
@@ -42,14 +42,14 @@ class StateTransformer:
42
42
  - controlback: controled -> idle
43
43
  - Note: 目前不支持状态扩展
44
44
  """
45
- states = ['idle', 'trip', 'shop', 'conve', 'paused', 'controled']
45
+ _states = ['idle', 'trip', 'shop', 'conve', 'paused', 'controled']
46
46
 
47
47
  def __init__(self, config=None):
48
48
  self.pre_state = None
49
- # TODO: 后续添加扩展接口
50
49
  # Initialize the state machine
51
- self.machine = Machine(model=self, states=StateTransformer.states, initial='idle')
52
-
50
+ self.machine = Machine(states=StateTransformer._states, initial='idle')
51
+ self.machine._transition_queue
52
+
53
53
  # idle
54
54
  self.machine.add_transition(trigger='gotrip', source='idle', dest='trip')
55
55
  self.machine.add_transition(trigger='goshop', source='idle', dest='shop')
@@ -81,11 +81,45 @@ class StateTransformer:
81
81
  self.machine.add_transition(trigger='gousercontrol', source='*', dest='controled')
82
82
  self.machine.add_transition(trigger='controlback', source='controled', dest='idle')
83
83
 
84
+ def reset_state_transformer(self, states:list[str], initial:str):
85
+ """
86
+ 重置状态控制器——重置后状态转化器没有任何可用transition
87
+
88
+ Args:
89
+ - states (list[str]): a list of states
90
+ - initial (str): initial state
91
+ """
92
+ self.machine = Machine(states=states, initial=initial)
93
+ self.machine.add_transition(trigger='nothing', source='*', dest='=')
94
+
95
+
96
+ def add_transition(self, trigger:str, source: str, dest:str, before=None, after=None):
97
+ """
98
+ 添加状态转换
99
+ Add state transition
100
+
101
+ Args:
102
+ - trigger (str): 用于唤醒该状态转换器
103
+ - source (str): 源状态, 如为'*' 表示任意状态皆可作为源状态
104
+ - dest (str): 目标状态, 如为'=' 表示目标状态与原状态相同
105
+ - defore (func): 即状态转化后执行的操作
106
+ - after (func): 即状态转化前执行的操作
107
+ """
108
+ self.machine.add_transition(trigger=trigger, source=source, dest=dest, before=before, after=after)
109
+
84
110
  def state_remember(self):
111
+ """
112
+ 记住当前状态并存储于pre_state属性中
113
+ Remember current state and store it to 'pre_state' attribute
114
+ """
85
115
  if self.state != 'paused': # 避免重复Pause
86
116
  self.pre_state = self.state
87
117
 
88
118
  def pause_back(self):
119
+ """
120
+ 从原状态恢复, 即恢复为pre_state记录的状态
121
+ Recover from pre_state
122
+ """
89
123
  if self.pre_state == 'trip':
90
124
  self.active_trip()
91
125
  elif self.pre_state == 'shop':
@@ -93,4 +127,7 @@ class StateTransformer:
93
127
  elif self.pre_state == 'conve':
94
128
  self.active_conve()
95
129
  elif self.pre_sate == 'idle':
96
- self.active_idle()
130
+ self.active_idle()
131
+
132
+ def trigger(self, command:str):
133
+ self.machine.trigger(command)
@@ -82,7 +82,7 @@ class UrbanLLM:
82
82
  """
83
83
  ppt = "如何理解这幅图像?"
84
84
  if prompt != None:
85
- ppt += prompt
85
+ ppt = prompt
86
86
  dialog = [{
87
87
  'role': 'user',
88
88
  'content': [
@@ -101,7 +101,7 @@ class UrbanLLM:
101
101
  print(response.code) # The error code.
102
102
  return "Error"
103
103
 
104
- def img_generate(self, prompt, size:str='512*512', quantity:int = 1):
104
+ def img_generate(self, prompt:str, size:str='512*512', quantity:int = 1):
105
105
  """
106
106
  图像生成
107
107
  Image generation