pycityagent 2.0.0a5__py3-none-any.whl → 2.0.0a7__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.
Files changed (38) hide show
  1. pycityagent/agent.py +14 -5
  2. pycityagent/environment/interact/interact.py +86 -29
  3. pycityagent/environment/sence/static.py +3 -2
  4. pycityagent/environment/sim/aoi_service.py +1 -1
  5. pycityagent/environment/sim/economy_services.py +1 -1
  6. pycityagent/environment/sim/road_service.py +1 -1
  7. pycityagent/environment/sim/social_service.py +1 -1
  8. pycityagent/environment/simulator.py +6 -4
  9. pycityagent/environment/utils/__init__.py +5 -1
  10. pycityagent/llm/__init__.py +1 -1
  11. pycityagent/llm/embedding.py +36 -35
  12. pycityagent/llm/llm.py +197 -161
  13. pycityagent/llm/llmconfig.py +7 -9
  14. pycityagent/llm/utils.py +2 -2
  15. pycityagent/memory/memory.py +1 -2
  16. pycityagent/memory/memory_base.py +1 -2
  17. pycityagent/memory/profile.py +1 -2
  18. pycityagent/memory/self_define.py +1 -2
  19. pycityagent/memory/state.py +1 -2
  20. pycityagent/message/__init__.py +1 -1
  21. pycityagent/message/messager.py +11 -4
  22. pycityagent/simulation/__init__.py +1 -1
  23. pycityagent/simulation/agentgroup.py +19 -11
  24. pycityagent/simulation/interview.py +9 -5
  25. pycityagent/simulation/simulation.py +89 -37
  26. pycityagent/simulation/survey/__init__.py +1 -6
  27. pycityagent/simulation/survey/manager.py +22 -21
  28. pycityagent/simulation/survey/models.py +8 -5
  29. pycityagent/utils/decorators.py +14 -4
  30. pycityagent/utils/parsers/__init__.py +2 -1
  31. pycityagent/workflow/block.py +4 -3
  32. pycityagent/workflow/prompt.py +16 -9
  33. pycityagent/workflow/tool.py +1 -2
  34. pycityagent/workflow/trigger.py +36 -23
  35. {pycityagent-2.0.0a5.dist-info → pycityagent-2.0.0a7.dist-info}/METADATA +1 -1
  36. pycityagent-2.0.0a7.dist-info/RECORD +70 -0
  37. pycityagent-2.0.0a5.dist-info/RECORD +0 -70
  38. {pycityagent-2.0.0a5.dist-info → pycityagent-2.0.0a7.dist-info}/WHEEL +0 -0
@@ -3,8 +3,7 @@ Agent Profile
3
3
  """
4
4
 
5
5
  from copy import deepcopy
6
- from typing import (Any, Callable, Dict, List, Optional, Sequence, Tuple,
7
- Union, cast)
6
+ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union, cast
8
7
 
9
8
  from ..utils.decorators import lock_decorator
10
9
  from .const import *
@@ -3,8 +3,7 @@ Self Define Data
3
3
  """
4
4
 
5
5
  from copy import deepcopy
6
- from typing import (Any, Callable, Dict, List, Optional, Sequence, Tuple,
7
- Union, cast)
6
+ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union, cast
8
7
 
9
8
  from ..utils.decorators import lock_decorator
10
9
  from .const import *
