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

@@ -0,0 +1,547 @@
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
+
15
+ import time
16
+ from typing import TYPE_CHECKING, Any, Dict, List, Tuple
17
+
18
+ if TYPE_CHECKING:
19
+ from nebula3.data.ResultSet import ( # type: ignore[import-untyped]
20
+ ResultSet,
21
+ )
22
+ from nebula3.gclient.net import ( # type: ignore[import-untyped]
23
+ ConnectionPool,
24
+ Session,
25
+ )
26
+
27
+ from camel.storages.graph_storages.base import BaseGraphStorage
28
+ from camel.storages.graph_storages.graph_element import (
29
+ GraphElement,
30
+ )
31
+ from camel.utils.commons import dependencies_required
32
+
33
+ MAX_RETRIES = 5
34
+ RETRY_DELAY = 3
35
+
36
+
37
+ class NebulaGraph(BaseGraphStorage):
38
+ @dependencies_required('nebula3')
39
+ def __init__(
40
+ self, host, username, password, space, port=9669, timeout=10000
41
+ ):
42
+ r"""Initializes the NebulaGraph client.
43
+
44
+ Args:
45
+ host (str): The host address of the NebulaGraph service.
46
+ username (str): The username for authentication.
47
+ password (str): The password for authentication.
48
+ space (str): The graph space to use. If it doesn't exist, a new
49
+ one will be created.
50
+ port (int, optional): The port number for the connection.
51
+ (default: :obj:`9669`)
52
+ timeout (int, optional): The connection timeout in milliseconds.
53
+ (default: :obj:`10000`)
54
+ """
55
+ self.host = host
56
+ self.username = username
57
+ self.password = password
58
+ self.space = space
59
+ self.timeout = timeout
60
+ self.port = port
61
+ self.schema: str = ""
62
+ self.structured_schema: Dict[str, Any] = {}
63
+ self.connection_pool = self._init_connection_pool()
64
+ self.session = self._get_session()
65
+
66
+ def _init_connection_pool(self) -> "ConnectionPool":
67
+ r"""Initialize the connection pool.
68
+
69
+ Returns:
70
+ ConnectionPool: A connection pool instance.
71
+
72
+ Raises:
73
+ Exception: If the connection pool initialization fails.
74
+ """
75
+ from nebula3.Config import Config # type: ignore[import-untyped]
76
+ from nebula3.gclient.net import ConnectionPool
77
+
78
+ config = Config()
79
+ config.max_connection_pool_size = 10
80
+ config.timeout = self.timeout
81
+
82
+ # Create the connection pool
83
+ connection_pool = ConnectionPool()
84
+
85
+ # Initialize the connection pool with Nebula Graph's address and port
86
+ if not connection_pool.init([(self.host, self.port)], config):
87
+ raise Exception("Failed to initialize the connection pool")
88
+
89
+ return connection_pool
90
+
91
+ def _get_session(self) -> "Session":
92
+ r"""Get a session from the connection pool.
93
+
94
+ Returns:
95
+ Session: A session object connected to NebulaGraph.
96
+
97
+ Raises:
98
+ Exception: If session creation or space usage fails.
99
+ """
100
+ session = self.connection_pool.get_session(
101
+ self.username, self.password
102
+ )
103
+ if not session:
104
+ raise Exception("Failed to create a session")
105
+
106
+ # Use the specified space
107
+ session.execute(
108
+ f"CREATE SPACE IF NOT EXISTS {self.space} "
109
+ "(vid_type=FIXED_STRING(30));"
110
+ )
111
+
112
+ for attempt in range(MAX_RETRIES):
113
+ res = session.execute(f"USE {self.space};")
114
+
115
+ if res.is_succeeded():
116
+ return session
117
+
118
+ if attempt < MAX_RETRIES - 1:
119
+ time.sleep(RETRY_DELAY)
120
+ else:
121
+ # Final attempt failed, raise an exception
122
+ raise Exception(
123
+ f"Failed to execute `{self.space}` after "
124
+ f"{MAX_RETRIES} attempts: {res.error_msg()}"
125
+ )
126
+
127
+ @property
128
+ def get_client(self) -> Any:
129
+ r"""Get the underlying graph storage client."""
130
+ return self.session
131
+
132
+ def query(self, query: str) -> "ResultSet": # type:ignore[override]
133
+ r"""Execute a query on the graph store.
134
+
135
+ Args:
136
+ query (str): The Cypher-like query to be executed.
137
+
138
+ Returns:
139
+ ResultSet: The result set of the query execution.
140
+
141
+ Raises:
142
+ ValueError: If the query execution fails.
143
+ """
144
+ try:
145
+ # Get the session
146
+ result_set = self.session.execute(query)
147
+ return result_set
148
+
149
+ except Exception as e:
150
+ raise ValueError(f"Query execution error: {e!s}")
151
+
152
+ def get_relationship_types(self) -> List[str]:
153
+ r"""Retrieve relationship types from the graph.
154
+
155
+ Returns:
156
+ List[str]: A list of relationship (edge) type names.
157
+ """
158
+ # Query all edge types
159
+ result = self.query('SHOW EDGES')
160
+ rel_types = []
161
+
162
+ # Extract relationship type names
163
+ for row in result.rows():
164
+ edge_name = row.values[0].get_sVal().decode('utf-8')
165
+ rel_types.append(edge_name)
166
+
167
+ return rel_types
168
+
169
+ def add_graph_elements(
170
+ self,
171
+ graph_elements: List[GraphElement],
172
+ ) -> None:
173
+ r"""Add graph elements (nodes and relationships) to the graph.
174
+
175
+ Args:
176
+ graph_elements (List[GraphElement]): A list of graph elements
177
+ containing nodes and relationships.
178
+ """
179
+ nodes = self._extract_nodes(graph_elements)
180
+ for node in nodes:
181
+ self.add_node(node['id'], node['type'])
182
+
183
+ relationships = self._extract_relationships(graph_elements)
184
+ for rel in relationships:
185
+ self.add_triplet(rel['subj']['id'], rel['obj']['id'], rel['type'])
186
+
187
+ def ensure_edge_type_exists(
188
+ self,
189
+ edge_type: str,
190
+ ) -> None:
191
+ r"""Ensures that a specified edge type exists in the NebulaGraph
192
+ database. If the edge type already exists, this method does nothing.
193
+
194
+ Args:
195
+ edge_type (str): The name of the edge type to be created.
196
+
197
+ Raises:
198
+ Exception: If the edge type creation fails after multiple retry
199
+ attempts, an exception is raised with the error message.
200
+ """
201
+ create_edge_stmt = f'CREATE EDGE IF NOT EXISTS {edge_type}()'
202
+
203
+ for attempt in range(MAX_RETRIES):
204
+ res = self.query(create_edge_stmt)
205
+ if res.is_succeeded():
206
+ return # Tag creation succeeded, exit the method
207
+
208
+ if attempt < MAX_RETRIES - 1:
209
+ time.sleep(RETRY_DELAY)
210
+ else:
211
+ # Final attempt failed, raise an exception
212
+ raise Exception(
213
+ f"Failed to create tag `{edge_type}` after "
214
+ f"{MAX_RETRIES} attempts: {res.error_msg()}"
215
+ )
216
+
217
+ def ensure_tag_exists(self, tag_name: str) -> None:
218
+ r"""Ensures a tag is created in the NebulaGraph database. If the tag
219
+ already exists, it does nothing.
220
+
221
+ Args:
222
+ tag_name (str): The name of the tag to be created.
223
+
224
+ Raises:
225
+ Exception: If the tag creation fails after retries, an exception
226
+ is raised with the error message.
227
+ """
228
+
229
+ create_tag_stmt = f'CREATE TAG IF NOT EXISTS {tag_name}()'
230
+
231
+ for attempt in range(MAX_RETRIES):
232
+ res = self.query(create_tag_stmt)
233
+ if res.is_succeeded():
234
+ return # Tag creation succeeded, exit the method
235
+
236
+ if attempt < MAX_RETRIES - 1:
237
+ time.sleep(RETRY_DELAY)
238
+ else:
239
+ # Final attempt failed, raise an exception
240
+ raise Exception(
241
+ f"Failed to create tag `{tag_name}` after "
242
+ f"{MAX_RETRIES} attempts: {res.error_msg()}"
243
+ )
244
+
245
+ def add_node(
246
+ self,
247
+ node_id: str,
248
+ tag_name: str,
249
+ ) -> None:
250
+ r"""Add a node with the specified tag and properties.
251
+
252
+ Args:
253
+ node_id (str): The ID of the node.
254
+ tag_name (str): The tag name of the node.
255
+ """
256
+ self.ensure_tag_exists(tag_name)
257
+
258
+ # Insert node without properties
259
+ insert_stmt = (
260
+ f'INSERT VERTEX IF NOT EXISTS {tag_name}() VALUES "{node_id}":()'
261
+ )
262
+
263
+ for attempt in range(MAX_RETRIES):
264
+ res = self.query(insert_stmt)
265
+ if res.is_succeeded():
266
+ return # Tag creation succeeded, exit the method
267
+
268
+ if attempt < MAX_RETRIES - 1:
269
+ time.sleep(RETRY_DELAY)
270
+ else:
271
+ # Final attempt failed, raise an exception
272
+ raise Exception(
273
+ f"Failed to add node `{node_id}` after"
274
+ f" {MAX_RETRIES} attempts: {res.error_msg()}"
275
+ )
276
+
277
+ def _extract_nodes(self, graph_elements: List[Any]) -> List[Dict]:
278
+ r"""Extracts unique nodes from graph elements.
279
+
280
+ Args:
281
+ graph_elements (List[Any]): A list of graph elements containing
282
+ nodes.
283
+
284
+ Returns:
285
+ List[Dict]: A list of dictionaries representing nodes.
286
+ """
287
+ nodes = []
288
+ seen_nodes = set()
289
+ for graph_element in graph_elements:
290
+ for node in graph_element.nodes:
291
+ node_key = (node.id, node.type)
292
+ if node_key not in seen_nodes:
293
+ nodes.append(
294
+ {
295
+ 'id': node.id,
296
+ 'type': node.type,
297
+ 'properties': node.properties,
298
+ }
299
+ )
300
+ seen_nodes.add(node_key)
301
+ return nodes
302
+
303
+ def _extract_relationships(self, graph_elements: List[Any]) -> List[Dict]:
304
+ r"""Extracts relationships from graph elements.
305
+
306
+ Args:
307
+ graph_elements (List[Any]): A list of graph elements containing
308
+ relationships.
309
+
310
+ Returns:
311
+ List[Dict]: A list of dictionaries representing relationships.
312
+ """
313
+ relationships = []
314
+ for graph_element in graph_elements:
315
+ for rel in graph_element.relationships:
316
+ relationship_dict = {
317
+ 'subj': {'id': rel.subj.id, 'type': rel.subj.type},
318
+ 'obj': {'id': rel.obj.id, 'type': rel.obj.type},
319
+ 'type': rel.type,
320
+ }
321
+ relationships.append(relationship_dict)
322
+ return relationships
323
+
324
+ def refresh_schema(self) -> None:
325
+ r"""Refreshes the schema by fetching the latest schema details."""
326
+ self.schema = self.get_schema()
327
+ self.structured_schema = self.get_structured_schema
328
+
329
+ @property
330
+ def get_structured_schema(self) -> Dict[str, Any]:
331
+ r"""Generates a structured schema consisting of node and relationship
332
+ properties, relationships, and metadata.
333
+
334
+ Returns:
335
+ Dict[str, Any]: A dictionary representing the structured schema.
336
+ """
337
+ _, node_properties = self.get_node_properties()
338
+ _, rel_properties = self.get_relationship_properties()
339
+ relationships = self.get_relationship_types()
340
+ index = self.get_indexes()
341
+
342
+ # Build structured_schema
343
+ structured_schema = {
344
+ "node_props": {
345
+ el["labels"]: el["properties"] for el in node_properties
346
+ },
347
+ "rel_props": {
348
+ el["type"]: el["properties"] for el in rel_properties
349
+ },
350
+ "relationships": relationships,
351
+ "metadata": {"index": index},
352
+ }
353
+
354
+ return structured_schema
355
+
356
+ def get_schema(self):
357
+ r"""Generates a schema string describing node and relationship
358
+ properties and relationships.
359
+
360
+ Returns:
361
+ str: A string describing the schema.
362
+ """
363
+ # Get all node and relationship properties
364
+ formatted_node_props, _ = self.get_node_properties()
365
+ formatted_rel_props, _ = self.get_relationship_properties()
366
+ formatted_rels = self.get_relationship_types()
367
+
368
+ # Generate schema string
369
+ schema = "\n".join(
370
+ [
371
+ "Node properties are the following:",
372
+ ", ".join(formatted_node_props),
373
+ "Relationship properties are the following:",
374
+ ", ".join(formatted_rel_props),
375
+ "The relationships are the following:",
376
+ ", ".join(formatted_rels),
377
+ ]
378
+ )
379
+
380
+ return schema
381
+
382
+ def get_indexes(self):
383
+ r"""Fetches the tag indexes from the database.
384
+
385
+ Returns:
386
+ List[str]: A list of tag index names.
387
+ """
388
+ result = self.query('SHOW TAG INDEXES')
389
+ indexes = []
390
+
391
+ # Get tag indexes
392
+ for row in result.rows():
393
+ index_name = row.values[0].get_sVal().decode('utf-8')
394
+ indexes.append(index_name)
395
+
396
+ return indexes
397
+
398
+ def add_triplet(
399
+ self,
400
+ subj: str,
401
+ obj: str,
402
+ rel: str,
403
+ ) -> None:
404
+ r"""Adds a relationship (triplet) between two entities in the Nebula
405
+ Graph database.
406
+
407
+ Args:
408
+ subj (str): The identifier for the subject entity.
409
+ obj (str): The identifier for the object entity.
410
+ rel (str): The relationship between the subject and object.
411
+ """
412
+ self.ensure_tag_exists(subj)
413
+ self.ensure_tag_exists(obj)
414
+ self.ensure_edge_type_exists(rel)
415
+ self.add_node(node_id=subj, tag_name=subj)
416
+ self.add_node(node_id=obj, tag_name=obj)
417
+
418
+ # Avoid latenicy
419
+ time.sleep(1)
420
+
421
+ insert_stmt = (
422
+ f'INSERT EDGE IF NOT EXISTS {rel}() VALUES "{subj}"->"{obj}":();'
423
+ )
424
+
425
+ res = self.query(insert_stmt)
426
+ if not res.is_succeeded():
427
+ raise Exception(
428
+ f'create relationship `]{subj}` -> `{obj}`'
429
+ + f'failed: {res.error_msg()}'
430
+ )
431
+
432
+ def delete_triplet(self, subj: str, obj: str, rel: str) -> None:
433
+ r"""Deletes a specific triplet (relationship between two entities)
434
+ from the Nebula Graph database.
435
+
436
+ Args:
437
+ subj (str): The identifier for the subject entity.
438
+ obj (str): The identifier for the object entity.
439
+ rel (str): The relationship between the subject and object.
440
+ """
441
+ delete_edge_query = f'DELETE EDGE {rel} "{subj}"->"{obj}";'
442
+ self.query(delete_edge_query)
443
+
444
+ if not self._check_edges(subj):
445
+ self.delete_entity(subj)
446
+ if not self._check_edges(obj):
447
+ self.delete_entity(obj)
448
+
449
+ def delete_entity(self, entity_id: str) -> None:
450
+ r"""Deletes an entity (vertex) from the graph.
451
+
452
+ Args:
453
+ entity_id (str): The identifier of the entity to be deleted.
454
+ """
455
+ delete_vertex_query = f'DELETE VERTEX "{entity_id}";'
456
+ self.query(delete_vertex_query)
457
+
458
+ def _check_edges(self, entity_id: str) -> bool:
459
+ r"""Checks if an entity has any remaining edges in the graph.
460
+
461
+ Args:
462
+ entity_id (str): The identifier of the entity.
463
+
464
+ Returns:
465
+ bool: :obj:`True` if the entity has edges, :obj:`False` otherwise.
466
+ """
467
+ # Combine the outgoing and incoming edge count query
468
+ check_query = f"""
469
+ (GO FROM {entity_id} OVER * YIELD count(*) as out_count)
470
+ UNION
471
+ (GO FROM {entity_id} REVERSELY OVER * YIELD count(*) as in_count)
472
+ """
473
+
474
+ # Execute the query
475
+ result = self.query(check_query)
476
+
477
+ # Check if the result contains non-zero edges
478
+ if result.is_succeeded():
479
+ rows = result.rows()
480
+ total_count = sum(int(row.values[0].get_iVal()) for row in rows)
481
+ return total_count > 0
482
+ else:
483
+ return False
484
+
485
+ def get_node_properties(self) -> Tuple[List[str], List[Dict[str, Any]]]:
486
+ r"""Retrieve node properties from the graph.
487
+
488
+ Returns:
489
+ Tuple[List[str], List[Dict[str, Any]]]: A tuple where the first
490
+ element is a list of node schema properties, and the second
491
+ element is a list of dictionaries representing node structures.
492
+ """
493
+ # Query all tags
494
+ result = self.query('SHOW TAGS')
495
+ node_schema_props = []
496
+ node_structure_props = []
497
+
498
+ # Iterate through each tag to get its properties
499
+ for row in result.rows():
500
+ tag_name = row.values[0].get_sVal().decode('utf-8')
501
+ describe_result = self.query(f'DESCRIBE TAG {tag_name}')
502
+ properties = []
503
+
504
+ for prop_row in describe_result.rows():
505
+ prop_name = prop_row.values[0].get_sVal().decode('utf-8')
506
+ node_schema_props.append(f"{tag_name}.{prop_name}")
507
+ properties.append(prop_name)
508
+
509
+ node_structure_props.append(
510
+ {"labels": tag_name, "properties": properties}
511
+ )
512
+
513
+ return node_schema_props, node_structure_props
514
+
515
+ def get_relationship_properties(
516
+ self,
517
+ ) -> Tuple[List[str], List[Dict[str, Any]]]:
518
+ r"""Retrieve relationship (edge) properties from the graph.
519
+
520
+ Returns:
521
+ Tuple[List[str], List[Dict[str, Any]]]: A tuple where the first
522
+ element is a list of relationship schema properties, and the
523
+ second element is a list of dictionaries representing
524
+ relationship structures.
525
+ """
526
+
527
+ # Query all edge types
528
+ result = self.query('SHOW EDGES')
529
+ rel_schema_props = []
530
+ rel_structure_props = []
531
+
532
+ # Iterate through each edge type to get its properties
533
+ for row in result.rows():
534
+ edge_name = row.values[0].get_sVal().decode('utf-8')
535
+ describe_result = self.query(f'DESCRIBE EDGE {edge_name}')
536
+ properties = []
537
+
538
+ for prop_row in describe_result.rows():
539
+ prop_name = prop_row.values[0].get_sVal().decode('utf-8')
540
+ rel_schema_props.append(f"{edge_name}.{prop_name}")
541
+ properties.append(prop_name)
542
+
543
+ rel_structure_props.append(
544
+ {"type": edge_name, "properties": properties}
545
+ )
546
+
547
+ return rel_schema_props, rel_structure_props
camel/tasks/task.py CHANGED
@@ -93,6 +93,10 @@ class Task(BaseModel):
93
93
 
