camel-ai 0.2.73a4__py3-none-any.whl → 0.2.80a2__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.
- camel/__init__.py +1 -1
- camel/agents/_utils.py +38 -0
- camel/agents/chat_agent.py +2217 -519
- camel/agents/mcp_agent.py +30 -27
- camel/configs/__init__.py +15 -0
- camel/configs/aihubmix_config.py +88 -0
- camel/configs/amd_config.py +70 -0
- camel/configs/cometapi_config.py +104 -0
- camel/configs/minimax_config.py +93 -0
- camel/configs/nebius_config.py +103 -0
- camel/data_collectors/alpaca_collector.py +15 -6
- camel/datasets/base_generator.py +39 -10
- camel/environments/single_step.py +28 -3
- camel/environments/tic_tac_toe.py +1 -1
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/docker/Dockerfile +3 -12
- camel/interpreters/e2b_interpreter.py +34 -1
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/loaders/__init__.py +11 -2
- camel/loaders/chunkr_reader.py +9 -0
- camel/memories/agent_memories.py +48 -4
- camel/memories/base.py +26 -0
- camel/memories/blocks/chat_history_block.py +122 -4
- camel/memories/context_creators/score_based.py +25 -384
- camel/memories/records.py +88 -8
- camel/messages/base.py +153 -34
- camel/models/__init__.py +10 -0
- camel/models/aihubmix_model.py +83 -0
- camel/models/aiml_model.py +1 -16
- camel/models/amd_model.py +101 -0
- camel/models/anthropic_model.py +6 -19
- camel/models/aws_bedrock_model.py +2 -33
- camel/models/azure_openai_model.py +114 -89
- camel/models/base_audio_model.py +3 -1
- camel/models/base_model.py +32 -14
- camel/models/cohere_model.py +1 -16
- camel/models/cometapi_model.py +83 -0
- camel/models/crynux_model.py +1 -16
- camel/models/deepseek_model.py +1 -16
- camel/models/fish_audio_model.py +6 -0
- camel/models/gemini_model.py +36 -18
- camel/models/groq_model.py +1 -17
- camel/models/internlm_model.py +1 -16
- camel/models/litellm_model.py +1 -16
- camel/models/lmstudio_model.py +1 -17
- camel/models/minimax_model.py +83 -0
- camel/models/mistral_model.py +1 -16
- camel/models/model_factory.py +27 -1
- camel/models/modelscope_model.py +1 -16
- camel/models/moonshot_model.py +105 -24
- camel/models/nebius_model.py +83 -0
- camel/models/nemotron_model.py +0 -5
- camel/models/netmind_model.py +1 -16
- camel/models/novita_model.py +1 -16
- camel/models/nvidia_model.py +1 -16
- camel/models/ollama_model.py +4 -19
- camel/models/openai_compatible_model.py +62 -41
- camel/models/openai_model.py +62 -57
- camel/models/openrouter_model.py +1 -17
- camel/models/ppio_model.py +1 -16
- camel/models/qianfan_model.py +1 -16
- camel/models/qwen_model.py +1 -16
- camel/models/reka_model.py +1 -16
- camel/models/samba_model.py +34 -47
- camel/models/sglang_model.py +64 -31
- camel/models/siliconflow_model.py +1 -16
- camel/models/stub_model.py +0 -4
- camel/models/togetherai_model.py +1 -16
- camel/models/vllm_model.py +1 -16
- camel/models/volcano_model.py +0 -17
- camel/models/watsonx_model.py +1 -16
- camel/models/yi_model.py +1 -16
- camel/models/zhipuai_model.py +60 -16
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/retrievers/auto_retriever.py +1 -0
- camel/runtimes/daytona_runtime.py +11 -12
- camel/societies/__init__.py +2 -0
- camel/societies/workforce/__init__.py +2 -0
- camel/societies/workforce/events.py +122 -0
- camel/societies/workforce/prompts.py +146 -66
- camel/societies/workforce/role_playing_worker.py +15 -11
- camel/societies/workforce/single_agent_worker.py +302 -65
- camel/societies/workforce/structured_output_handler.py +30 -18
- camel/societies/workforce/task_channel.py +163 -27
- camel/societies/workforce/utils.py +107 -13
- camel/societies/workforce/workflow_memory_manager.py +772 -0
- camel/societies/workforce/workforce.py +1949 -579
- camel/societies/workforce/workforce_callback.py +74 -0
- camel/societies/workforce/workforce_logger.py +168 -145
- camel/societies/workforce/workforce_metrics.py +33 -0
- camel/storages/key_value_storages/json.py +15 -2
- camel/storages/key_value_storages/mem0_cloud.py +48 -47
- camel/storages/object_storages/google_cloud.py +1 -1
- camel/storages/vectordb_storages/oceanbase.py +13 -13
- camel/storages/vectordb_storages/qdrant.py +3 -3
- camel/storages/vectordb_storages/tidb.py +8 -6
- camel/tasks/task.py +4 -3
- camel/toolkits/__init__.py +20 -7
- camel/toolkits/aci_toolkit.py +45 -0
- camel/toolkits/base.py +6 -4
- camel/toolkits/code_execution.py +28 -1
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/dappier_toolkit.py +5 -1
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +11 -31
- camel/toolkits/excel_toolkit.py +1 -1
- camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +430 -36
- camel/toolkits/function_tool.py +13 -3
- camel/toolkits/github_toolkit.py +104 -17
- camel/toolkits/gmail_toolkit.py +1839 -0
- camel/toolkits/google_calendar_toolkit.py +38 -4
- camel/toolkits/google_drive_mcp_toolkit.py +12 -31
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +15 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +77 -8
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +884 -88
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +5 -612
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +0 -1
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +959 -89
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +9 -2
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +281 -213
- camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +23 -3
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +72 -7
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +582 -132
- camel/toolkits/hybrid_browser_toolkit_py/actions.py +158 -0
- camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +55 -8
- camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +43 -0
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +321 -8
- camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +10 -4
- camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +45 -4
- camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +151 -53
- camel/toolkits/klavis_toolkit.py +5 -1
- camel/toolkits/markitdown_toolkit.py +27 -1
- camel/toolkits/math_toolkit.py +64 -10
- camel/toolkits/mcp_toolkit.py +366 -71
- camel/toolkits/memory_toolkit.py +5 -1
- camel/toolkits/message_integration.py +18 -13
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/note_taking_toolkit.py +19 -10
- camel/toolkits/notion_mcp_toolkit.py +16 -26
- camel/toolkits/openbb_toolkit.py +5 -1
- camel/toolkits/origene_mcp_toolkit.py +8 -49
- camel/toolkits/playwright_mcp_toolkit.py +12 -31
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/search_toolkit.py +264 -91
- camel/toolkits/slack_toolkit.py +64 -10
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +17 -11
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/toolkits/zapier_toolkit.py +5 -1
- camel/types/__init__.py +2 -2
- camel/types/enums.py +274 -7
- camel/types/openai_types.py +2 -2
- camel/types/unified_model_type.py +15 -0
- camel/utils/commons.py +36 -5
- camel/utils/constants.py +3 -0
- camel/utils/context_utils.py +1003 -0
- camel/utils/mcp.py +138 -4
- camel/utils/token_counting.py +43 -20
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +223 -83
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +170 -141
- camel/loaders/pandas_reader.py +0 -368
- camel/toolkits/openai_agent_toolkit.py +0 -135
- camel/toolkits/terminal_toolkit.py +0 -1550
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from abc import ABC, abstractmethod
|
|
17
|
+
|
|
18
|
+
from .events import (
|
|
19
|
+
AllTasksCompletedEvent,
|
|
20
|
+
TaskAssignedEvent,
|
|
21
|
+
TaskCompletedEvent,
|
|
22
|
+
TaskCreatedEvent,
|
|
23
|
+
TaskDecomposedEvent,
|
|
24
|
+
TaskFailedEvent,
|
|
25
|
+
TaskStartedEvent,
|
|
26
|
+
WorkerCreatedEvent,
|
|
27
|
+
WorkerDeletedEvent,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class WorkforceCallback(ABC):
|
|
32
|
+
r"""Interface for recording workforce lifecycle events.
|
|
33
|
+
|
|
34
|
+
Implementations should persist or stream events as appropriate.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def log_task_created(
|
|
39
|
+
self,
|
|
40
|
+
event: TaskCreatedEvent,
|
|
41
|
+
) -> None:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
@abstractmethod
|
|
45
|
+
def log_task_decomposed(self, event: TaskDecomposedEvent) -> None:
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
@abstractmethod
|
|
49
|
+
def log_task_assigned(self, event: TaskAssignedEvent) -> None:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
@abstractmethod
|
|
53
|
+
def log_task_started(self, event: TaskStartedEvent) -> None:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def log_task_completed(self, event: TaskCompletedEvent) -> None:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def log_task_failed(self, event: TaskFailedEvent) -> None:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def log_worker_created(self, event: WorkerCreatedEvent) -> None:
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def log_worker_deleted(self, event: WorkerDeletedEvent) -> None:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def log_all_tasks_completed(self, event: AllTasksCompletedEvent) -> None:
|
|
74
|
+
pass
|
|
@@ -16,12 +16,26 @@ from datetime import datetime, timezone
|
|
|
16
16
|
from typing import Any, Dict, List, Optional
|
|
17
17
|
|
|
18
18
|
from camel.logger import get_logger
|
|
19
|
+
from camel.societies.workforce.events import (
|
|
20
|
+
AllTasksCompletedEvent,
|
|
21
|
+
QueueStatusEvent,
|
|
22
|
+
TaskAssignedEvent,
|
|
23
|
+
TaskCompletedEvent,
|
|
24
|
+
TaskCreatedEvent,
|
|
25
|
+
TaskDecomposedEvent,
|
|
26
|
+
TaskFailedEvent,
|
|
27
|
+
TaskStartedEvent,
|
|
28
|
+
WorkerCreatedEvent,
|
|
29
|
+
WorkerDeletedEvent,
|
|
30
|
+
)
|
|
31
|
+
from camel.societies.workforce.workforce_callback import WorkforceCallback
|
|
32
|
+
from camel.societies.workforce.workforce_metrics import WorkforceMetrics
|
|
19
33
|
from camel.types.agents import ToolCallingRecord
|
|
20
34
|
|
|
21
35
|
logger = get_logger(__name__)
|
|
22
36
|
|
|
23
37
|
|
|
24
|
-
class WorkforceLogger:
|
|
38
|
+
class WorkforceLogger(WorkforceCallback, WorkforceMetrics):
|
|
25
39
|
r"""Logs events and metrics for a Workforce instance."""
|
|
26
40
|
|
|
27
41
|
def __init__(self, workforce_id: str):
|
|
@@ -55,195 +69,201 @@ class WorkforceLogger:
|
|
|
55
69
|
|
|
56
70
|
def log_task_created(
|
|
57
71
|
self,
|
|
58
|
-
|
|
59
|
-
description: str,
|
|
60
|
-
parent_task_id: Optional[str] = None,
|
|
61
|
-
task_type: Optional[str] = None,
|
|
62
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
72
|
+
event: TaskCreatedEvent,
|
|
63
73
|
) -> None:
|
|
64
74
|
r"""Logs the creation of a new task."""
|
|
65
75
|
self._log_event(
|
|
66
|
-
|
|
67
|
-
task_id=task_id,
|
|
68
|
-
description=description,
|
|
69
|
-
parent_task_id=parent_task_id,
|
|
70
|
-
task_type=task_type,
|
|
71
|
-
metadata=metadata or {},
|
|
76
|
+
event_type=event.event_type,
|
|
77
|
+
task_id=event.task_id,
|
|
78
|
+
description=event.description,
|
|
79
|
+
parent_task_id=event.parent_task_id,
|
|
80
|
+
task_type=event.task_type,
|
|
81
|
+
metadata=event.metadata or {},
|
|
72
82
|
)
|
|
73
|
-
self._task_hierarchy[task_id] = {
|
|
74
|
-
'parent': parent_task_id,
|
|
83
|
+
self._task_hierarchy[event.task_id] = {
|
|
84
|
+
'parent': event.parent_task_id,
|
|
75
85
|
'children': [],
|
|
76
86
|
'status': 'created',
|
|
77
|
-
'description': description,
|
|
87
|
+
'description': event.description,
|
|
78
88
|
'assigned_to': None,
|
|
79
|
-
**(metadata or {}),
|
|
89
|
+
**(event.metadata or {}),
|
|
80
90
|
}
|
|
81
|
-
if
|
|
82
|
-
|
|
91
|
+
if (
|
|
92
|
+
event.parent_task_id
|
|
93
|
+
and event.parent_task_id in self._task_hierarchy
|
|
94
|
+
):
|
|
95
|
+
self._task_hierarchy[event.parent_task_id]['children'].append(
|
|
96
|
+
event.task_id
|
|
97
|
+
)
|
|
83
98
|
|
|
84
99
|
def log_task_decomposed(
|
|
85
100
|
self,
|
|
86
|
-
|
|
87
|
-
subtask_ids: List[str],
|
|
88
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
101
|
+
event: TaskDecomposedEvent,
|
|
89
102
|
) -> None:
|
|
90
103
|
r"""Logs the decomposition of a task into subtasks."""
|
|
91
104
|
self._log_event(
|
|
92
|
-
|
|
93
|
-
parent_task_id=parent_task_id,
|
|
94
|
-
subtask_ids=subtask_ids,
|
|
95
|
-
metadata=metadata or {},
|
|
105
|
+
event_type=event.event_type,
|
|
106
|
+
parent_task_id=event.parent_task_id,
|
|
107
|
+
subtask_ids=event.subtask_ids,
|
|
108
|
+
metadata=event.metadata or {},
|
|
96
109
|
)
|
|
97
|
-
if parent_task_id in self._task_hierarchy:
|
|
98
|
-
self._task_hierarchy[parent_task_id]['status'] = "decomposed"
|
|
110
|
+
if event.parent_task_id in self._task_hierarchy:
|
|
111
|
+
self._task_hierarchy[event.parent_task_id]['status'] = "decomposed"
|
|
99
112
|
|
|
100
113
|
def log_task_assigned(
|
|
101
114
|
self,
|
|
102
|
-
|
|
103
|
-
worker_id: str,
|
|
104
|
-
queue_time_seconds: Optional[float] = None,
|
|
105
|
-
dependencies: Optional[List[str]] = None,
|
|
106
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
115
|
+
event: TaskAssignedEvent,
|
|
107
116
|
) -> None:
|
|
108
117
|
r"""Logs the assignment of a task to a worker."""
|
|
109
118
|
self._log_event(
|
|
110
|
-
|
|
111
|
-
task_id=task_id,
|
|
112
|
-
worker_id=worker_id,
|
|
113
|
-
queue_time_seconds=queue_time_seconds,
|
|
114
|
-
dependencies=dependencies or [],
|
|
115
|
-
metadata=metadata or {},
|
|
119
|
+
event_type=event.event_type,
|
|
120
|
+
task_id=event.task_id,
|
|
121
|
+
worker_id=event.worker_id,
|
|
122
|
+
queue_time_seconds=event.queue_time_seconds,
|
|
123
|
+
dependencies=event.dependencies or [],
|
|
124
|
+
metadata=event.metadata or {},
|
|
116
125
|
)
|
|
117
|
-
if task_id in self._task_hierarchy:
|
|
118
|
-
self._task_hierarchy[task_id]['status'] = 'assigned'
|
|
119
|
-
self._task_hierarchy[task_id]['assigned_to'] =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
self.
|
|
123
|
-
|
|
126
|
+
if event.task_id in self._task_hierarchy:
|
|
127
|
+
self._task_hierarchy[event.task_id]['status'] = 'assigned'
|
|
128
|
+
self._task_hierarchy[event.task_id]['assigned_to'] = (
|
|
129
|
+
event.worker_id
|
|
130
|
+
)
|
|
131
|
+
self._task_hierarchy[event.task_id]['dependencies'] = (
|
|
132
|
+
event.dependencies or []
|
|
133
|
+
)
|
|
134
|
+
if event.worker_id in self._worker_information:
|
|
135
|
+
self._worker_information[event.worker_id]['current_task_id'] = (
|
|
136
|
+
event.task_id
|
|
137
|
+
)
|
|
138
|
+
self._worker_information[event.worker_id]['status'] = 'busy'
|
|
124
139
|
|
|
125
140
|
def log_task_started(
|
|
126
141
|
self,
|
|
127
|
-
|
|
128
|
-
worker_id: str,
|
|
129
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
142
|
+
event: TaskStartedEvent,
|
|
130
143
|
) -> None:
|
|
131
144
|
r"""Logs when a worker starts processing a task."""
|
|
132
145
|
self._log_event(
|
|
133
|
-
|
|
134
|
-
task_id=task_id,
|
|
135
|
-
worker_id=worker_id,
|
|
136
|
-
metadata=metadata or {},
|
|
146
|
+
event_type=event.event_type,
|
|
147
|
+
task_id=event.task_id,
|
|
148
|
+
worker_id=event.worker_id,
|
|
149
|
+
metadata=event.metadata or {},
|
|
137
150
|
)
|
|
138
|
-
if task_id in self._task_hierarchy:
|
|
139
|
-
self._task_hierarchy[task_id]['status'] = 'processing'
|
|
151
|
+
if event.task_id in self._task_hierarchy:
|
|
152
|
+
self._task_hierarchy[event.task_id]['status'] = 'processing'
|
|
140
153
|
|
|
141
|
-
def log_task_completed(
|
|
142
|
-
self,
|
|
143
|
-
task_id: str,
|
|
144
|
-
worker_id: str,
|
|
145
|
-
result_summary: Optional[str] = None,
|
|
146
|
-
processing_time_seconds: Optional[float] = None,
|
|
147
|
-
token_usage: Optional[Dict[str, int]] = None,
|
|
148
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
149
|
-
) -> None:
|
|
154
|
+
def log_task_completed(self, event: TaskCompletedEvent) -> None:
|
|
150
155
|
r"""Logs the successful completion of a task."""
|
|
151
156
|
self._log_event(
|
|
152
|
-
|
|
153
|
-
task_id=task_id,
|
|
154
|
-
worker_id=worker_id,
|
|
155
|
-
result_summary=result_summary,
|
|
156
|
-
processing_time_seconds=processing_time_seconds,
|
|
157
|
-
token_usage=token_usage or {},
|
|
158
|
-
metadata=metadata or {},
|
|
157
|
+
event_type=event.event_type,
|
|
158
|
+
task_id=event.task_id,
|
|
159
|
+
worker_id=event.worker_id,
|
|
160
|
+
result_summary=event.result_summary,
|
|
161
|
+
processing_time_seconds=event.processing_time_seconds,
|
|
162
|
+
token_usage=event.token_usage or {},
|
|
163
|
+
metadata=event.metadata or {},
|
|
159
164
|
)
|
|
160
|
-
if task_id in self._task_hierarchy:
|
|
161
|
-
self._task_hierarchy[task_id]['status'] = 'completed'
|
|
162
|
-
self._task_hierarchy[task_id]['assigned_to'] = None
|
|
165
|
+
if event.task_id in self._task_hierarchy:
|
|
166
|
+
self._task_hierarchy[event.task_id]['status'] = 'completed'
|
|
167
|
+
self._task_hierarchy[event.task_id]['assigned_to'] = None
|
|
163
168
|
# Store processing time in task hierarchy for display in tree
|
|
164
|
-
if processing_time_seconds is not None:
|
|
165
|
-
self._task_hierarchy[task_id][
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
if event.processing_time_seconds is not None:
|
|
170
|
+
self._task_hierarchy[event.task_id][
|
|
171
|
+
'completion_time_seconds'
|
|
172
|
+
] = event.processing_time_seconds
|
|
168
173
|
# Store token usage in task hierarchy for display in tree
|
|
169
|
-
if token_usage is not None:
|
|
170
|
-
self._task_hierarchy[task_id]['token_usage'] =
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
self._worker_information[worker_id]['
|
|
175
|
-
|
|
174
|
+
if event.token_usage is not None:
|
|
175
|
+
self._task_hierarchy[event.task_id]['token_usage'] = (
|
|
176
|
+
event.token_usage
|
|
177
|
+
)
|
|
178
|
+
if event.worker_id in self._worker_information:
|
|
179
|
+
self._worker_information[event.worker_id]['current_task_id'] = None
|
|
180
|
+
self._worker_information[event.worker_id]['status'] = 'idle'
|
|
181
|
+
self._worker_information[event.worker_id]['tasks_completed'] = (
|
|
182
|
+
self._worker_information[event.worker_id].get(
|
|
183
|
+
'tasks_completed', 0
|
|
184
|
+
)
|
|
176
185
|
+ 1
|
|
177
186
|
)
|
|
178
187
|
|
|
179
188
|
def log_task_failed(
|
|
180
189
|
self,
|
|
181
|
-
|
|
182
|
-
error_message: str,
|
|
183
|
-
worker_id: Optional[str] = None,
|
|
184
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
190
|
+
event: TaskFailedEvent,
|
|
185
191
|
) -> None:
|
|
186
192
|
r"""Logs the failure of a task."""
|
|
187
193
|
self._log_event(
|
|
188
|
-
|
|
189
|
-
task_id=task_id,
|
|
190
|
-
worker_id=worker_id,
|
|
191
|
-
error_message=error_message,
|
|
192
|
-
metadata=metadata or {},
|
|
194
|
+
event_type=event.event_type,
|
|
195
|
+
task_id=event.task_id,
|
|
196
|
+
worker_id=event.worker_id,
|
|
197
|
+
error_message=event.error_message,
|
|
198
|
+
metadata=event.metadata or {},
|
|
193
199
|
)
|
|
194
|
-
if task_id in self._task_hierarchy:
|
|
195
|
-
self._task_hierarchy[task_id]['status'] = 'failed'
|
|
196
|
-
self._task_hierarchy[task_id]['error'] = error_message
|
|
197
|
-
self._task_hierarchy[task_id]['assigned_to'] = None
|
|
198
|
-
if worker_id and worker_id in self._worker_information:
|
|
199
|
-
self._worker_information[worker_id]['current_task_id'] = None
|
|
200
|
-
self._worker_information[worker_id]['status'] = 'idle'
|
|
201
|
-
self._worker_information[worker_id]['tasks_failed'] = (
|
|
202
|
-
self._worker_information[worker_id].get(
|
|
200
|
+
if event.task_id in self._task_hierarchy:
|
|
201
|
+
self._task_hierarchy[event.task_id]['status'] = 'failed'
|
|
202
|
+
self._task_hierarchy[event.task_id]['error'] = event.error_message
|
|
203
|
+
self._task_hierarchy[event.task_id]['assigned_to'] = None
|
|
204
|
+
if event.worker_id and event.worker_id in self._worker_information:
|
|
205
|
+
self._worker_information[event.worker_id]['current_task_id'] = None
|
|
206
|
+
self._worker_information[event.worker_id]['status'] = 'idle'
|
|
207
|
+
self._worker_information[event.worker_id]['tasks_failed'] = (
|
|
208
|
+
self._worker_information[event.worker_id].get(
|
|
209
|
+
'tasks_failed', 0
|
|
210
|
+
)
|
|
211
|
+
+ 1
|
|
203
212
|
)
|
|
204
213
|
|
|
205
214
|
def log_worker_created(
|
|
206
215
|
self,
|
|
207
|
-
|
|
208
|
-
worker_type: str,
|
|
209
|
-
role: str,
|
|
210
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
216
|
+
event: WorkerCreatedEvent,
|
|
211
217
|
) -> None:
|
|
212
218
|
r"""Logs the creation of a new worker."""
|
|
213
219
|
self._log_event(
|
|
214
|
-
|
|
215
|
-
worker_id=worker_id,
|
|
216
|
-
worker_type=worker_type,
|
|
217
|
-
role=role,
|
|
218
|
-
metadata=metadata or {},
|
|
220
|
+
event_type=event.event_type,
|
|
221
|
+
worker_id=event.worker_id,
|
|
222
|
+
worker_type=event.worker_type,
|
|
223
|
+
role=event.role,
|
|
224
|
+
metadata=event.metadata or {},
|
|
219
225
|
)
|
|
220
|
-
self._worker_information[worker_id] = {
|
|
221
|
-
'type': worker_type,
|
|
222
|
-
'role': role,
|
|
226
|
+
self._worker_information[event.worker_id] = {
|
|
227
|
+
'type': event.worker_type,
|
|
228
|
+
'role': event.role,
|
|
223
229
|
'status': 'idle',
|
|
224
230
|
'current_task_id': None,
|
|
225
231
|
'tasks_completed': 0,
|
|
226
232
|
'tasks_failed': 0,
|
|
227
|
-
**(metadata or {}),
|
|
233
|
+
**(event.metadata or {}),
|
|
228
234
|
}
|
|
229
235
|
|
|
230
236
|
def log_worker_deleted(
|
|
231
237
|
self,
|
|
232
|
-
|
|
233
|
-
reason: Optional[str] = None,
|
|
234
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
238
|
+
event: WorkerDeletedEvent,
|
|
235
239
|
) -> None:
|
|
236
240
|
r"""Logs the deletion of a worker."""
|
|
237
241
|
self._log_event(
|
|
238
|
-
|
|
239
|
-
worker_id=worker_id,
|
|
240
|
-
reason=reason,
|
|
241
|
-
metadata=metadata or {},
|
|
242
|
+
event_type=event.event_type,
|
|
243
|
+
worker_id=event.worker_id,
|
|
244
|
+
reason=event.reason,
|
|
245
|
+
metadata=event.metadata or {},
|
|
242
246
|
)
|
|
243
|
-
if worker_id in self._worker_information:
|
|
244
|
-
self._worker_information[worker_id]['status'] = 'deleted'
|
|
247
|
+
if event.worker_id in self._worker_information:
|
|
248
|
+
self._worker_information[event.worker_id]['status'] = 'deleted'
|
|
245
249
|
# Or del self._worker_information[worker_id]
|
|
246
250
|
|
|
251
|
+
def log_queue_status(
|
|
252
|
+
self,
|
|
253
|
+
event: QueueStatusEvent,
|
|
254
|
+
) -> None:
|
|
255
|
+
r"""Logs the status of a task queue."""
|
|
256
|
+
self._log_event(
|
|
257
|
+
event_type=event.event_type,
|
|
258
|
+
queue_name=event.queue_name,
|
|
259
|
+
length=event.length,
|
|
260
|
+
pending_task_ids=event.pending_task_ids or [],
|
|
261
|
+
metadata=event.metadata or {},
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
def log_all_tasks_completed(self, event: AllTasksCompletedEvent) -> None:
|
|
265
|
+
pass
|
|
266
|
+
|
|
247
267
|
def reset_task_data(self) -> None:
|
|
248
268
|
r"""Resets logs and data related to tasks, preserving worker
|
|
249
269
|
information.
|
|
@@ -263,22 +283,6 @@ class WorkforceLogger:
|
|
|
263
283
|
f"{self.workforce_id}"
|
|
264
284
|
)
|
|
265
285
|
|
|
266
|
-
def log_queue_status(
|
|
267
|
-
self,
|
|
268
|
-
queue_name: str,
|
|
269
|
-
length: int,
|
|
270
|
-
pending_task_ids: Optional[List[str]] = None,
|
|
271
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
272
|
-
) -> None:
|
|
273
|
-
r"""Logs the status of a task queue."""
|
|
274
|
-
self._log_event(
|
|
275
|
-
'queue_status',
|
|
276
|
-
queue_name=queue_name,
|
|
277
|
-
length=length,
|
|
278
|
-
pending_task_ids=pending_task_ids or [],
|
|
279
|
-
metadata=metadata or {},
|
|
280
|
-
)
|
|
281
|
-
|
|
282
286
|
def dump_to_json(self, file_path: str) -> None:
|
|
283
287
|
r"""Dumps all log entries to a JSON file.
|
|
284
288
|
|
|
@@ -495,6 +499,11 @@ class WorkforceLogger:
|
|
|
495
499
|
|
|
496
500
|
tasks_handled_by_worker: Dict[str, int] = {}
|
|
497
501
|
|
|
502
|
+
# Track unique task final states to avoid double-counting
|
|
503
|
+
task_final_states: Dict[
|
|
504
|
+
str, str
|
|
505
|
+
] = {} # task_id -> 'completed' or 'failed'
|
|
506
|
+
|
|
498
507
|
# Helper function to check if a task is the main task (has no parent)
|
|
499
508
|
def is_main_task(task_id: str) -> bool:
|
|
500
509
|
return (
|
|
@@ -528,7 +537,9 @@ class WorkforceLogger:
|
|
|
528
537
|
elif event_type == 'task_completed':
|
|
529
538
|
# Exclude main task from total count
|
|
530
539
|
if not is_main_task(task_id):
|
|
531
|
-
|
|
540
|
+
# Track final state - a completed task overwrites any
|
|
541
|
+
# previous failed state
|
|
542
|
+
task_final_states[task_id] = 'completed'
|
|
532
543
|
# Count tasks handled by worker (only for non-main tasks)
|
|
533
544
|
if 'worker_id' in entry and entry['worker_id'] is not None:
|
|
534
545
|
worker_id = entry['worker_id']
|
|
@@ -550,7 +561,11 @@ class WorkforceLogger:
|
|
|
550
561
|
elif event_type == 'task_failed':
|
|
551
562
|
# Exclude main task from total count
|
|
552
563
|
if not is_main_task(task_id):
|
|
553
|
-
|
|
564
|
+
# Only track as failed if not already completed
|
|
565
|
+
# (in case of retries, the final completion overwrites
|
|
566
|
+
# failed state)
|
|
567
|
+
if task_final_states.get(task_id) != 'completed':
|
|
568
|
+
task_final_states[task_id] = 'failed'
|
|
554
569
|
# Count tasks handled by worker (only for non-main tasks)
|
|
555
570
|
if 'worker_id' in entry and entry['worker_id'] is not None:
|
|
556
571
|
worker_id = entry['worker_id']
|
|
@@ -565,6 +580,14 @@ class WorkforceLogger:
|
|
|
565
580
|
kpis['total_workforce_running_time_seconds'] = (
|
|
566
581
|
last_timestamp - first_timestamp
|
|
567
582
|
).total_seconds()
|
|
583
|
+
|
|
584
|
+
# Count unique tasks by final state
|
|
585
|
+
for _task_id, state in task_final_states.items():
|
|
586
|
+
if state == 'completed':
|
|
587
|
+
kpis['total_tasks_completed'] += 1
|
|
588
|
+
elif state == 'failed':
|
|
589
|
+
kpis['total_tasks_failed'] += 1
|
|
590
|
+
|
|
568
591
|
# Calculate worker utilization based on proportion of tasks handled
|
|
569
592
|
total_tasks_processed_for_utilization = (
|
|
570
593
|
kpis['total_tasks_completed'] + kpis['total_tasks_failed']
|
|
@@ -610,9 +633,9 @@ class WorkforceLogger:
|
|
|
610
633
|
|
|
611
634
|
kpis['total_workers_created'] = len(self._worker_information)
|
|
612
635
|
|
|
613
|
-
# Current pending tasks
|
|
614
|
-
kpis['current_pending_tasks'] = kpis['total_tasks_created'] - (
|
|
615
|
-
|
|
636
|
+
# Current pending tasks - tasks created but not yet completed or failed
|
|
637
|
+
kpis['current_pending_tasks'] = kpis['total_tasks_created'] - len(
|
|
638
|
+
task_final_states
|
|
616
639
|
)
|
|
617
640
|
|
|
618
641
|
return kpis
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from typing import Any, Dict
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class WorkforceMetrics(ABC):
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def reset_task_data(self) -> None:
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def dump_to_json(self, file_path: str) -> None:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def get_ascii_tree_representation(self) -> str:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def get_kpis(self) -> Dict[str, Any]:
|
|
33
|
+
pass
|
|
@@ -17,6 +17,8 @@ from enum import EnumMeta
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from typing import Any, ClassVar, Dict, List, Optional
|
|
19
19
|
|
|
20
|
+
from pydantic import BaseModel
|
|
21
|
+
|
|
20
22
|
from camel.storages.key_value_storages import BaseKeyValueStorage
|
|
21
23
|
from camel.types import (
|
|
22
24
|
ModelType,
|
|
@@ -27,8 +29,13 @@ from camel.types import (
|
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class CamelJSONEncoder(json.JSONEncoder):
|
|
30
|
-
r"""A custom JSON encoder for serializing
|
|
31
|
-
|
|
32
|
+
r"""A custom JSON encoder for serializing CAMEL-specific types.
|
|
33
|
+
|
|
34
|
+
Handles serialization of:
|
|
35
|
+
- Enumerated types (RoleType, TaskType, ModelType, OpenAIBackendRole)
|
|
36
|
+
- Pydantic BaseModel objects (from structured outputs)
|
|
37
|
+
|
|
38
|
+
Ensures these types can be stored in and retrieved from JSON format.
|
|
32
39
|
"""
|
|
33
40
|
|
|
34
41
|
CAMEL_ENUMS: ClassVar[Dict[str, EnumMeta]] = {
|
|
@@ -39,8 +46,14 @@ class CamelJSONEncoder(json.JSONEncoder):
|
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
def default(self, obj) -> Any:
|
|
49
|
+
# Handle CAMEL enum types
|
|
42
50
|
if type(obj) in self.CAMEL_ENUMS.values():
|
|
43
51
|
return {"__enum__": str(obj)}
|
|
52
|
+
|
|
53
|
+
# Handle Pydantic BaseModel objects (e.g., from structured outputs)
|
|
54
|
+
if isinstance(obj, BaseModel):
|
|
55
|
+
return obj.model_dump()
|
|
56
|
+
|
|
44
57
|
# Let the base class default method raise the TypeError
|
|
45
58
|
return json.JSONEncoder.default(self, obj)
|
|
46
59
|
|