@@ -3,8 +3,7 @@ Agent State
3
3
  """
4
4
 
5
5
  from copy import deepcopy
6
- from typing import (Any, Callable, Dict, List, Optional, Sequence, Tuple,
7
- Union, cast)
6
+ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union, cast
8
7
 
9
8
  from ..utils.decorators import lock_decorator
10
9
  from .const import *
@@ -1,3 +1,3 @@
1
1
  from .messager import Messager
2
2
 
3
- __all__ = ["Messager"]
3
+ __all__ = ["Messager"]
@@ -4,9 +4,14 @@ import logging
4
4
  import math
5
5
  from aiomqtt import Client
6
6
 
7
+
7
8
  class Messager:
8
- def __init__(self, broker, port=1883, timeout=math.inf):
9
- self.client = Client(broker, port=port, timeout=timeout)
9
+ def __init__(
10
+ self, hostname, port=1883, username=None, password=None, timeout=math.inf
11
+ ):
12
+ self.client = Client(
13
+ hostname, port=port, username=username, password=password, timeout=timeout
14
+ )
10
15
  self.connected = False # 是否已连接标志
11
16
  self.message_queue = asyncio.Queue() # 用于存储接收到的消息
12
17
  self.subscribers = {} # 订阅者信息,topic -> Agent 映射
@@ -31,7 +36,9 @@ class Messager:
31
36
 
32
37
  async def subscribe(self, topic, agent):
33
38
  if not self.is_connected():
34
- logging.error(f"Cannot subscribe to {topic} because not connected to the Broker.")
39
+ logging.error(
40
+ f"Cannot subscribe to {topic} because not connected to the Broker."
41
+ )
35
42
  return
36
43
  await self.client.subscribe(topic)
37
44
  self.subscribers[topic] = agent
@@ -48,7 +55,7 @@ class Messager:
48
55
  while not self.message_queue.empty():
49
56
  messages.append(await self.message_queue.get())
50
57
  return messages
51
-
58
+
52
59
  async def send_message(self, topic: str, payload: str, sender_id: int):
53
60
  """通过 Messager 发送消息,包含发送者 ID"""
54
61
  # 构造消息,payload 中加入 sender_id 以便接收者识别
@@ -4,4 +4,4 @@
4
4
 
5
5
  from .simulation import AgentSimulation
6
6
 
7
- __all__ = ["AgentSimulation"]
7
+ __all__ = ["AgentSimulation"]
@@ -8,12 +8,19 @@ from pycityagent.llm.llm import LLM
8
8
  from pycityagent.llm.llmconfig import LLMConfig
9
9
  from pycityagent.message import Messager
10
10
 
11
+
11
12
  @ray.remote
12
13
  class AgentGroup:
13
- def __init__(self, agents: list[Agent], config: dict):
14
+ def __init__(self, agents: list[Agent], config: dict, exp_id: str):
14
15
  self.agents = agents
15
16
  self.config = config
16
- self.messager = Messager(config["simulator_request"]["mqtt"]["server"], config["simulator_request"]["mqtt"]["port"])
17
+ self.exp_id = exp_id
18
+ self.messager = Messager(
19
+ hostname=config["simulator_request"]["mqtt"]["server"],
20
+ port=config["simulator_request"]["mqtt"]["port"],
21
+ username=config["simulator_request"]["mqtt"].get("username", None),
22
+ password=config["simulator_request"]["mqtt"].get("password", None),
23
+ )
17
24
  self.initialized = False
18
25
 
19
26
  # Step:1 prepare LLM client
@@ -27,9 +34,12 @@ class AgentGroup:
27
34
 
28
35
  # Step:3 prepare Economy client
29
36
  logging.info("-----Creating Economy client in remote...")
30
- self.economy_client = EconomyClient(config["simulator_request"]["economy"]['server'])
37
+ self.economy_client = EconomyClient(
38
+ config["simulator_request"]["economy"]["server"]
39
+ )
31
40
 
32
41
  for agent in self.agents:
42
+ agent.set_exp_id(self.exp_id)
33
43
  agent.set_llm_client(self.llm)
34
44
  agent.set_simulator(self.simulator)
35
45
  agent.set_economy_client(self.economy_client)
@@ -44,7 +54,7 @@ class AgentGroup:
44
54
  await self.messager.start_listening()
45
55
  for agent in self.agents:
46
56
  agent.set_messager(self.messager)
47
- topic = f"/agents/{agent._agent_id}/chat"
57
+ topic = f"/exps/{self.exp_id}/agents/{agent._agent_id}/chat"
48
58
  await self.messager.subscribe(topic, agent)
49
59
  self.initialized = True
50
60
 
@@ -70,10 +80,9 @@ class AgentGroup:
70
80
 
71
81
  # 添加解码步骤,将bytes转换为str
72
82
  if isinstance(payload, bytes):
73
- payload = payload.decode('utf-8')
74
-
75
- # 提取 agent_id(主题格式为 "/agents/{agent_id}/chat"
76
- _, agent_id, _ = topic.strip("/").split("/")
83
+ payload = payload.decode("utf-8")
84
+ # 提取 agent_id(主题格式为 "/exps/{exp_id}/agents/{agent_id}/chat")
85
+ _, _, _, agent_id, _ = topic.strip("/").split("/")
77
86
  agent_id = int(agent_id)
78
87
 
79
88
  if agent_id in self.id2agent:
@@ -95,15 +104,14 @@ class AgentGroup:
95
104
  start_time = await self.simulator.get_time()
96
105
  # 计算结束时间(秒)
97
106
  end_time = start_time + day * 24 * 3600 # 将天数转换为秒
98
-
107
+
99
108
  while True:
100
109
  current_time = await self.simulator.get_time()
101
110
  if current_time >= end_time:
102
111
  break
103
-
112
+
104
113
  await self.step()
105
114
 
106
115
  except Exception as e:
107
116
  logging.error(f"模拟器运行错误: {str(e)}")
108
117
  raise
109
-
@@ -2,20 +2,24 @@ from dataclasses import dataclass
2
2
  from datetime import datetime
3
3
  from typing import List, Optional
4
4
 
5
+
5
6
  @dataclass
6
7
  class InterviewRecord:
7
8
  """采访记录"""
9
+
8
10
  timestamp: datetime
9
11
  agent_name: str
10
12
  question: str
11
13
  response: str
12
14
  blocking: bool
13
15
 
16
+
14
17
  class InterviewManager:
15
18
  """采访管理器"""
19
+
16
20
  def __init__(self):
17
21
  self._history: List[InterviewRecord] = []
18
-
22
+
19
23
  def add_record(self, agent_name: str, question: str, response: str, blocking: bool):
20
24
  """添加采访记录"""
21
25
  record = InterviewRecord(
@@ -23,14 +27,14 @@ class InterviewManager:
23
27
  agent_name=agent_name,
24
28
  question=question,
25
29
  response=response,
26
- blocking=blocking
30
+ blocking=blocking,
27
31
  )
28
32
  self._history.append(record)
29
-
33
+
30
34
  def get_agent_history(self, agent_name: str) -> List[InterviewRecord]:
31
35
  """获取指定智能体的采访历史"""
32
36
  return [r for r in self._history if r.agent_name == agent_name]
33
-
37
+
34
38
  def get_recent_history(self, limit: int = 10) -> List[InterviewRecord]:
35
39
  """获取最近的采访记录"""
36
- return sorted(self._history, key=lambda x: x.timestamp, reverse=True)[:limit]
40
+ return sorted(self._history, key=lambda x: x.timestamp, reverse=True)[:limit]
@@ -1,33 +1,36 @@
1
1
  import asyncio
2
2
  import json
3
3
  import logging
4
+ import uuid
4
5
  from datetime import datetime
5
6
  import random
6
7
  from typing import Dict, List, Optional, Callable
7
8
  from mosstool.map._map_util.const import AOI_START_ID
8
9
 
9
- from pycityagent.economy.econ_client import EconomyClient
10
- from pycityagent.environment.simulator import Simulator
11
10
  from pycityagent.memory.memory import Memory
12
- from pycityagent.message.messager import Messager
13
11
 
14
12
  from ..agent import Agent
15
13
  from .interview import InterviewManager
16
14
  from .survey import QuestionType, SurveyManager
17
15
  from .ui import InterviewUI
18
16
  from .agentgroup import AgentGroup
17
+
19
18
  logger = logging.getLogger(__name__)
20
19
 
21
20
 
22
21
  class AgentSimulation:
23
22
  """城市智能体模拟器"""
24
- def __init__(self, agent_class: type[Agent], config: dict, agent_prefix: str = "agent_"):
23
+
24
+ def __init__(
25
+ self, agent_class: type[Agent], config: dict, agent_prefix: str = "agent_"
26
+ ):
25
27
  """
