camel-ai 0.1.9__py3-none-any.whl → 0.2.3__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.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (102) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +334 -113
  3. camel/agents/knowledge_graph_agent.py +4 -6
  4. camel/bots/__init__.py +34 -0
  5. camel/bots/discord_app.py +138 -0
  6. camel/bots/slack/__init__.py +30 -0
  7. camel/bots/slack/models.py +158 -0
  8. camel/bots/slack/slack_app.py +255 -0
  9. camel/bots/telegram_bot.py +82 -0
  10. camel/configs/__init__.py +1 -2
  11. camel/configs/anthropic_config.py +2 -5
  12. camel/configs/base_config.py +6 -6
  13. camel/configs/gemini_config.py +1 -1
  14. camel/configs/groq_config.py +2 -3
  15. camel/configs/ollama_config.py +1 -2
  16. camel/configs/openai_config.py +2 -23
  17. camel/configs/samba_config.py +2 -2
  18. camel/configs/togetherai_config.py +1 -1
  19. camel/configs/vllm_config.py +1 -1
  20. camel/configs/zhipuai_config.py +2 -3
  21. camel/embeddings/openai_embedding.py +2 -2
  22. camel/loaders/__init__.py +2 -0
  23. camel/loaders/chunkr_reader.py +163 -0
  24. camel/loaders/firecrawl_reader.py +13 -45
  25. camel/loaders/unstructured_io.py +65 -29
  26. camel/messages/__init__.py +1 -0
  27. camel/messages/func_message.py +2 -2
  28. camel/models/__init__.py +2 -4
  29. camel/models/anthropic_model.py +32 -26
  30. camel/models/azure_openai_model.py +39 -36
  31. camel/models/base_model.py +31 -20
  32. camel/models/gemini_model.py +37 -29
  33. camel/models/groq_model.py +29 -23
  34. camel/models/litellm_model.py +44 -61
  35. camel/models/mistral_model.py +33 -30
  36. camel/models/model_factory.py +66 -76
  37. camel/models/nemotron_model.py +33 -23
  38. camel/models/ollama_model.py +42 -47
  39. camel/models/{openai_compatibility_model.py → openai_compatible_model.py} +36 -41
  40. camel/models/openai_model.py +60 -25
  41. camel/models/reka_model.py +30 -28
  42. camel/models/samba_model.py +82 -177
  43. camel/models/stub_model.py +2 -2
  44. camel/models/togetherai_model.py +37 -43
  45. camel/models/vllm_model.py +43 -50
  46. camel/models/zhipuai_model.py +33 -27
  47. camel/retrievers/auto_retriever.py +28 -10
  48. camel/retrievers/vector_retriever.py +72 -44
  49. camel/societies/babyagi_playing.py +6 -3
  50. camel/societies/role_playing.py +17 -3
  51. camel/storages/__init__.py +2 -0
  52. camel/storages/graph_storages/__init__.py +2 -0
  53. camel/storages/graph_storages/graph_element.py +3 -5
  54. camel/storages/graph_storages/nebula_graph.py +547 -0
  55. camel/storages/key_value_storages/json.py +6 -1
  56. camel/tasks/task.py +11 -4
  57. camel/tasks/task_prompt.py +4 -0
  58. camel/toolkits/__init__.py +28 -24
  59. camel/toolkits/arxiv_toolkit.py +155 -0
  60. camel/toolkits/ask_news_toolkit.py +653 -0
  61. camel/toolkits/base.py +2 -3
  62. camel/toolkits/code_execution.py +6 -7
  63. camel/toolkits/dalle_toolkit.py +6 -6
  64. camel/toolkits/{openai_function.py → function_tool.py} +34 -11
  65. camel/toolkits/github_toolkit.py +9 -10
  66. camel/toolkits/google_maps_toolkit.py +7 -14
  67. camel/toolkits/google_scholar_toolkit.py +146 -0
  68. camel/toolkits/linkedin_toolkit.py +7 -10
  69. camel/toolkits/math_toolkit.py +8 -8
  70. camel/toolkits/open_api_toolkit.py +5 -8
  71. camel/toolkits/reddit_toolkit.py +7 -10
  72. camel/toolkits/retrieval_toolkit.py +5 -9
  73. camel/toolkits/search_toolkit.py +9 -9
  74. camel/toolkits/slack_toolkit.py +11 -14
  75. camel/toolkits/twitter_toolkit.py +377 -454
  76. camel/toolkits/weather_toolkit.py +6 -6
  77. camel/toolkits/whatsapp_toolkit.py +177 -0
  78. camel/types/__init__.py +6 -1
  79. camel/types/enums.py +43 -85
  80. camel/types/openai_types.py +3 -0
  81. camel/types/unified_model_type.py +104 -0
  82. camel/utils/__init__.py +0 -2
  83. camel/utils/async_func.py +7 -7
  84. camel/utils/commons.py +40 -4
  85. camel/utils/token_counting.py +38 -214
  86. camel/workforce/__init__.py +6 -6
  87. camel/workforce/base.py +9 -5
  88. camel/workforce/prompts.py +179 -0
  89. camel/workforce/role_playing_worker.py +181 -0
  90. camel/workforce/{single_agent_node.py → single_agent_worker.py} +49 -23
  91. camel/workforce/task_channel.py +7 -8
  92. camel/workforce/utils.py +20 -50
  93. camel/workforce/{worker_node.py → worker.py} +15 -12
  94. camel/workforce/workforce.py +456 -19
  95. camel_ai-0.2.3.dist-info/LICENSE +201 -0
  96. {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/METADATA +40 -65
  97. {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/RECORD +98 -86
  98. {camel_ai-0.1.9.dist-info → camel_ai-0.2.3.dist-info}/WHEEL +1 -1
  99. camel/models/open_source_model.py +0 -170
  100. camel/workforce/manager_node.py +0 -299
  101. camel/workforce/role_playing_node.py +0 -168
  102. camel/workforce/workforce_prompt.py +0 -125
@@ -0,0 +1,181 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ from __future__ import annotations
15
+
16
+ import ast
17
+ from typing import Dict, List, Optional
18
+
19
+ from colorama import Fore
20
+
21
+ from camel.agents.chat_agent import ChatAgent
22
+ from camel.messages.base import BaseMessage
23
+ from camel.societies import RolePlaying
24
+ from camel.tasks.task import Task, TaskState
25
+ from camel.utils import print_text_animated
26
+ from camel.workforce.prompts import (
27
+ ROLEPLAY_PROCESS_TASK_PROMPT,
28
+ ROLEPLAY_SUMMARIZE_PROMPT,
29
+ )
30
+ from camel.workforce.utils import TaskResult
31
+ from camel.workforce.worker import Worker
32
+
33
+
34
+ class RolePlayingWorker(Worker):
35
+ r"""A worker node that contains a role playing.
36
+
37
+ Args:
38
+ description (str): Description of the node.
39
+ assistant_role_name (str): The role name of the assistant agent.
40
+ user_role_name (str): The role name of the user agent.
41
+ assistant_agent_kwargs (Optional[Dict], optional): The keyword
42
+ arguments to initialize the assistant agent in the role playing,
43
+ like the model name, etc. Defaults to None.
44
+ user_agent_kwargs (Optional[Dict], optional): The keyword arguments to
45
+ initialize the user agent in the role playing, like the model name,
46
+ etc. Defaults to None.
47
+ chat_turn_limit (int, optional): The maximum number of chat turns in
48
+ the role playing. Defaults to 3.
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ description: str,
54
+ assistant_role_name: str,
55
+ user_role_name: str,
56
+ assistant_agent_kwargs: Optional[Dict] = None,
57
+ user_agent_kwargs: Optional[Dict] = None,
58
+ chat_turn_limit: int = 3,
59
+ ) -> None:
60
+ super().__init__(description)
61
+ summ_sys_msg = BaseMessage.make_assistant_message(
62
+ role_name="Summarizer",
63
+ content="You are a good summarizer. You will be presented with "
64
+ "scenarios where an assistant and a user with specific roles "
65
+ "are trying to solve a task. Your job is summarizing the result "
66
+ "of the task based on the chat history.",
67
+ )
68
+ self.summarize_agent = ChatAgent(summ_sys_msg)
69
+ self.chat_turn_limit = chat_turn_limit
70
+ self.assistant_role_name = assistant_role_name
71
+ self.user_role_name = user_role_name
72
+ self.assistant_agent_kwargs = assistant_agent_kwargs
73
+ self.user_agent_kwargs = user_agent_kwargs
74
+
75
+ async def _process_task(
76
+ self, task: Task, dependencies: List[Task]
77
+ ) -> TaskState:
78
+ r"""Processes a task leveraging its dependencies through role-playing.
79
+
80
+ This method orchestrates a role-playing session between an AI
81
+ assistant and an AI user to process a given task. It initiates with a
82
+ generated prompt based on the task and its dependencies, conducts a
83
+ dialogue up to a specified chat turn limit, and then summarizes the
84
+ dialogue to determine the task's outcome.
85
+
86
+ Args:
87
+ task (Task): The task object to be processed, containing necessary
88
+ details like content and type.
89
+ dependencies (List[Task]): A list of task objects that the current
90
+ task depends on.
91
+
92
+ Returns:
93
+ TaskState: `TaskState.DONE` if processed successfully, otherwise
94
+ `TaskState.FAILED`.
95
+ """
96
+ dependency_tasks_info = self._get_dep_tasks_info(dependencies)
97
+ prompt = ROLEPLAY_PROCESS_TASK_PROMPT.format(
98
+ content=task.content,
99
+ dependency_task_info=dependency_tasks_info,
100
+ additional_info=task.additional_info,
101
+ )
102
+ role_play_session = RolePlaying(
103
+ assistant_role_name=self.assistant_role_name,
104
+ user_role_name=self.user_role_name,
105
+ assistant_agent_kwargs=self.assistant_agent_kwargs,
106
+ user_agent_kwargs=self.user_agent_kwargs,
107
+ task_prompt=prompt,
108
+ with_task_specify=False,
109
+ )
110
+ n = 0
111
+ input_msg = role_play_session.init_chat()
112
+ chat_history = []
113
+ while n < self.chat_turn_limit:
114
+ n += 1
115
+ assistant_response, user_response = role_play_session.step(
116
+ input_msg
117
+ )
118
+
119
+ if assistant_response.terminated:
120
+ reason = assistant_response.info['termination_reasons']
121
+ print(
122
+ f"{Fore.GREEN}AI Assistant terminated. Reason: "
123
+ f"{reason}.{Fore.RESET}"
124
+ )
125
+ break
126
+
127
+ if user_response.terminated:
128
+ reason = user_response.info['termination_reasons']
129
+ print(
130
+ f"{Fore.GREEN}AI User terminated. Reason: {reason}."
131
+ f"{Fore.RESET}"
132
+ )
133
+ break
134
+
135
+ print_text_animated(
136
+ f"{Fore.BLUE}AI User:\n\n{user_response.msg.content}"
137
+ f"{Fore.RESET}\n",
138
+ delay=0.005,
139
+ )
140
+ chat_history.append(f"AI User: {user_response.msg.content}")
141
+
142
+ print_text_animated(
143
+ f"{Fore.GREEN}AI Assistant:{Fore.RESET}", delay=0.005
144
+ )
145
+
146
+ for func_record in assistant_response.info['tool_calls']:
147
+ print(func_record)
148
+
149
+ print_text_animated(
150
+ f"\n{Fore.GREEN}{assistant_response.msg.content}"
151
+ f"{Fore.RESET}\n",
152
+ delay=0.005,
153
+ )
154
+ chat_history.append(
155
+ f"AI Assistant: {assistant_response.msg.content}"
156
+ )
157
+
158
+ if "CAMEL_TASK_DONE" in user_response.msg.content:
159
+ break
160
+
161
+ input_msg = assistant_response.msg
162
+
163
+ chat_history_str = "\n".join(chat_history)
164
+ prompt = ROLEPLAY_SUMMARIZE_PROMPT.format(
165
+ user_role=self.user_role_name,
166
+ assistant_role=self.assistant_role_name,
167
+ content=task.content,
168
+ chat_history=chat_history_str,
169
+ additional_info=task.additional_info,
170
+ )
171
+ req = BaseMessage.make_user_message(
172
+ role_name="User",
173
+ content=prompt,
174
+ )
175
+ response = self.summarize_agent.step(req, response_format=TaskResult)
176
+ result_dict = ast.literal_eval(response.msg.content)
177
+ task_result = TaskResult(**result_dict)
178
+ task.result = task_result.content
179
+
180
+ print(f"Task result: {task.result}\n")
181
+ return TaskState.DONE
@@ -13,32 +13,41 @@
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
14
  from __future__ import annotations
15
15
 
16
- from typing import List
16
+ import ast
17
+ from typing import Any, List
17
18
 
18
- from camel.agents.base import BaseAgent
19
+ from colorama import Fore
20
+
21
+ from camel.agents import ChatAgent
19
22
  from camel.messages.base import BaseMessage
20
23
  from camel.tasks.task import Task, TaskState
21
- from camel.workforce.utils import parse_task_result_resp
22
- from camel.workforce.worker_node import WorkerNode
23
- from camel.workforce.workforce_prompt import PROCESS_TASK_PROMPT
24
+ from camel.utils import print_text_animated
25
+ from camel.workforce.prompts import PROCESS_TASK_PROMPT
26
+ from camel.workforce.utils import TaskResult
27
+ from camel.workforce.worker import Worker
24
28
 
25
29
 
26
- class SingleAgentNode(WorkerNode):
30
+ class SingleAgentWorker(Worker):
27
31
  r"""A worker node that consists of a single agent.
28
32
 
29
33
  Args:
30
34
  description (str): Description of the node.
31
- worker (BaseAgent): Worker of the node. A single agent.
35
+ worker (ChatAgent): Worker of the node. A single agent.
32
36
  """
33
37
 
34
38
  def __init__(
35
39
  self,
36
40
  description: str,
37
- worker: BaseAgent,
41
+ worker: ChatAgent,
38
42
  ) -> None:
39
43
  super().__init__(description)
40
44
  self.worker = worker
41
45
 
46
+ def reset(self) -> Any:
47
+ r"""Resets the worker to its initial state."""
48
+ super().reset()
49
+ self.worker.reset()
50
+
42
51
  async def _process_task(
43
52
  self, task: Task, dependencies: List[Task]
44
53
  ) -> TaskState:
@@ -57,21 +66,38 @@ class SingleAgentNode(WorkerNode):
57
66
  TaskState: `TaskState.DONE` if processed successfully, otherwise
58
67
  `TaskState.FAILED`.
59
68
  """
69
+ dependency_tasks_info = self._get_dep_tasks_info(dependencies)
70
+ prompt = PROCESS_TASK_PROMPT.format(
71
+ content=task.content,
72
+ dependency_tasks_info=dependency_tasks_info,
73
+ additional_info=task.additional_info,
74
+ )
75
+ req = BaseMessage.make_user_message(
76
+ role_name="User",
77
+ content=prompt,
78
+ )
60
79
  try:
61
- dependency_tasks_info = self._get_dep_tasks_info(dependencies)
62
- prompt = PROCESS_TASK_PROMPT.format(
63
- content=task.content,
64
- type=task.type,
65
- dependency_task_info=dependency_tasks_info,
66
- )
67
- req = BaseMessage.make_user_message(
68
- role_name="User",
69
- content=prompt,
80
+ response = self.worker.step(req, response_format=TaskResult)
81
+ except Exception as e:
82
+ print(
83
+ f"{Fore.RED}Error occurred while processing task {task.id}:"
84
+ f"\n{e}{Fore.RESET}"
70
85
  )
71
- response = self.worker.step(req)
72
- # print("info['tool_calls']:", response.info['tool_calls'])
73
- task.result = parse_task_result_resp(response.msg.content)
74
- print('Task result:', task.result, '\n')
75
- return TaskState.DONE
76
- except Exception:
77
86
  return TaskState.FAILED
87
+
88
+ print(f"======\n{Fore.GREEN}Reply from {self}:{Fore.RESET}")
89
+
90
+ result_dict = ast.literal_eval(response.msg.content)
91
+ task_result = TaskResult(**result_dict)
92
+
93
+ color = Fore.RED if task_result.failed else Fore.GREEN
94
+ print_text_animated(
95
+ f"\n{color}{task_result.content}{Fore.RESET}\n======",
96
+ delay=0.005,
97
+ )
98
+
99
+ if task_result.failed:
100
+ return TaskState.FAILED
101
+
102
+ task.result = task_result.content
103
+ return TaskState.DONE
@@ -21,12 +21,13 @@ from camel.tasks import Task
21
21
  class PacketStatus(Enum):
22
22
  r"""The status of a packet. The packet can be in one of the following
23
23
  states:
24
+
24
25
  - ``SENT``: The packet has been sent to a worker.
25
26
  - ``RETURNED``: The packet has been returned by the worker, meaning that
26
- the status of the task inside has been updated.
27
+ the status of the task inside has been updated.
27
28
  - ``ARCHIVED``: The packet has been archived, meaning that the content of
28
- the task inside will not be changed. The task is considered
29
- as a dependency.
29
+ the task inside will not be changed. The task is considered
30
+ as a dependency.
30
31
  """
31
32
 
32
33
  SENT = "SENT"
@@ -61,7 +62,7 @@ class Packet:
61
62
  publisher_id: str,
62
63
  assignee_id: Optional[str] = None,
63
64
  status: PacketStatus = PacketStatus.SENT,
64
- ):
65
+ ) -> None:
65
66
  self.task = task
66
67
  self.publisher_id = publisher_id
67
68
  self.assignee_id = assignee_id
@@ -83,7 +84,6 @@ class TaskChannel:
83
84
  self._task_dict: Dict[str, Packet] = {}
84
85
 
85
86
  async def get_returned_task_by_publisher(self, publisher_id: str) -> Task:
86
- await self.print_channel()
87
87
  async with self._condition:
88
88
  while True:
89
89
  for task_id in self._task_id_list:
@@ -167,7 +167,6 @@ class TaskChannel:
167
167
  raise ValueError(f"Task {task_id} not found.")
168
168
  return self._task_dict[task_id].task
169
169
 
170
- async def print_channel(self):
170
+ async def get_channel_debug_info(self) -> str:
171
171
  async with self._condition:
172
- print(self._task_dict)
173
- print(self._task_id_list)
172
+ return str(self._task_dict) + '\n' + str(self._task_id_list)
camel/workforce/utils.py CHANGED
@@ -11,66 +11,36 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
- import re
15
14
  from functools import wraps
16
15
  from typing import Callable
17
16
 
17
+ from pydantic import BaseModel, Field
18
18
 
19
- class NodeConf:
20
- def __init__(self, role: str, system: str, description: str):
21
- self.role = role
22
- self.system = system
23
- self.description = description
24
19
 
25
-
26
- # TODO: integrate structured response directly instead of parsing
27
- def parse_create_node_resp(response: str) -> NodeConf:
28
- r"""Parses the response of the new workforce creation from the manager
29
- agent."""
30
- config = re.search(r"(<workforce>.*</workforce>)", response, re.DOTALL)
31
- if config is None:
32
- raise ValueError("No workforce configuration found in the response.")
33
- config_raw = config.group(1)
34
-
35
- try:
36
- import xml.etree.ElementTree as ET
37
-
38
- root = ET.fromstring(config_raw)
39
- workforce_info = {child.tag: child.text for child in root}
40
- except Exception as e:
41
- raise ValueError(f"Failed to parse workforce configuration: {e}")
42
-
43
- if (
44
- "role" not in workforce_info
45
- or "system" not in workforce_info
46
- or "description" not in workforce_info
47
- ):
48
- raise ValueError("Missing required fields in workforce configuration.")
49
-
50
- return NodeConf(
51
- role=workforce_info["role"] or "",
52
- system=workforce_info["system"] or "",
53
- description=workforce_info["description"] or "",
20
+ class WorkerConf(BaseModel):
21
+ role: str = Field(
22
+ description="The role of the agent working in the work node."
23
+ )
24
+ sys_msg: str = Field(
25
+ description="The system message that will be sent to the agent in "
26
+ "the node."
27
+ )
28
+ description: str = Field(
29
+ description="The description of the new work node itself."
54
30
  )
55
31
 
56
32
 
57
- def parse_assign_task_resp(response: str) -> str:
58
- r"""Parses the response of the task assignment from the manager agent."""
59
- assignee_id = re.search(r"<id>(.*)</id>", response)
60
- if assignee_id is None:
61
- raise ValueError("No assignee found in the response.")
62
- return assignee_id.group(1)
33
+ class TaskResult(BaseModel):
34
+ content: str = Field(description="The result of the task.")
35
+ failed: bool = Field(
36
+ description="Flag indicating whether the task processing failed."
37
+ )
63
38
 
64
39
 
65
- def parse_task_result_resp(response: str) -> str:
66
- r"""Parses the result of the task from the signle agent workforce."""
67
- task_result = re.search(r"<result>(.*)</result>", response, re.DOTALL)
68
- failed_tag = re.search(r"<failed></failed>", response)
69
- if failed_tag:
70
- task_result = None
71
- if task_result is None:
72
- raise ValueError("No result found in the response.")
73
- return task_result.group(1)
40
+ class TaskAssignResult(BaseModel):
41
+ assignee_id: str = Field(
42
+ description="The ID of the workforce that is assigned to the task."
43
+ )
74
44
 
75
45
 
76
46
  def check_if_running(running: bool) -> Callable:
@@ -13,6 +13,7 @@
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
14
  from __future__ import annotations
15
15
 
16
+ import logging
16
17
  from abc import ABC, abstractmethod
17
18
  from typing import List
18
19
 
@@ -23,8 +24,10 @@ from camel.workforce.base import BaseNode
23
24
  from camel.workforce.task_channel import TaskChannel
24
25
  from camel.workforce.utils import check_if_running
25
26
 
27
+ logger = logging.getLogger(__name__)
26
28
 
27
- class WorkerNode(BaseNode, ABC):
29
+
30
+ class Worker(BaseNode, ABC):
28
31
  r"""A worker node that works on tasks. It is the basic unit of task
29
32
  processing in the workforce system.
30
33
 
@@ -33,15 +36,15 @@ class WorkerNode(BaseNode, ABC):
33
36
 
34
37
  """
35
38
 
36
- # TODO: Make RolePlaying and Agent scceed from one parent class, so that
37
- # we don't need two different classes for the worker node.
38
-
39
39
  def __init__(
40
40
  self,
41
41
  description: str,
42
42
  ) -> None:
43
43
  super().__init__(description)
44
44
 
45
+ def __repr__(self):
46
+ return f"Worker node {self.node_id} ({self.description})"
47
+
45
48
  @abstractmethod
46
49
  async def _process_task(
47
50
  self, task: Task, dependencies: List[Task]
@@ -52,6 +55,7 @@ class WorkerNode(BaseNode, ABC):
52
55
  'DONE' if the task is successfully processed,
53
56
  'FAILED' if the processing fails.
54
57
  """
58
+ pass
55
59
 
56
60
  async def _get_assigned_task(self) -> Task:
57
61
  r"""Get the task assigned to this node from the channel."""
@@ -80,27 +84,26 @@ class WorkerNode(BaseNode, ABC):
80
84
  indefinitely.
81
85
  """
82
86
  self._running = True
83
- print(f"{Fore.GREEN}Worker node {self.node_id} started.{Fore.RESET}")
87
+ logger.info(f"{self} started.")
84
88
 
85
89
  while True:
86
- # get the earliest task assigned to this node
90
+ # Get the earliest task assigned to this node
87
91
  task = await self._get_assigned_task()
88
92
  print(
89
- f'worker node {self.node_id} get task:',
90
- task.id,
91
- task.content,
93
+ f"{Fore.YELLOW}{self} get task {task.id}: {task.content}"
94
+ f"{Fore.RESET}"
92
95
  )
93
- # get the Task instance of dependencies
96
+ # Get the Task instance of dependencies
94
97
  dependency_ids = await self._channel.get_dependency_ids()
95
98
  task_dependencies = [
96
99
  await self._channel.get_task_by_id(dep_id)
97
100
  for dep_id in dependency_ids
98
101
  ]
99
102
 
100
- # process the task
103
+ # Process the task
101
104
  task_state = await self._process_task(task, task_dependencies)
102
105
 
103
- # update the result and status of the task
106
+ # Update the result and status of the task
104
107
  task.set_state(task_state)
105
108
 
106
109
  await self._channel.return_task(task.id)