camel-ai 0.2.69a4__py3-none-any.whl → 0.2.69a7__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.

@@ -19,7 +19,7 @@ import time
19
19
  import uuid
20
20
  from collections import deque
21
21
  from enum import Enum
22
- from typing import Any, Coroutine, Deque, Dict, List, Optional
22
+ from typing import Any, Coroutine, Deque, Dict, List, Optional, Set, Tuple
23
23
 
24
24
  from colorama import Fore
25
25
 
@@ -37,6 +37,7 @@ from camel.societies.workforce.role_playing_worker import RolePlayingWorker
37
37
  from camel.societies.workforce.single_agent_worker import SingleAgentWorker
38
38
  from camel.societies.workforce.task_channel import TaskChannel
39
39
  from camel.societies.workforce.utils import (
40
+ TaskAssignment,
40
41
  TaskAssignResult,
41
42
  WorkerConf,
42
43
  check_if_running,
@@ -1164,22 +1165,30 @@ class Workforce(BaseNode):
1164
1165
  )
1165
1166
  return info
1166
1167
 
1167
- def _find_assignee(
1168
- self,
1169
- tasks: List[Task],
1168
+ def _get_valid_worker_ids(self) -> set:
1169
+ r"""Get all valid worker IDs from child nodes.
1170
+
1171
+ Returns:
1172
+ set: Set of valid worker IDs that can be assigned tasks.
1173
+ """
1174
+ valid_worker_ids = {child.node_id for child in self._children}
1175
+ return valid_worker_ids
1176
+
1177
+ def _call_coordinator_for_assignment(
1178
+ self, tasks: List[Task], invalid_ids: Optional[List[str]] = None
1170
1179
  ) -> TaskAssignResult:
1171
- r"""Assigns multiple tasks to worker nodes with the best capabilities.
1180
+ r"""Call coordinator agent to assign tasks with optional validation
1181
+ feedback in the case of invalid worker IDs.
1172
1182
 
1173
- Parameters:
1174
- tasks (List[Task]): The tasks to be assigned.
1183
+ Args:
1184
+ tasks (List[Task]): Tasks to assign.
1185
+ invalid_ids (List[str], optional): Invalid worker IDs from previous
1186
+ attempt (if any).
1175
1187
 
1176
1188
  Returns:
1177
- TaskAssignResult: Assignment result containing task assignments
1178
- with their dependencies.
1189
+ TaskAssignResult: Assignment result from coordinator.
1179
1190
  """
1180
- self.coordinator_agent.reset()
1181
-
1182
- # Format tasks information for the prompt
1191
+ # format tasks information for the prompt
1183
1192
  tasks_info = ""
1184
1193
  for task in tasks:
1185
1194
  tasks_info += f"Task ID: {task.id}\n"
@@ -1188,29 +1197,212 @@ class Workforce(BaseNode):
1188
1197
  tasks_info += f"Additional Info: {task.additional_info}\n"
1189
1198
  tasks_info += "---\n"
1190
1199
 
1191
- prompt = ASSIGN_TASK_PROMPT.format(
1192
- tasks_info=tasks_info,
1193
- child_nodes_info=self._get_child_nodes_info(),
1200
+ prompt = str(
1201
+ ASSIGN_TASK_PROMPT.format(
1202
+ tasks_info=tasks_info,
1203
+ child_nodes_info=self._get_child_nodes_info(),
1204
+ )
1194
1205
  )
1195
1206
 
1196
- logger.debug(
1197
- f"Sending batch assignment request to coordinator "
1198
- f"for {len(tasks)} tasks."
1199
- )
1207
+ # add feedback if this is a retry
1208
+ if invalid_ids:
1209
+ valid_worker_ids = list(self._get_valid_worker_ids())
1210
+ feedback = (
1211
+ f"VALIDATION ERROR: The following worker IDs are invalid: "
1212
+ f"{invalid_ids}. "
1213
+ f"VALID WORKER IDS: {valid_worker_ids}. "
1214
+ f"Please reassign ONLY the above tasks using these valid IDs."
1215
+ )
1216
+ prompt = prompt + f"\n\n{feedback}"
1200
1217
 
1201
1218
  response = self.coordinator_agent.step(
1202
1219
  prompt, response_format=TaskAssignResult
1203
1220
  )
1221
+
1204
1222
  if response.msg is None or response.msg.content is None:
1205
1223
  logger.error(
1206
1224
  "Coordinator agent returned empty response for task assignment"
1207
1225
  )
1208
- # Return empty result as fallback
1209
1226
  return TaskAssignResult(assignments=[])
1210
1227
 
1211
1228
  result_dict = json.loads(response.msg.content, parse_int=str)
1212
- task_assign_result = TaskAssignResult(**result_dict)
1213
- return task_assign_result
1229
+ return TaskAssignResult(**result_dict)
1230
+
1231
+ def _validate_assignments(
1232
+ self, assignments: List[TaskAssignment], valid_ids: Set[str]
1233
+ ) -> Tuple[List[TaskAssignment], List[TaskAssignment]]:
1234
+ r"""Validate task assignments against valid worker IDs.
1235
+
1236
+ Args:
1237
+ assignments (List[TaskAssignment]): Assignments to validate.
1238
+ valid_ids (Set[str]): Set of valid worker IDs.
1239
+
1240
+ Returns:
1241
+ Tuple[List[TaskAssignment], List[TaskAssignment]]:
1242
+ (valid_assignments, invalid_assignments)
1243
+ """
1244
+ valid_assignments: List[TaskAssignment] = []
1245
+ invalid_assignments: List[TaskAssignment] = []
1246
+
1247
+ for assignment in assignments:
1248
+ if assignment.assignee_id in valid_ids:
1249
+ valid_assignments.append(assignment)
1250
+ else:
1251
+ invalid_assignments.append(assignment)
1252
+
1253
+ return valid_assignments, invalid_assignments
1254
+
1255
+ def _handle_task_assignment_fallbacks(self, tasks: List[Task]) -> List:
1256
+ r"""Create new workers for unassigned tasks as fallback.
1257
+
1258
+ Args:
1259
+ tasks (List[Task]): Tasks that need new workers.
1260
+
1261
+ Returns:
1262
+ List[TaskAssignment]: Assignments for newly created workers.
1263
+ """
1264
+ fallback_assignments = []
1265
+
1266
+ for task in tasks:
1267
+ logger.info(f"Creating new worker for unassigned task {task.id}")
1268
+ new_worker = self._create_worker_node_for_task(task)
1269
+
1270
+ assignment = TaskAssignment(
1271
+ task_id=task.id,
1272
+ assignee_id=new_worker.node_id,
1273
+ dependencies=[],
1274
+ )
1275
+ fallback_assignments.append(assignment)
1276
+
1277
+ return fallback_assignments
1278
+
1279
+ def _handle_assignment_retry_and_fallback(
1280
+ self,
1281
+ invalid_assignments: List[TaskAssignment],
1282
+ tasks: List[Task],
1283
+ valid_worker_ids: Set[str],
1284
+ ) -> List[TaskAssignment]:
1285
+ r"""Called if Coordinator agent fails to assign tasks to valid worker
1286
+ IDs. Handles retry assignment and fallback worker creation for invalid
1287
+ assignments.
1288
+
1289
+ Args:
1290
+ invalid_assignments (List[TaskAssignment]): Invalid assignments to
1291
+ retry.
1292
+ tasks (List[Task]): Original tasks list for task lookup.
1293
+ valid_worker_ids (set): Set of valid worker IDs.
1294
+
1295
+ Returns:
1296
+ List[TaskAssignment]: Final assignments for the invalid tasks.
1297
+ """
1298
+ invalid_ids = [a.assignee_id for a in invalid_assignments]
1299
+ invalid_tasks = [
1300
+ task
1301
+ for task in tasks
1302
+ if any(a.task_id == task.id for a in invalid_assignments)
1303
+ ]
1304
+
1305
+ # handle cases where coordinator returned no assignments at all
1306
+ if not invalid_assignments:
1307
+ invalid_tasks = tasks # all tasks need assignment
1308
+ logger.warning(
1309
+ f"Coordinator returned no assignments. "
1310
+ f"Retrying assignment for all {len(invalid_tasks)} tasks."
1311
+ )
1312
+ else:
1313
+ logger.warning(
1314
+ f"Invalid worker IDs detected: {invalid_ids}. "
1315
+ f"Retrying assignment for {len(invalid_tasks)} tasks."
1316
+ )
1317
+
1318
+ # retry assignment with feedback
1319
+ retry_result = self._call_coordinator_for_assignment(
1320
+ invalid_tasks, invalid_ids
1321
+ )
1322
+ final_assignments = []
1323
+
1324
+ if retry_result.assignments:
1325
+ retry_valid, retry_invalid = self._validate_assignments(
1326
+ retry_result.assignments, valid_worker_ids
1327
+ )
1328
+ final_assignments.extend(retry_valid)
1329
+
1330
+ # collect tasks that are still unassigned for fallback
1331
+ if retry_invalid:
1332
+ unassigned_tasks = [
1333
+ task
1334
+ for task in invalid_tasks
1335
+ if any(a.task_id == task.id for a in retry_invalid)
1336
+ ]
1337
+ else:
1338
+ unassigned_tasks = []
1339
+ else:
1340
+ # retry failed completely, all invalid tasks need fallback
1341
+ logger.warning("Retry assignment failed")
1342
+ unassigned_tasks = invalid_tasks
1343
+
1344
+ # handle fallback for any remaining unassigned tasks
1345
+ if unassigned_tasks:
1346
+ logger.warning(
1347
+ f"Creating fallback workers for {len(unassigned_tasks)} "
1348
+ f"unassigned tasks"
1349
+ )
1350
+ fallback_assignments = self._handle_task_assignment_fallbacks(
1351
+ unassigned_tasks
1352
+ )
1353
+ final_assignments.extend(fallback_assignments)
1354
+
1355
+ return final_assignments
1356
+
1357
+ def _find_assignee(
1358
+ self,
1359
+ tasks: List[Task],
1360
+ ) -> TaskAssignResult:
1361
+ r"""Assigns multiple tasks to worker nodes with the best capabilities.
1362
+
1363
+ Parameters:
1364
+ tasks (List[Task]): The tasks to be assigned.
1365
+
1366
+ Returns:
1367
+ TaskAssignResult: Assignment result containing task assignments
1368
+ with their dependencies.
1369
+ """
1370
+ self.coordinator_agent.reset()
1371
+ valid_worker_ids = self._get_valid_worker_ids()
1372
+
1373
+ logger.debug(
1374
+ f"Sending batch assignment request to coordinator "
1375
+ f"for {len(tasks)} tasks."
1376
+ )
1377
+
1378
+ assignment_result = self._call_coordinator_for_assignment(tasks)
1379
+
1380
+ # validate assignments
1381
+ valid_assignments, invalid_assignments = self._validate_assignments(
1382
+ assignment_result.assignments, valid_worker_ids
1383
+ )
1384
+
1385
+ # check if we have assignments for all tasks
1386
+ assigned_task_ids = {
1387
+ a.task_id for a in valid_assignments + invalid_assignments
1388
+ }
1389
+ unassigned_tasks = [t for t in tasks if t.id not in assigned_task_ids]
1390
+
1391
+ # if all assignments are valid and all tasks are assigned, return early
1392
+ if not invalid_assignments and not unassigned_tasks:
1393
+ return TaskAssignResult(assignments=valid_assignments)
1394
+
1395
+ # handle retry and fallback for
1396
+ # invalid assignments and unassigned tasks
1397
+ all_problem_assignments = invalid_assignments
1398
+ retry_and_fallback_assignments = (
1399
+ self._handle_assignment_retry_and_fallback(
1400
+ all_problem_assignments, tasks, valid_worker_ids
1401
+ )
1402
+ )
1403
+ valid_assignments.extend(retry_and_fallback_assignments)
1404
+
1405
+ return TaskAssignResult(assignments=valid_assignments)
1214
1406
 
1215
1407
  async def _post_task(self, task: Task, assignee_id: str) -> None:
1216
1408
  # Record the start time when a task is posted
@@ -26,6 +26,7 @@ from .vectordb_storages.base import (
26
26
  VectorDBQueryResult,
27
27
  VectorRecord,
28
28
  )
29
+ from .vectordb_storages.chroma import ChromaStorage
29
30
  from .vectordb_storages.faiss import FaissStorage
30
31
  from .vectordb_storages.milvus import MilvusStorage
31
32
  from .vectordb_storages.oceanbase import OceanBaseStorage
@@ -52,4 +53,5 @@ __all__ = [
52
53
  'Mem0Storage',
53
54
  'OceanBaseStorage',
54
55
  'WeaviateStorage',
56
+ 'ChromaStorage',
55
57
  ]
@@ -19,6 +19,7 @@ from .base import (
19
19
  VectorDBStatus,
20
20
  VectorRecord,
21
21
  )
22
+ from .chroma import ChromaStorage
22
23
  from .faiss import FaissStorage
23
24
  from .milvus import MilvusStorage
24
25
  from .oceanbase import OceanBaseStorage
@@ -30,6 +31,7 @@ __all__ = [
30
31
  'BaseVectorStorage',
31
32
  'VectorDBQuery',
32
33
  'VectorDBQueryResult',
34
+ 'ChromaStorage',
33
35
  'QdrantStorage',
34
36
  'MilvusStorage',
35
37
  "TiDBStorage",