26
28
  Args:
27
29
  agent_class: 智能体类
28
30
  config: 配置
29
31
  agent_prefix: 智能体名称前缀
30
32
  """
33
+ self.exp_id = uuid.uuid4()
31
34
  self.agent_class = agent_class
32
35
  self.config = config
33
36
  self.agent_prefix = agent_prefix
@@ -42,16 +45,21 @@ class AgentSimulation:
42
45
  self._blocked_agents: List[str] = [] # 新增:持续阻塞的智能体列表
43
46
  self._survey_manager = SurveyManager()
44
47
 
45
- async def init_agents(self, agent_count: int, group_size: int = 1000, memory_config_func: Callable = None) -> None:
48
+ async def init_agents(
49
+ self,
50
+ agent_count: int,
51
+ group_size: int = 1000,
52
+ memory_config_func: Callable = None,
53
+ ) -> None:
46
54
  """初始化智能体
47
-
55
+
48
56
  Args:
49
57
  agent_count: 要创建的总智能体数量
50
58
  group_size: 每个组的智能体数量,每一个组为一个独立的ray actor
51
59
  memory_config_func: 返回Memory配置的函数,需要返回(EXTRA_ATTRIBUTES, PROFILE, BASE)元组
52
60
  """
53
61
  if memory_config_func is None:
54
- memory_config_func = self.default_memory_config_func
62
+ memory_config_func = self.default_memory_config_func
55
63
 
56
64
  for i in range(agent_count):
57
65
  agent_name = f"{self.agent_prefix}{i}"
@@ -59,11 +67,9 @@ class AgentSimulation:
59
67
  # 获取Memory配置
60
68
  extra_attributes, profile, base = memory_config_func()
61
69
  memory = Memory(
62
- config=extra_attributes,
63
- profile=profile.copy(),
64
- base=base.copy()
70
+ config=extra_attributes, profile=profile.copy(), base=base.copy()
65
71
  )
66
-
72
+
67
73
  # 创建智能体时传入Memory配置
68
74
  agent = self.agent_class(
69
75
  name=agent_name,
@@ -74,55 +80,103 @@ class AgentSimulation:
74
80
 
75
81
  # 计算需要的组数,向上取整以处理不足一组的情况
76
82
  num_group = (agent_count + group_size - 1) // group_size
77
-
83
+
78
84
  for i in range(num_group):
79
85
  # 计算当前组的起始和结束索引
80
86
  start_idx = i * group_size
81
87
  end_idx = min((i + 1) * group_size, agent_count)
82
-
88
+
83
89
  # 获取当前组的agents
84
90
  agents = list(self._agents.values())[start_idx:end_idx]
85
91
  group_name = f"{self.agent_prefix}_group_{i}"
86
- group = AgentGroup.remote(agents, self.config)
92
+ group = AgentGroup.remote(agents, self.config, self.exp_id)
87
93
  self._groups[group_name] = group
88
94
 
89
95
  def default_memory_config_func(self):
90
96
  """默认的Memory配置函数"""
91
97
  EXTRA_ATTRIBUTES = {
92
98
  # 需求信息
93
- "needs": (dict, {
94
- 'hungry': random.random(), # 饥饿感
95
- 'tired': random.random(), # 疲劳感
96
- 'safe': random.random(), # 安全需
97
- 'social': random.random(), # 社会需求
98
- }, True),
99
+ "needs": (
100
+ dict,
101
+ {
102
+ "hungry": random.random(), # 饥饿感
103
+ "tired": random.random(), # 疲劳感
104
+ "safe": random.random(), # 安全需
105
+ "social": random.random(), # 社会需求
106
+ },
107
+ True,
108
+ ),
99
109
  "current_need": (str, "none", True),
100
110
  "current_plan": (list, [], True),
101
111
  "current_step": (dict, {"intention": "", "type": ""}, True),
102
- "execution_context" : (dict, {}, True),
112
+ "execution_context": (dict, {}, True),
103
113
  "plan_history": (list, [], True),
104
114
  }
105
115
 
106
116
  PROFILE = {
107
117
  "gender": random.choice(["male", "female"]),
108
- "education": random.choice(["Doctor", "Master", "Bachelor", "College", "High School"]),
118
+ "education": random.choice(
119
+ ["Doctor", "Master", "Bachelor", "College", "High School"]
120
+ ),
109
121
  "consumption": random.choice(["sightly low", "low", "medium", "high"]),
110
- "occupation": random.choice(["Student", "Teacher", "Doctor", "Engineer", "Manager", "Businessman", "Artist", "Athlete", "Other"]),
122
+ "occupation": random.choice(
123
+ [
124
+ "Student",
125
+ "Teacher",
126
+ "Doctor",
127
+ "Engineer",
128
+ "Manager",
129
+ "Businessman",
130
+ "Artist",
131
+ "Athlete",
132
+ "Other",
133
+ ]
134
+ ),
111
135
  "age": random.randint(18, 65),
112
- "skill": random.choice(["Good at problem-solving", "Good at communication", "Good at creativity", "Good at teamwork", "Other"]),
136
+ "skill": random.choice(
137
+ [
138
+ "Good at problem-solving",
139
+ "Good at communication",
140
+ "Good at creativity",
141
+ "Good at teamwork",
142
+ "Other",
143
+ ]
144
+ ),
113
145
  "family_consumption": random.choice(["low", "medium", "high"]),
114
- "personality": random.choice(["outgoint", "introvert", "ambivert", "extrovert"]),
146
+ "personality": random.choice(
147
+ ["outgoint", "introvert", "ambivert", "extrovert"]
148
+ ),
115
149
  "income": random.randint(1000, 10000),
116
150
  "currency": random.randint(10000, 100000),
117
151
  "residence": random.choice(["city", "suburb", "rural"]),
118
- "race": random.choice(["Chinese", "American", "British", "French", "German", "Japanese", "Korean", "Russian", "Other"]),
119
- "religion": random.choice(["none", "Christian", "Muslim", "Buddhist", "Hindu", "Other"]),
120
- "marital_status": random.choice(["not married", "married", "divorced", "widowed"]),
121
- }
152
+ "race": random.choice(
153
+ [
154
+ "Chinese",
155
+ "American",
156
+ "British",
157
+ "French",
158
+ "German",
159
+ "Japanese",
160
+ "Korean",
161
+ "Russian",
162
+ "Other",
163
+ ]
164
+ ),
165
+ "religion": random.choice(
166
+ ["none", "Christian", "Muslim", "Buddhist", "Hindu", "Other"]
167
+ ),
168
+ "marital_status": random.choice(
169
+ ["not married", "married", "divorced", "widowed"]
170
+ ),
171
+ }
122
172
 
123
173
  BASE = {
124
- "home": {"aoi_position": {"aoi_id": AOI_START_ID + random.randint(1, 50000)}},
125
- "work": {"aoi_position": {"aoi_id": AOI_START_ID + random.randint(1, 50000)}},
174
+ "home": {
175
+ "aoi_position": {"aoi_id": AOI_START_ID + random.randint(1, 50000)}
176
+ },
177
+ "work": {
178
+ "aoi_position": {"aoi_id": AOI_START_ID + random.randint(1, 50000)}
179
+ },
126
180
  }
127
181
 
128
182
  return EXTRA_ATTRIBUTES, PROFILE, BASE
@@ -219,7 +273,7 @@ class AgentSimulation:
219
273
  except Exception as e:
220
274
  logger.error(f"采访过程出错: {str(e)}")
221
275
  return f"采访过程出现错误: {str(e)}"
222
-
276
+
223
277
  async def submit_survey(self, agent_name: str, survey_id: str) -> str:
224
278
  """向智能体提交问卷