94
94
  result: Optional[str] = ""
95
95
 
96
+ failure_count: int = 0
97
+
98
+ additional_info: Optional[str] = None
99
+
96
100
  @classmethod
97
101
  def from_message(cls, message: BaseMessage) -> "Task":
98
102
  r"""Create a task from a message.
@@ -193,7 +197,7 @@ class Task(BaseModel):
193
197
  def decompose(
194
198
  self,
195
199
  agent: ChatAgent,
196
- template: TextPrompt = TASK_DECOMPOSE_PROMPT,
200
+ prompt: Optional[str] = None,
197
201
  task_parser: Callable[[str, str], List["Task"]] = parse_response,
198
202
  ) -> List["Task"]:
199
203
  r"""Decompose a task to a list of sub-tasks. It can be used for data
@@ -201,8 +205,8 @@ class Task(BaseModel):
201
205
 
202
206
  Args:
203
207
  agent (ChatAgent): An agent that used to decompose the task.
204
- template (TextPrompt): The prompt template to decompose
205
- task. If not provided, the default template will be used.
208
+ prompt (str, optional): A prompt to decompose the task. If not
209
+ provided, the default prompt will be used.
206
210
  task_parser (Callable[[str, str], List[Task]], optional): A
207
211
  function to extract Task from response. If not provided,
