camel-ai 0.2.61__py3-none-any.whl → 0.2.64__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.
- camel/__init__.py +1 -1
- camel/agents/chat_agent.py +75 -16
- camel/agents/mcp_agent.py +10 -6
- camel/configs/__init__.py +3 -0
- camel/configs/crynux_config.py +94 -0
- camel/{data_collector → data_collectors}/alpaca_collector.py +1 -1
- camel/{data_collector → data_collectors}/sharegpt_collector.py +1 -1
- camel/interpreters/base.py +14 -1
- camel/interpreters/docker/Dockerfile +63 -7
- camel/interpreters/docker_interpreter.py +65 -7
- camel/interpreters/e2b_interpreter.py +23 -8
- camel/interpreters/internal_python_interpreter.py +30 -2
- camel/interpreters/ipython_interpreter.py +21 -3
- camel/interpreters/subprocess_interpreter.py +34 -2
- camel/memories/records.py +5 -3
- camel/models/__init__.py +2 -0
- camel/models/azure_openai_model.py +101 -25
- camel/models/cohere_model.py +65 -0
- camel/models/crynux_model.py +94 -0
- camel/models/deepseek_model.py +43 -1
- camel/models/gemini_model.py +50 -4
- camel/models/litellm_model.py +38 -0
- camel/models/mistral_model.py +66 -0
- camel/models/model_factory.py +10 -1
- camel/models/openai_compatible_model.py +81 -17
- camel/models/openai_model.py +86 -16
- camel/models/reka_model.py +69 -0
- camel/models/samba_model.py +69 -2
- camel/models/sglang_model.py +74 -2
- camel/models/watsonx_model.py +62 -0
- camel/retrievers/auto_retriever.py +20 -1
- camel/{runtime → runtimes}/daytona_runtime.py +1 -1
- camel/{runtime → runtimes}/docker_runtime.py +1 -1
- camel/{runtime → runtimes}/llm_guard_runtime.py +2 -2
- camel/{runtime → runtimes}/remote_http_runtime.py +1 -1
- camel/{runtime → runtimes}/ubuntu_docker_runtime.py +1 -1
- camel/societies/workforce/base.py +7 -3
- camel/societies/workforce/role_playing_worker.py +2 -2
- camel/societies/workforce/single_agent_worker.py +25 -1
- camel/societies/workforce/worker.py +5 -3
- camel/societies/workforce/workforce.py +409 -7
- camel/storages/__init__.py +2 -0
- camel/storages/vectordb_storages/__init__.py +2 -0
- camel/storages/vectordb_storages/weaviate.py +714 -0
- camel/tasks/task.py +19 -10
- camel/toolkits/__init__.py +2 -0
- camel/toolkits/code_execution.py +37 -8
- camel/toolkits/file_write_toolkit.py +4 -2
- camel/toolkits/mcp_toolkit.py +480 -733
- camel/toolkits/pptx_toolkit.py +777 -0
- camel/types/enums.py +56 -1
- camel/types/unified_model_type.py +5 -0
- camel/utils/__init__.py +16 -0
- camel/utils/langfuse.py +258 -0
- camel/utils/mcp_client.py +1046 -0
- {camel_ai-0.2.61.dist-info → camel_ai-0.2.64.dist-info}/METADATA +9 -1
- {camel_ai-0.2.61.dist-info → camel_ai-0.2.64.dist-info}/RECORD +68 -62
- /camel/{data_collector → data_collectors}/__init__.py +0 -0
- /camel/{data_collector → data_collectors}/base.py +0 -0
- /camel/{runtime → runtimes}/__init__.py +0 -0
- /camel/{runtime → runtimes}/api.py +0 -0
- /camel/{runtime → runtimes}/base.py +0 -0
- /camel/{runtime → runtimes}/configs.py +0 -0
- /camel/{runtime → runtimes}/utils/__init__.py +0 -0
- /camel/{runtime → runtimes}/utils/function_risk_toolkit.py +0 -0
- /camel/{runtime → runtimes}/utils/ignore_risk_toolkit.py +0 -0
- {camel_ai-0.2.61.dist-info → camel_ai-0.2.64.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.61.dist-info → camel_ai-0.2.64.dist-info}/licenses/LICENSE +0 -0
|
@@ -18,7 +18,7 @@ import time
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import Callable, List, Optional, Union
|
|
20
20
|
|
|
21
|
-
from camel.
|
|
21
|
+
from camel.runtimes.docker_runtime import DockerRuntime
|
|
22
22
|
from camel.toolkits import FunctionTool
|
|
23
23
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
from abc import ABC, abstractmethod
|
|
15
|
-
from typing import Any
|
|
15
|
+
from typing import Any, Optional
|
|
16
16
|
|
|
17
17
|
from camel.societies.workforce.task_channel import TaskChannel
|
|
18
18
|
from camel.societies.workforce.utils import check_if_running
|
|
@@ -23,10 +23,14 @@ class BaseNode(ABC):
|
|
|
23
23
|
|
|
24
24
|
Args:
|
|
25
25
|
description (str): Description of the node.
|
|
26
|
+
node_id (Optional[str]): ID of the node. If not provided, it will
|
|
27
|
+
be generated automatically. (default: :obj:`None`)
|
|
26
28
|
"""
|
|
27
29
|
|
|
28
|
-
def __init__(
|
|
29
|
-
self
|
|
30
|
+
def __init__(
|
|
31
|
+
self, description: str, node_id: Optional[str] = None
|
|
32
|
+
) -> None:
|
|
33
|
+
self.node_id = node_id if node_id is not None else str(id(self))
|
|
30
34
|
self.description = description
|
|
31
35
|
self._channel: TaskChannel = TaskChannel()
|
|
32
36
|
self._running = False
|
|
@@ -48,7 +48,7 @@ class RolePlayingWorker(Worker):
|
|
|
48
48
|
initialize the summarize agent, like the model name, etc.
|
|
49
49
|
(default: :obj:`None`)
|
|
50
50
|
chat_turn_limit (int): The maximum number of chat turns in the role
|
|
51
|
-
playing. (default: :obj:`
|
|
51
|
+
playing. (default: :obj:`20`)
|
|
52
52
|
"""
|
|
53
53
|
|
|
54
54
|
def __init__(
|
|
@@ -59,7 +59,7 @@ class RolePlayingWorker(Worker):
|
|
|
59
59
|
assistant_agent_kwargs: Optional[Dict] = None,
|
|
60
60
|
user_agent_kwargs: Optional[Dict] = None,
|
|
61
61
|
summarize_agent_kwargs: Optional[Dict] = None,
|
|
62
|
-
chat_turn_limit: int =
|
|
62
|
+
chat_turn_limit: int = 20,
|
|
63
63
|
) -> None:
|
|
64
64
|
super().__init__(description)
|
|
65
65
|
self.summarize_agent_kwargs = summarize_agent_kwargs
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import datetime
|
|
16
17
|
import json
|
|
17
18
|
from typing import Any, List
|
|
18
19
|
|
|
@@ -39,7 +40,8 @@ class SingleAgentWorker(Worker):
|
|
|
39
40
|
description: str,
|
|
40
41
|
worker: ChatAgent,
|
|
41
42
|
) -> None:
|
|
42
|
-
|
|
43
|
+
node_id = worker.agent_id
|
|
44
|
+
super().__init__(description, node_id=node_id)
|
|
43
45
|
self.worker = worker
|
|
44
46
|
|
|
45
47
|
def reset(self) -> Any:
|
|
@@ -82,6 +84,28 @@ class SingleAgentWorker(Worker):
|
|
|
82
84
|
)
|
|
83
85
|
return TaskState.FAILED
|
|
84
86
|
|
|
87
|
+
# Populate additional_info with worker attempt details
|
|
88
|
+
if task.additional_info is None:
|
|
89
|
+
task.additional_info = {}
|
|
90
|
+
|
|
91
|
+
# Create worker attempt details with descriptive keys
|
|
92
|
+
worker_attempt_details = {
|
|
93
|
+
"agent_id": getattr(
|
|
94
|
+
self.worker, "agent_id", self.worker.role_name
|
|
95
|
+
),
|
|
96
|
+
"timestamp": str(datetime.datetime.now()),
|
|
97
|
+
"description": f"Attempt by "
|
|
98
|
+
f"{getattr(self.worker, 'agent_id', self.worker.role_name)} "
|
|
99
|
+
f"to process task {task.content}",
|
|
100
|
+
"response_content": response.msg.content,
|
|
101
|
+
"tool_calls": response.info["tool_calls"],
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Store the worker attempt in additional_info
|
|
105
|
+
if "worker_attempts" not in task.additional_info:
|
|
106
|
+
task.additional_info["worker_attempts"] = []
|
|
107
|
+
task.additional_info["worker_attempts"].append(worker_attempt_details)
|
|
108
|
+
|
|
85
109
|
print(f"======\n{Fore.GREEN}Reply from {self}:{Fore.RESET}")
|
|
86
110
|
|
|
87
111
|
result_dict = json.loads(response.msg.content)
|
|
@@ -15,7 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
17
|
from abc import ABC, abstractmethod
|
|
18
|
-
from typing import List
|
|
18
|
+
from typing import List, Optional
|
|
19
19
|
|
|
20
20
|
from colorama import Fore
|
|
21
21
|
|
|
@@ -33,14 +33,16 @@ class Worker(BaseNode, ABC):
|
|
|
33
33
|
|
|
34
34
|
Args:
|
|
35
35
|
description (str): Description of the node.
|
|
36
|
-
|
|
36
|
+
node_id (Optional[str]): ID of the node. If not provided, it will
|
|
37
|
+
be generated automatically. (default: :obj:`None`)
|
|
37
38
|
"""
|
|
38
39
|
|
|
39
40
|
def __init__(
|
|
40
41
|
self,
|
|
41
42
|
description: str,
|
|
43
|
+
node_id: Optional[str] = None,
|
|
42
44
|
) -> None:
|
|
43
|
-
super().__init__(description)
|
|
45
|
+
super().__init__(description, node_id=node_id)
|
|
44
46
|
|
|
45
47
|
def __repr__(self):
|
|
46
48
|
return f"Worker node {self.node_id} ({self.description})"
|
|
@@ -15,13 +15,13 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
import asyncio
|
|
17
17
|
import json
|
|
18
|
+
import uuid
|
|
18
19
|
from collections import deque
|
|
19
20
|
from typing import Deque, Dict, List, Optional
|
|
20
21
|
|
|
21
22
|
from colorama import Fore
|
|
22
23
|
|
|
23
24
|
from camel.agents import ChatAgent
|
|
24
|
-
from camel.configs import ChatGPTConfig
|
|
25
25
|
from camel.logger import get_logger
|
|
26
26
|
from camel.messages.base import BaseMessage
|
|
27
27
|
from camel.models import ModelFactory
|
|
@@ -43,6 +43,7 @@ from camel.societies.workforce.worker import Worker
|
|
|
43
43
|
from camel.tasks.task import Task, TaskState
|
|
44
44
|
from camel.toolkits import CodeExecutionToolkit, SearchToolkit, ThinkingToolkit
|
|
45
45
|
from camel.types import ModelPlatformType, ModelType
|
|
46
|
+
from camel.utils import dependencies_required
|
|
46
47
|
|
|
47
48
|
logger = get_logger(__name__)
|
|
48
49
|
|
|
@@ -85,6 +86,10 @@ class Workforce(BaseNode):
|
|
|
85
86
|
available parameters.
|
|
86
87
|
(default: :obj:`None` - creates workers with SearchToolkit,
|
|
87
88
|
CodeExecutionToolkit, and ThinkingToolkit)
|
|
89
|
+
graceful_shutdown_timeout (float, optional): The timeout in seconds
|
|
90
|
+
for graceful shutdown when a task fails 3 times. During this
|
|
91
|
+
period, the workforce remains active for debugging.
|
|
92
|
+
Set to 0 for immediate shutdown. (default: :obj:`15.0`)
|
|
88
93
|
|
|
89
94
|
Example:
|
|
90
95
|
>>> # Configure with custom model
|
|
@@ -109,11 +114,13 @@ class Workforce(BaseNode):
|
|
|
109
114
|
coordinator_agent_kwargs: Optional[Dict] = None,
|
|
110
115
|
task_agent_kwargs: Optional[Dict] = None,
|
|
111
116
|
new_worker_agent_kwargs: Optional[Dict] = None,
|
|
117
|
+
graceful_shutdown_timeout: float = 15.0,
|
|
112
118
|
) -> None:
|
|
113
119
|
super().__init__(description)
|
|
114
120
|
self._child_listening_tasks: Deque[asyncio.Task] = deque()
|
|
115
121
|
self._children = children or []
|
|
116
122
|
self.new_worker_agent_kwargs = new_worker_agent_kwargs
|
|
123
|
+
self.graceful_shutdown_timeout = graceful_shutdown_timeout
|
|
117
124
|
|
|
118
125
|
# Warning messages for default model usage
|
|
119
126
|
if coordinator_agent_kwargs is None:
|
|
@@ -421,15 +428,10 @@ class Workforce(BaseNode):
|
|
|
421
428
|
*ThinkingToolkit().get_tools(),
|
|
422
429
|
]
|
|
423
430
|
|
|
424
|
-
model_config_dict = ChatGPTConfig(
|
|
425
|
-
tools=function_list,
|
|
426
|
-
temperature=0.0,
|
|
427
|
-
).as_dict()
|
|
428
|
-
|
|
429
431
|
model = ModelFactory.create(
|
|
430
432
|
model_platform=ModelPlatformType.DEFAULT,
|
|
431
433
|
model_type=ModelType.DEFAULT,
|
|
432
|
-
model_config_dict=
|
|
434
|
+
model_config_dict={"temperature": 0},
|
|
433
435
|
)
|
|
434
436
|
|
|
435
437
|
return ChatAgent(worker_sys_msg, model=model, tools=function_list) # type: ignore[arg-type]
|
|
@@ -494,6 +496,26 @@ class Workforce(BaseNode):
|
|
|
494
496
|
await self._channel.archive_task(task.id)
|
|
495
497
|
await self._post_ready_tasks()
|
|
496
498
|
|
|
499
|
+
async def _graceful_shutdown(self, failed_task: Task) -> None:
|
|
500
|
+
r"""Handle graceful shutdown with configurable timeout. This is used to
|
|
501
|
+
keep the workforce running for a while to debug the failed task.
|
|
502
|
+
|
|
503
|
+
Args:
|
|
504
|
+
failed_task (Task): The task that failed and triggered shutdown.
|
|
505
|
+
"""
|
|
506
|
+
if self.graceful_shutdown_timeout <= 0:
|
|
507
|
+
# Immediate shutdown if timeout is 0 or negative
|
|
508
|
+
return
|
|
509
|
+
|
|
510
|
+
logger.warning(
|
|
511
|
+
f"Workforce will shutdown in {self.graceful_shutdown_timeout} "
|
|
512
|
+
f"seconds due to failure. You can use this time to inspect the "
|
|
513
|
+
f"current state of the workforce."
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
# Wait for the full timeout period
|
|
517
|
+
await asyncio.sleep(self.graceful_shutdown_timeout)
|
|
518
|
+
|
|
497
519
|
@check_if_running(False)
|
|
498
520
|
async def _listen_to_channel(self) -> None:
|
|
499
521
|
r"""Continuously listen to the channel, post task to the channel and
|
|
@@ -517,6 +539,8 @@ class Workforce(BaseNode):
|
|
|
517
539
|
f"{Fore.RED}Task {returned_task.id} has failed "
|
|
518
540
|
f"for 3 times, halting the workforce.{Fore.RESET}"
|
|
519
541
|
)
|
|
542
|
+
# Graceful shutdown instead of immediate break
|
|
543
|
+
await self._graceful_shutdown(returned_task)
|
|
520
544
|
break
|
|
521
545
|
elif returned_task.state == TaskState.OPEN:
|
|
522
546
|
# TODO: multi-layer workforce
|
|
@@ -568,6 +592,7 @@ class Workforce(BaseNode):
|
|
|
568
592
|
coordinator_agent_kwargs={},
|
|
569
593
|
task_agent_kwargs={},
|
|
570
594
|
new_worker_agent_kwargs=self.new_worker_agent_kwargs,
|
|
595
|
+
graceful_shutdown_timeout=self.graceful_shutdown_timeout,
|
|
571
596
|
)
|
|
572
597
|
|
|
573
598
|
new_instance.task_agent = self.task_agent.clone(with_memory)
|
|
@@ -598,3 +623,380 @@ class Workforce(BaseNode):
|
|
|
598
623
|
continue
|
|
599
624
|
|
|
600
625
|
return new_instance
|
|
626
|
+
|
|
627
|
+
@dependencies_required("mcp")
|
|
628
|
+
def to_mcp(
|
|
629
|
+
self,
|
|
630
|
+
name: str = "CAMEL-Workforce",
|
|
631
|
+
description: str = (
|
|
632
|
+
"A workforce system using the CAMEL AI framework for "
|
|
633
|
+
"multi-agent collaboration."
|
|
634
|
+
),
|
|
635
|
+
dependencies: Optional[List[str]] = None,
|
|
636
|
+
host: str = "localhost",
|
|
637
|
+
port: int = 8001,
|
|
638
|
+
):
|
|
639
|
+
r"""Expose this Workforce as an MCP server.
|
|
640
|
+
|
|
641
|
+
Args:
|
|
642
|
+
name (str): Name of the MCP server.
|
|
643
|
+
(default: :obj:`CAMEL-Workforce`)
|
|
644
|
+
description (str): Description of the workforce. If
|
|
645
|
+
None, a generic description is used. (default: :obj:`A
|
|
646
|
+
workforce system using the CAMEL AI framework for
|
|
647
|
+
multi-agent collaboration.`)
|
|
648
|
+
dependencies (Optional[List[str]]): Additional
|
|
649
|
+
dependencies for the MCP server. (default: :obj:`None`)
|
|
650
|
+
host (str): Host to bind to for HTTP transport.
|
|
651
|
+
(default: :obj:`localhost`)
|
|
652
|
+
port (int): Port to bind to for HTTP transport.
|
|
653
|
+
(default: :obj:`8001`)
|
|
654
|
+
|
|
655
|
+
Returns:
|
|
656
|
+
FastMCP: An MCP server instance that can be run.
|
|
657
|
+
"""
|
|
658
|
+
from mcp.server.fastmcp import FastMCP
|
|
659
|
+
|
|
660
|
+
# Combine dependencies
|
|
661
|
+
all_dependencies = ["camel-ai[all]"]
|
|
662
|
+
if dependencies:
|
|
663
|
+
all_dependencies.extend(dependencies)
|
|
664
|
+
|
|
665
|
+
mcp_server = FastMCP(
|
|
666
|
+
name,
|
|
667
|
+
dependencies=all_dependencies,
|
|
668
|
+
host=host,
|
|
669
|
+
port=port,
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
# Store workforce reference
|
|
673
|
+
workforce_instance = self
|
|
674
|
+
|
|
675
|
+
# Define functions first
|
|
676
|
+
def process_task(task_content, task_id=None, additional_info=None):
|
|
677
|
+
r"""Process a task using the workforce.
|
|
678
|
+
|
|
679
|
+
Args:
|
|
680
|
+
task_content (str): The content of the task to be processed.
|
|
681
|
+
task_id (str, optional): Unique identifier for the task. If
|
|
682
|
+
None, a UUID will be automatically generated.
|
|
683
|
+
(default: :obj:`None`)
|
|
684
|
+
additional_info (str, optional): Additional information or
|
|
685
|
+
context for the task. (default: :obj:`None`)
|
|
686
|
+
|
|
687
|
+
Returns:
|
|
688
|
+
Dict[str, Any]: A dictionary containing the processing result
|
|
689
|
+
with the following keys:
|
|
690
|
+
- status (str): "success" or "error"
|
|
691
|
+
- task_id (str): The ID of the processed task
|
|
692
|
+
- state (str): Final state of the task
|
|
693
|
+
- result (str): Task result content
|
|
694
|
+
- subtasks (List[Dict]): List of subtask information
|
|
695
|
+
- message (str): Error message if status is "error"
|
|
696
|
+
|
|
697
|
+
Example:
|
|
698
|
+
>>> result = process_task("Analyze market trends", "task_001")
|
|
699
|
+
>>> print(result["status"]) # "success" or "error"
|
|
700
|
+
"""
|
|
701
|
+
task = Task(
|
|
702
|
+
content=task_content,
|
|
703
|
+
id=task_id or str(uuid.uuid4()),
|
|
704
|
+
additional_info=additional_info or "",
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
try:
|
|
708
|
+
result_task = workforce_instance.process_task(task)
|
|
709
|
+
return {
|
|
710
|
+
"status": "success",
|
|
711
|
+
"task_id": result_task.id,
|
|
712
|
+
"state": str(result_task.state),
|
|
713
|
+
"result": result_task.result or "",
|
|
714
|
+
"subtasks": [
|
|
715
|
+
{
|
|
716
|
+
"id": subtask.id,
|
|
717
|
+
"content": subtask.content,
|
|
718
|
+
"state": str(subtask.state),
|
|
719
|
+
"result": subtask.result or "",
|
|
720
|
+
}
|
|
721
|
+
for subtask in (result_task.subtasks or [])
|
|
722
|
+
],
|
|
723
|
+
}
|
|
724
|
+
except Exception as e:
|
|
725
|
+
return {
|
|
726
|
+
"status": "error",
|
|
727
|
+
"message": str(e),
|
|
728
|
+
"task_id": task.id,
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
# Reset tool
|
|
732
|
+
def reset():
|
|
733
|
+
r"""Reset the workforce to its initial state.
|
|
734
|
+
|
|
735
|
+
Clears all pending tasks, resets all child nodes, and returns
|
|
736
|
+
the workforce to a clean state ready for new task processing.
|
|
737
|
+
|
|
738
|
+
Returns:
|
|
739
|
+
Dict[str, str]: A dictionary containing the reset result with:
|
|
740
|
+
- status (str): "success" or "error"
|
|
741
|
+
- message (str): Descriptive message about the operation
|
|
742
|
+
|
|
743
|
+
Example:
|
|
744
|
+
>>> result = reset()
|
|
745
|
+
>>> print(result["message"]) # "Workforce reset successfully"
|
|
746
|
+
"""
|
|
747
|
+
try:
|
|
748
|
+
workforce_instance.reset()
|
|
749
|
+
return {
|
|
750
|
+
"status": "success",
|
|
751
|
+
"message": "Workforce reset successfully",
|
|
752
|
+
}
|
|
753
|
+
except Exception as e:
|
|
754
|
+
return {"status": "error", "message": str(e)}
|
|
755
|
+
|
|
756
|
+
# Workforce info resource and tool
|
|
757
|
+
def get_workforce_info():
|
|
758
|
+
r"""Get comprehensive information about the workforce.
|
|
759
|
+
|
|
760
|
+
Retrieves the current state and configuration of the workforce
|
|
761
|
+
including its ID, description, running status, and task queue
|
|
762
|
+
information.
|
|
763
|
+
|
|
764
|
+
Returns:
|
|
765
|
+
Dict[str, Any]: A dictionary containing workforce information:
|
|
766
|
+
- node_id (str): Unique identifier of the workforce
|
|
767
|
+
- description (str): Workforce description
|
|
768
|
+
- mcp_description (str): MCP server description
|
|
769
|
+
- children_count (int): Number of child workers
|
|
770
|
+
- is_running (bool): Whether the workforce is active
|
|
771
|
+
- pending_tasks_count (int): Number of queued tasks
|
|
772
|
+
- current_task_id (str or None): ID of the active task
|
|
773
|
+
|
|
774
|
+
Example:
|
|
775
|
+
>>> info = get_workforce_info()
|
|
776
|
+
>>> print(f"Running: {info['is_running']}")
|
|
777
|
+
>>> print(f"Children: {info['children_count']}")
|
|
778
|
+
"""
|
|
779
|
+
info = {
|
|
780
|
+
"node_id": workforce_instance.node_id,
|
|
781
|
+
"description": workforce_instance.description,
|
|
782
|
+
"mcp_description": description,
|
|
783
|
+
"children_count": len(workforce_instance._children),
|
|
784
|
+
"is_running": workforce_instance._running,
|
|
785
|
+
"pending_tasks_count": len(workforce_instance._pending_tasks),
|
|
786
|
+
"current_task_id": (
|
|
787
|
+
workforce_instance._task.id
|
|
788
|
+
if workforce_instance._task
|
|
789
|
+
else None
|
|
790
|
+
),
|
|
791
|
+
}
|
|
792
|
+
return info
|
|
793
|
+
|
|
794
|
+
# Children info resource and tool
|
|
795
|
+
def get_children_info():
|
|
796
|
+
r"""Get information about all child nodes in the workforce.
|
|
797
|
+
|
|
798
|
+
Retrieves comprehensive information about each child worker
|
|
799
|
+
including their type, capabilities, and configuration details.
|
|
800
|
+
|
|
801
|
+
Returns:
|
|
802
|
+
List[Dict[str, Any]]: A list of dictionaries, each containing
|
|
803
|
+
child node information with common keys:
|
|
804
|
+
- node_id (str): Unique identifier of the child
|
|
805
|
+
- description (str): Child node description
|
|
806
|
+
- type (str): Type of worker (e.g., "SingleAgentWorker")
|
|
807
|
+
|
|
808
|
+
Additional keys depend on worker type:
|
|
809
|
+
|
|
810
|
+
For SingleAgentWorker:
|
|
811
|
+
- tools (List[str]): Available tool names
|
|
812
|
+
- role_name (str): Agent's role name
|
|
813
|
+
|
|
814
|
+
For RolePlayingWorker:
|
|
815
|
+
- assistant_role (str): Assistant agent role
|
|
816
|
+
- user_role (str): User agent role
|
|
817
|
+
- chat_turn_limit (int): Maximum conversation turns
|
|
818
|
+
|
|
819
|
+
For Workforce:
|
|
820
|
+
- children_count (int): Number of nested children
|
|
821
|
+
- is_running (bool): Whether the nested workforce is active
|
|
822
|
+
|
|
823
|
+
Example:
|
|
824
|
+
>>> children = get_children_info()
|
|
825
|
+
>>> for child in children:
|
|
826
|
+
... print(f"{child['type']}: {child['description']}")
|
|
827
|
+
"""
|
|
828
|
+
children_info = []
|
|
829
|
+
for child in workforce_instance._children:
|
|
830
|
+
child_info = {
|
|
831
|
+
"node_id": child.node_id,
|
|
832
|
+
"description": child.description,
|
|
833
|
+
"type": type(child).__name__,
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if isinstance(child, SingleAgentWorker):
|
|
837
|
+
child_info["tools"] = list(child.worker.tool_dict.keys())
|
|
838
|
+
child_info["role_name"] = child.worker.role_name
|
|
839
|
+
elif isinstance(child, RolePlayingWorker):
|
|
840
|
+
child_info["assistant_role"] = child.assistant_role_name
|
|
841
|
+
child_info["user_role"] = child.user_role_name
|
|
842
|
+
child_info["chat_turn_limit"] = child.chat_turn_limit
|
|
843
|
+
elif isinstance(child, Workforce):
|
|
844
|
+
child_info["children_count"] = len(child._children)
|
|
845
|
+
child_info["is_running"] = child._running
|
|
846
|
+
|
|
847
|
+
children_info.append(child_info)
|
|
848
|
+
|
|
849
|
+
return children_info
|
|
850
|
+
|
|
851
|
+
# Add single agent worker
|
|
852
|
+
def add_single_agent_worker(
|
|
853
|
+
description,
|
|
854
|
+
system_message=None,
|
|
855
|
+
role_name="Assistant",
|
|
856
|
+
agent_kwargs=None,
|
|
857
|
+
):
|
|
858
|
+
r"""Add a single agent worker to the workforce.
|
|
859
|
+
|
|
860
|
+
Creates and adds a new SingleAgentWorker to the workforce with
|
|
861
|
+
the specified configuration. The worker cannot be added while
|
|
862
|
+
the workforce is currently running.
|
|
863
|
+
|
|
864
|
+
Args:
|
|
865
|
+
description (str): Description of the worker's role and
|
|
866
|
+
capabilities.
|
|
867
|
+
system_message (str, optional): Custom system message for the
|
|
868
|
+
agent. If None, a default message based on role_name is
|
|
869
|
+
used. (default: :obj:`None`)
|
|
870
|
+
role_name (str, optional): Name of the agent's role.
|
|
871
|
+
(default: :obj:`"Assistant"`)
|
|
872
|
+
agent_kwargs (Dict, optional): Additional keyword arguments
|
|
873
|
+
to pass to the ChatAgent constructor, such as model
|
|
874
|
+
configuration, tools, etc. (default: :obj:`None`)
|
|
875
|
+
|
|
876
|
+
Returns:
|
|
877
|
+
Dict[str, str]: A dictionary containing the operation result:
|
|
878
|
+
- status (str): "success" or "error"
|
|
879
|
+
- message (str): Descriptive message about the operation
|
|
880
|
+
- worker_id (str): ID of the created worker (on success)
|
|
881
|
+
|
|
882
|
+
Example:
|
|
883
|
+
>>> result = add_single_agent_worker(
|
|
884
|
+
... "Data Analyst",
|
|
885
|
+
... "You are a data analysis expert.",
|
|
886
|
+
... "Analyst"
|
|
887
|
+
... )
|
|
888
|
+
>>> print(result["status"]) # "success" or "error"
|
|
889
|
+
"""
|
|
890
|
+
try:
|
|
891
|
+
if workforce_instance._running:
|
|
892
|
+
return {
|
|
893
|
+
"status": "error",
|
|
894
|
+
"message": "Cannot add workers while workforce is running", # noqa: E501
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
# Create agent with provided configuration
|
|
898
|
+
sys_msg = BaseMessage.make_assistant_message(
|
|
899
|
+
role_name=role_name,
|
|
900
|
+
content=system_message or f"You are a {role_name}.",
|
|
901
|
+
)
|
|
902
|
+
|
|
903
|
+
agent = ChatAgent(sys_msg, **(agent_kwargs or {}))
|
|
904
|
+
workforce_instance.add_single_agent_worker(description, agent)
|
|
905
|
+
|
|
906
|
+
return {
|
|
907
|
+
"status": "success",
|
|
908
|
+
"message": f"Single agent worker '{description}' added",
|
|
909
|
+
"worker_id": workforce_instance._children[-1].node_id,
|
|
910
|
+
}
|
|
911
|
+
except Exception as e:
|
|
912
|
+
return {"status": "error", "message": str(e)}
|
|
913
|
+
|
|
914
|
+
# Add role playing worker
|
|
915
|
+
def add_role_playing_worker(
|
|
916
|
+
description,
|
|
917
|
+
assistant_role_name,
|
|
918
|
+
user_role_name,
|
|
919
|
+
chat_turn_limit=20,
|
|
920
|
+
assistant_agent_kwargs=None,
|
|
921
|
+
user_agent_kwargs=None,
|
|
922
|
+
summarize_agent_kwargs=None,
|
|
923
|
+
):
|
|
924
|
+
r"""Add a role playing worker to the workforce.
|
|
925
|
+
|
|
926
|
+
Creates and adds a new RolePlayingWorker to the workforce that
|
|
927
|
+
uses two agents in a conversational role-playing setup. The
|
|
928
|
+
worker cannot be added while the workforce is currently running.
|
|
929
|
+
|
|
930
|
+
Args:
|
|
931
|
+
description (str): Description of the role playing worker's
|
|
932
|
+
purpose and capabilities.
|
|
933
|
+
assistant_role_name (str): Name/role of the assistant agent
|
|
934
|
+
in the role playing scenario.
|
|
935
|
+
user_role_name (str): Name/role of the user agent in the
|
|
936
|
+
role playing scenario.
|
|
937
|
+
chat_turn_limit (int, optional): Maximum number of
|
|
938
|
+
conversation turns between the two agents.
|
|
939
|
+
(default: :obj:`20`)
|
|
940
|
+
assistant_agent_kwargs (Dict, optional): Keyword arguments
|
|
941
|
+
for configuring the assistant ChatAgent, such as model
|
|
942
|
+
type, tools, etc. (default: :obj:`None`)
|
|
943
|
+
user_agent_kwargs (Dict, optional): Keyword arguments for
|
|
944
|
+
configuring the user ChatAgent, such as model type,
|
|
945
|
+
tools, etc. (default: :obj:`None`)
|
|
946
|
+
summarize_agent_kwargs (Dict, optional): Keyword arguments
|
|
947
|
+
for configuring the summarization agent used to process
|
|
948
|
+
the conversation results. (default: :obj:`None`)
|
|
949
|
+
|
|
950
|
+
Returns:
|
|
951
|
+
Dict[str, str]: A dictionary containing the operation result:
|
|
952
|
+
- status (str): "success" or "error"
|
|
953
|
+
- message (str): Descriptive message about the operation
|
|
954
|
+
- worker_id (str): ID of the created worker (on success)
|
|
955
|
+
|
|
956
|
+
Example:
|
|
957
|
+
>>> result = add_role_playing_worker(
|
|
958
|
+
... "Design Review Team",
|
|
959
|
+
... "Design Critic",
|
|
960
|
+
... "Design Presenter",
|
|
961
|
+
... chat_turn_limit=5
|
|
962
|
+
... )
|
|
963
|
+
>>> print(result["status"]) # "success" or "error"
|
|
964
|
+
"""
|
|
965
|
+
try:
|
|
966
|
+
if workforce_instance._running:
|
|
967
|
+
return {
|
|
968
|
+
"status": "error",
|
|
969
|
+
"message": "Cannot add workers while workforce is running", # noqa: E501
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
workforce_instance.add_role_playing_worker(
|
|
973
|
+
description=description,
|
|
974
|
+
assistant_role_name=assistant_role_name,
|
|
975
|
+
user_role_name=user_role_name,
|
|
976
|
+
chat_turn_limit=chat_turn_limit,
|
|
977
|
+
assistant_agent_kwargs=assistant_agent_kwargs,
|
|
978
|
+
user_agent_kwargs=user_agent_kwargs,
|
|
979
|
+
summarize_agent_kwargs=summarize_agent_kwargs,
|
|
980
|
+
)
|
|
981
|
+
|
|
982
|
+
return {
|
|
983
|
+
"status": "success",
|
|
984
|
+
"message": f"Role playing worker '{description}' added",
|
|
985
|
+
"worker_id": workforce_instance._children[-1].node_id,
|
|
986
|
+
}
|
|
987
|
+
except Exception as e:
|
|
988
|
+
return {"status": "error", "message": str(e)}
|
|
989
|
+
|
|
990
|
+
# Now register everything using decorators
|
|
991
|
+
mcp_server.tool()(process_task)
|
|
992
|
+
mcp_server.tool()(reset)
|
|
993
|
+
mcp_server.tool()(add_single_agent_worker)
|
|
994
|
+
mcp_server.tool()(add_role_playing_worker)
|
|
995
|
+
|
|
996
|
+
mcp_server.resource("workforce://")(get_workforce_info)
|
|
997
|
+
mcp_server.tool()(get_workforce_info)
|
|
998
|
+
|
|
999
|
+
mcp_server.resource("children://")(get_children_info)
|
|
1000
|
+
mcp_server.tool()(get_children_info)
|
|
1001
|
+
|
|
1002
|
+
return mcp_server
|
camel/storages/__init__.py
CHANGED
|
@@ -31,6 +31,7 @@ from .vectordb_storages.milvus import MilvusStorage
|
|
|
31
31
|
from .vectordb_storages.oceanbase import OceanBaseStorage
|
|
32
32
|
from .vectordb_storages.qdrant import QdrantStorage
|
|
33
33
|
from .vectordb_storages.tidb import TiDBStorage
|
|
34
|
+
from .vectordb_storages.weaviate import WeaviateStorage
|
|
34
35
|
|
|
35
36
|
__all__ = [
|
|
36
37
|
'BaseKeyValueStorage',
|
|
@@ -50,4 +51,5 @@ __all__ = [
|
|
|
50
51
|
'NebulaGraph',
|
|
51
52
|
'Mem0Storage',
|
|
52
53
|
'OceanBaseStorage',
|
|
54
|
+
'WeaviateStorage',
|
|
53
55
|
]
|
|
@@ -24,6 +24,7 @@ from .milvus import MilvusStorage
|
|
|
24
24
|
from .oceanbase import OceanBaseStorage
|
|
25
25
|
from .qdrant import QdrantStorage
|
|
26
26
|
from .tidb import TiDBStorage
|
|
27
|
+
from .weaviate import WeaviateStorage
|
|
27
28
|
|
|
28
29
|
__all__ = [
|
|
29
30
|
'BaseVectorStorage',
|
|
@@ -34,6 +35,7 @@ __all__ = [
|
|
|
34
35
|
"TiDBStorage",
|
|
35
36
|
'FaissStorage',
|
|
36
37
|
'OceanBaseStorage',
|
|
38
|
+
'WeaviateStorage',
|
|
37
39
|
'VectorRecord',
|
|
38
40
|
'VectorDBStatus',
|
|
39
41
|
]
|