225
279
 
@@ -320,9 +374,7 @@ class AgentSimulation:
320
374
  prevent_thread_lock=True,
321
375
  quiet=True,
322
376
  )
323
- logger.info(
324
- f"Gradio Frontend is running on http://{server_name}:{server_port}"
325
- )
377
+ logger.info(f"Gradio Frontend is running on http://{server_name}:{server_port}")
326
378
 
327
379
  async def step(self):
328
380
  """运行一步, 即每个智能体执行一次forward"""
@@ -334,7 +386,7 @@ class AgentSimulation:
334
386
  except Exception as e:
335
387
  logger.error(f"运行错误: {str(e)}")
336
388
  raise
337
-
389
+
338
390
  async def run(
339
391
  self,
340
392
  day: int = 1,
@@ -349,7 +401,7 @@ class AgentSimulation:
349
401
  tasks = []
350
402
  for group in self._groups.values():
351
403
  tasks.append(group.run.remote(day))
352
-
404
+
353
405
  await asyncio.gather(*tasks)
354
406
 
355
407
  except Exception as e:
@@ -1,9 +1,4 @@
1
1
  from .models import QuestionType, Question, Survey
2
2
  from .manager import SurveyManager
3
3
 
4
- __all__ = [
5
- 'QuestionType',
6
- 'Question',
7
- 'Survey',
8
- 'SurveyManager'
9
- ]
4
+ __all__ = ["QuestionType", "Question", "Survey", "SurveyManager"]
@@ -4,14 +4,17 @@ import uuid
4
4
  import json
5
5
  from .models import Survey, Question, QuestionType
6
6
 
7
+
7
8
  class SurveyManager:
8
9
  def __init__(self):
9
10
  self._surveys: Dict[str, Survey] = {}
10
-
11
- def create_survey(self, title: str, description: str, questions: List[dict]) -> Survey:
11
+
12
+ def create_survey(
13
+ self, title: str, description: str, questions: List[dict]
14
+ ) -> Survey:
12
15
  """创建新问卷"""
13
16
  survey_id = str(uuid.uuid4())
14
-
17
+
15
18
  # 转换问题数据
16
19
  survey_questions = []
17
20
  for q in questions:
@@ -21,47 +24,45 @@ class SurveyManager:
21
24
  required=q.get("required", True),
22
25
  options=q.get("options", []),
23
26
  min_rating=q.get("min_rating", 1),
24
- max_rating=q.get("max_rating", 5)
27
+ max_rating=q.get("max_rating", 5),
25
28
  )