208
212
  the default parse_response will be used.
@@ -212,7 +216,7 @@ class Task(BaseModel):
212
216
  """
213
217
 
214
218
  role_name = agent.role_name
215
- content = template.format(
219
+ content = prompt or TASK_DECOMPOSE_PROMPT.format(
216
220
  role_name=role_name,
217
221
  content=self.content,
218
222
  )
@@ -221,6 +225,8 @@ class Task(BaseModel):
221
225
  )
222
226
  response = agent.step(msg)
223
227
  tasks = task_parser(response.msg.content, self.id)
228
+ for task in tasks:
229
+ task.additional_info = self.additional_info
224
230
  return tasks
225
231
 
226
232
  def compose(
@@ -248,6 +254,7 @@ class Task(BaseModel):
248
254
  content = template.format(
249
255
  role_name=role_name,
250
256
  content=self.content,
257
+ additional_info=self.additional_info,
251
258
  other_results=sub_tasks_result,
252
259
  )
253
260
  msg = BaseMessage.make_user_message(
@@ -39,6 +39,10 @@ The root task is:
39
39
 
40
40
  {content}
41
41
 
42
+ The additional information of the task is:
43
+
44
+ {additional_info}
45
+
42
46
  The related tasks result and status:
43
47
 
44
48
  {other_results}
camel/types/enums.py CHANGED
@@ -82,6 +82,7 @@ class ModelType(Enum):
82
82
  MISTRAL_MIXTRAL_8x7B = "open-mixtral-8x7b"
83
83
  MISTRAL_MIXTRAL_8x22B = "open-mixtral-8x22b"
84
84
  MISTRAL_CODESTRAL_MAMBA = "open-codestral-mamba"
85
+ MISTRAL_PIXTRAL_12B = "pixtral-12b-2409"
85
86
 
86
87
  # Reka models
87
88
  REKA_CORE = "reka-core"
@@ -186,6 +187,7 @@ class ModelType(Enum):
186
187
  ModelType.MISTRAL_MIXTRAL_8x7B,
187
188
  ModelType.MISTRAL_MIXTRAL_8x22B,
188
189
  ModelType.MISTRAL_CODESTRAL_MAMBA,
190
+ ModelType.MISTRAL_PIXTRAL_12B,
189
191
  }
190
192
 
191
193
  @property
@@ -278,6 +280,7 @@ class ModelType(Enum):
278
280
  ModelType.O1_MINI,
279
281
  ModelType.MISTRAL_LARGE,
280
282
  ModelType.MISTRAL_NEMO,
283
+ ModelType.MISTRAL_PIXTRAL_12B,
281
284
  ModelType.QWEN_2,
282
285
  }:
283
286
  return 128_000
camel/utils/commons.py CHANGED
@@ -381,10 +381,17 @@ def json_to_function_code(json_obj: Dict) -> str:
381
381
  docstring_args = []
382
382
  return_keys = []
383
383
 
384
+ prop_to_python = {
385
+ 'string': 'str',
386
+ 'number': 'float',
387
+ 'integer': 'int',
388
+ 'boolean': 'bool',
389
+ }
390
+
384
391
  for prop in required:
385
392
  description = properties[prop]['description']
386
393
  prop_type = properties[prop]['type']
387
- python_type = 'str' if prop_type == 'string' else prop_type
394
+ python_type = prop_to_python.get(prop_type, prop_type)
388
395
  args.append(f"{prop}: {python_type}")
389
396
  docstring_args.append(
390
397
  f" {prop} ({python_type}): {description}."
@@ -12,12 +12,12 @@
12
12
  # limitations under the License.
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
14
 
15
- from .base import BaseNode
16
- from .manager_node import ManagerNode
17
- from .worker_node import WorkerNode
15
+ from .role_playing_worker import RolePlayingWorker
16
+ from .single_agent_worker import SingleAgentWorker
17
+ from .workforce import Workforce
18
18
 
19
19
  __all__ = [
20
- "BaseNode",
21
- "WorkerNode",
22
- "ManagerNode",
20
+ "Workforce",
21
+ "SingleAgentWorker",
22
+ "RolePlayingWorker",
23
23
  ]