26
29
  survey_questions.append(question)
27
-
30
+
28
31
  survey = Survey(
29
32
  id=survey_id,
30
33
  title=title,
31
34
  description=description,
32
- questions=survey_questions
35
+ questions=survey_questions,
33
36
  )
34
-
37
+
35
38
  self._surveys[survey_id] = survey
36
39
  return survey
37
-
40
+
38
41
  def get_survey(self, survey_id: str) -> Optional[Survey]:
39
42
  """获取指定问卷"""
40
43
  return self._surveys.get(survey_id)
41
-
44
+
42
45
  def get_all_surveys(self) -> List[Survey]:
43
46
  """获取所有问卷"""
44
47
  return list(self._surveys.values())
45
-
48
+
46
49
  def add_response(self, survey_id: str, agent_name: str, response: dict) -> bool:
47
50
  """添加问卷回答"""
48
51
  survey = self.get_survey(survey_id)
49
52
  if not survey:
50
53
  return False
51
-
52
- survey.responses[agent_name] = {
53
- "timestamp": datetime.now(),
54
- **response
55
- }
54
+
55
+ survey.responses[agent_name] = {"timestamp": datetime.now(), **response}
56
56
  return True
57
-
57
+
58
58
  def export_results(self, survey_id: str) -> str:
59
59
  """导出问卷结果"""
60
60
  survey = self.get_survey(survey_id)
61
61
  if not survey:
62
62
  return json.dumps({"error": "问卷不存在"})
63
-
64
- return json.dumps({
65
- "survey": survey.to_dict(),
66
- "responses": survey.responses
67
- }, ensure_ascii=False, indent=2)
63
+
64
+ return json.dumps(
65
+ {"survey": survey.to_dict(), "responses": survey.responses},
66
+ ensure_ascii=False,
67
+ indent=2,
68
+ )
@@ -4,6 +4,7 @@ from datetime import datetime
4
4
  from enum import Enum
5
5
  import uuid
6
6
 
7
+
7
8
  class QuestionType(Enum):
8
9
  TEXT = "文本"
9
10
  SINGLE_CHOICE = "单选"
@@ -11,6 +12,7 @@ class QuestionType(Enum):
11
12
  RATING = "评分"
12
13
  LIKERT = "李克特量表"
13
14
 
15
+
14
16
  @dataclass
15
17
  class Question:
16
18
  content: str
@@ -19,7 +21,7 @@ class Question:
19
21
  options: List[str] = field(default_factory=list)
20
22
  min_rating: int = 1
21
23
  max_rating: int = 5
22
-
24
+
23
25
  def to_dict(self) -> dict:
24
26
  return {
25
27
  "content": self.content,
@@ -27,9 +29,10 @@ class Question:
27
29
  "required": self.required,
28
30
  "options": self.options,
29
31
  "min_rating": self.min_rating,
30
- "max_rating": self.max_rating
32
+ "max_rating": self.max_rating,
31
33
  }
32
34
 
35
+
33
36
  @dataclass
34
37
  class Survey:
35
38
  id: str
@@ -38,12 +41,12 @@ class Survey:
38
41
  questions: List[Question]
39
42
  responses: Dict[str, dict] = field(default_factory=dict)
40
43
  created_at: datetime = field(default_factory=datetime.now)
41
-
44
+
42
45
  def to_dict(self) -> dict:
43
46
  return {
44
47
  "id": self.id,
45
48
  "title": self.title,
46
49
  "description": self.description,
47
50
  "questions": [q.to_dict() for q in self.questions],
48
- "response_count": len(self.responses)
49
- }
51
+ "response_count": len(self.responses),
52
+ }
@@ -2,18 +2,21 @@ import time
2
2
  import functools
3
3
  import inspect
4
4
 
5
- CALLING_STRING = "function: `{func_name}` in \"{file_path}\", line {line_number}, arguments: `{arguments}` start time: `{start_time}` end time: `{end_time}` output: `{output}`"
5
+ CALLING_STRING = 'function: `{func_name}` in "{file_path}", line {line_number}, arguments: `{arguments}` start time: `{start_time}` end time: `{end_time}` output: `{output}`'
6
6
 
7
- __all__ =[
7
+ __all__ = [
8
8
  "record_call_aio",
9
9
  "record_call",
10
10
  "lock_decorator",
11
11
  ]
12
+
13
+
12
14
  def record_call_aio(record_function_calling: bool = True):
13
15
  """
14
16
  Decorator to log the async function call details if `record_function_calling` is True.
15
17
  """
16
- def decorator(func):
18
+
19
+ def decorator(func):
17
20
  async def wrapper(*args, **kwargs):
18
21
  cur_frame = inspect.currentframe()
19
22
  assert cur_frame is not None
@@ -40,14 +43,18 @@ def record_call_aio(record_function_calling: bool = True):
40
43
  )
41
44
  )
42
45
  return result
46
+
43
47
  return wrapper
48
+
44
49
  return decorator
45
50
 
51
+
46
52
  def record_call(record_function_calling: bool = True):
47
53
  """
48
54
  Decorator to log the function call details if `record_function_calling` is True.
49
55
  """
50
- def decorator(func):
56
+
57
+ def decorator(func):
51
58
  def wrapper(*args, **kwargs):
52
59
  cur_frame = inspect.currentframe()
53
60
  assert cur_frame is not None
@@ -74,9 +81,12 @@ def record_call(record_function_calling: bool = True):
74
81
  )
75
82
  )
76
83
  return result
84
+
77
85
  return wrapper
86
+
78
87
  return decorator
79
88
 
89
+
80
90
  def lock_decorator(func):
81
91
  async def wrapper(self, *args, **kwargs):
82
92
  lock = self._lock
@@ -1,6 +1,7 @@
1
1
  """Model response parser module."""
2
+
2
3
  from .parser_base import ParserBase
3
- from .json_parser import JsonDictParser,JsonObjectParser
4
+ from .json_parser import JsonDictParser, JsonObjectParser
4
5
  from .code_block_parser import CodeBlockParser
5
6
 
6
7