camel-ai 0.2.62__py3-none-any.whl → 0.2.65__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 +95 -24
- camel/agents/mcp_agent.py +5 -1
- camel/benchmarks/mock_website/README.md +96 -0
- camel/benchmarks/mock_website/mock_web.py +299 -0
- camel/benchmarks/mock_website/requirements.txt +3 -0
- camel/benchmarks/mock_website/shopping_mall/app.py +465 -0
- camel/benchmarks/mock_website/task.json +104 -0
- camel/configs/__init__.py +3 -0
- camel/configs/crynux_config.py +94 -0
- camel/datasets/models.py +1 -1
- camel/datasets/static_dataset.py +6 -0
- 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 +87 -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/societies/workforce/role_playing_worker.py +11 -3
- camel/societies/workforce/single_agent_worker.py +31 -1
- camel/societies/workforce/utils.py +51 -0
- 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 +27 -10
- camel/toolkits/async_browser_toolkit.py +97 -54
- camel/toolkits/browser_toolkit.py +65 -18
- camel/toolkits/code_execution.py +37 -8
- camel/toolkits/function_tool.py +2 -2
- camel/toolkits/mcp_toolkit.py +13 -2
- camel/toolkits/playwright_mcp_toolkit.py +16 -3
- camel/toolkits/task_planning_toolkit.py +134 -0
- camel/types/enums.py +61 -2
- 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 +84 -17
- {camel_ai-0.2.62.dist-info → camel_ai-0.2.65.dist-info}/METADATA +9 -12
- {camel_ai-0.2.62.dist-info → camel_ai-0.2.65.dist-info}/RECORD +59 -49
- {camel_ai-0.2.62.dist-info → camel_ai-0.2.65.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.62.dist-info → camel_ai-0.2.65.dist-info}/licenses/LICENSE +0 -0
camel/models/samba_model.py
CHANGED
|
@@ -38,6 +38,9 @@ from camel.utils import (
|
|
|
38
38
|
BaseTokenCounter,
|
|
39
39
|
OpenAITokenCounter,
|
|
40
40
|
api_keys_required,
|
|
41
|
+
get_current_agent_session_id,
|
|
42
|
+
update_current_observation,
|
|
43
|
+
update_langfuse_trace,
|
|
41
44
|
)
|
|
42
45
|
|
|
43
46
|
try:
|
|
@@ -48,6 +51,14 @@ try:
|
|
|
48
51
|
except (ImportError, AttributeError):
|
|
49
52
|
LLMEvent = None
|
|
50
53
|
|
|
54
|
+
if os.environ.get("LANGFUSE_ENABLED", "False").lower() == "true":
|
|
55
|
+
try:
|
|
56
|
+
from langfuse.decorators import observe
|
|
57
|
+
except ImportError:
|
|
58
|
+
from camel.utils import observe
|
|
59
|
+
else:
|
|
60
|
+
from camel.utils import observe
|
|
61
|
+
|
|
51
62
|
|
|
52
63
|
class SambaModel(BaseModelBackend):
|
|
53
64
|
r"""SambaNova service interface.
|
|
@@ -161,6 +172,7 @@ class SambaModel(BaseModelBackend):
|
|
|
161
172
|
" SambaNova service"
|
|
162
173
|
)
|
|
163
174
|
|
|
175
|
+
@observe(as_type="generation")
|
|
164
176
|
async def _arun( # type: ignore[misc]
|
|
165
177
|
self,
|
|
166
178
|
messages: List[OpenAIMessage],
|
|
@@ -178,13 +190,42 @@ class SambaModel(BaseModelBackend):
|
|
|
178
190
|
`ChatCompletion` in the non-stream mode, or
|
|
179
191
|
`AsyncStream[ChatCompletionChunk]` in the stream mode.
|
|
180
192
|
"""
|
|
193
|
+
|
|
194
|
+
update_current_observation(
|
|
195
|
+
input={
|
|
196
|
+
"messages": messages,
|
|
197
|
+
"tools": tools,
|
|
198
|
+
},
|
|
199
|
+
model=str(self.model_type),
|
|
200
|
+
model_parameters=self.model_config_dict,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Update Langfuse trace with current agent session and metadata
|
|
204
|
+
agent_session_id = get_current_agent_session_id()
|
|
205
|
+
if agent_session_id:
|
|
206
|
+
update_langfuse_trace(
|
|
207
|
+
session_id=agent_session_id,
|
|
208
|
+
metadata={
|
|
209
|
+
"source": "camel",
|
|
210
|
+
"agent_id": agent_session_id,
|
|
211
|
+
"agent_type": "camel_chat_agent",
|
|
212
|
+
"model_type": str(self.model_type),
|
|
213
|
+
},
|
|
214
|
+
tags=["CAMEL-AI", str(self.model_type)],
|
|
215
|
+
)
|
|
216
|
+
|
|
181
217
|
if "tools" in self.model_config_dict:
|
|
182
218
|
del self.model_config_dict["tools"]
|
|
183
219
|
if self.model_config_dict.get("stream") is True:
|
|
184
220
|
return await self._arun_streaming(messages)
|
|
185
221
|
else:
|
|
186
|
-
|
|
222
|
+
response = await self._arun_non_streaming(messages)
|
|
223
|
+
update_current_observation(
|
|
224
|
+
usage=response.usage,
|
|
225
|
+
)
|
|
226
|
+
return response
|
|
187
227
|
|
|
228
|
+
@observe(as_type="generation")
|
|
188
229
|
def _run( # type: ignore[misc]
|
|
189
230
|
self,
|
|
190
231
|
messages: List[OpenAIMessage],
|
|
@@ -202,12 +243,38 @@ class SambaModel(BaseModelBackend):
|
|
|
202
243
|
`ChatCompletion` in the non-stream mode, or
|
|
203
244
|
`Stream[ChatCompletionChunk]` in the stream mode.
|
|
204
245
|
"""
|
|
246
|
+
update_current_observation(
|
|
247
|
+
input={
|
|
248
|
+
"messages": messages,
|
|
249
|
+
"tools": tools,
|
|
250
|
+
},
|
|
251
|
+
model=str(self.model_type),
|
|
252
|
+
model_parameters=self.model_config_dict,
|
|
253
|
+
)
|
|
254
|
+
# Update Langfuse trace with current agent session and metadata
|
|
255
|
+
agent_session_id = get_current_agent_session_id()
|
|
256
|
+
if agent_session_id:
|
|
257
|
+
update_langfuse_trace(
|
|
258
|
+
session_id=agent_session_id,
|
|
259
|
+
metadata={
|
|
260
|
+
"source": "camel",
|
|
261
|
+
"agent_id": agent_session_id,
|
|
262
|
+
"agent_type": "camel_chat_agent",
|
|
263
|
+
"model_type": str(self.model_type),
|
|
264
|
+
},
|
|
265
|
+
tags=["CAMEL-AI", str(self.model_type)],
|
|
266
|
+
)
|
|
267
|
+
|
|
205
268
|
if "tools" in self.model_config_dict:
|
|
206
269
|
del self.model_config_dict["tools"]
|
|
207
270
|
if self.model_config_dict.get("stream") is True:
|
|
208
271
|
return self._run_streaming(messages)
|
|
209
272
|
else:
|
|
210
|
-
|
|
273
|
+
response = self._run_non_streaming(messages)
|
|
274
|
+
update_current_observation(
|
|
275
|
+
usage=response.usage,
|
|
276
|
+
)
|
|
277
|
+
return response
|
|
211
278
|
|
|
212
279
|
def _run_streaming(
|
|
213
280
|
self, messages: List[OpenAIMessage]
|
camel/models/sglang_model.py
CHANGED
|
@@ -29,7 +29,21 @@ from camel.types import (
|
|
|
29
29
|
ChatCompletionChunk,
|
|
30
30
|
ModelType,
|
|
31
31
|
)
|
|
32
|
-
from camel.utils import
|
|
32
|
+
from camel.utils import (
|
|
33
|
+
BaseTokenCounter,
|
|
34
|
+
OpenAITokenCounter,
|
|
35
|
+
get_current_agent_session_id,
|
|
36
|
+
update_current_observation,
|
|
37
|
+
update_langfuse_trace,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if os.environ.get("LANGFUSE_ENABLED", "False").lower() == "true":
|
|
41
|
+
try:
|
|
42
|
+
from langfuse.decorators import observe
|
|
43
|
+
except ImportError:
|
|
44
|
+
from camel.utils import observe
|
|
45
|
+
else:
|
|
46
|
+
from camel.utils import observe
|
|
33
47
|
|
|
34
48
|
|
|
35
49
|
class SGLangModel(BaseModelBackend):
|
|
@@ -195,6 +209,7 @@ class SGLangModel(BaseModelBackend):
|
|
|
195
209
|
"input into SGLang model backend."
|
|
196
210
|
)
|
|
197
211
|
|
|
212
|
+
@observe(as_type='generation')
|
|
198
213
|
async def _arun(
|
|
199
214
|
self,
|
|
200
215
|
messages: List[OpenAIMessage],
|
|
@@ -213,6 +228,28 @@ class SGLangModel(BaseModelBackend):
|
|
|
213
228
|
`AsyncStream[ChatCompletionChunk]` in the stream mode.
|
|
214
229
|
"""
|
|
215
230
|
|
|
231
|
+
update_current_observation(
|
|
232
|
+
input={
|
|
233
|
+
"messages": messages,
|
|
234
|
+
"tools": tools,
|
|
235
|
+
},
|
|
236
|
+
model=str(self.model_type),
|
|
237
|
+
model_parameters=self.model_config_dict,
|
|
238
|
+
)
|
|
239
|
+
# Update Langfuse trace with current agent session and metadata
|
|
240
|
+
agent_session_id = get_current_agent_session_id()
|
|
241
|
+
if agent_session_id:
|
|
242
|
+
update_langfuse_trace(
|
|
243
|
+
session_id=agent_session_id,
|
|
244
|
+
metadata={
|
|
245
|
+
"source": "camel",
|
|
246
|
+
"agent_id": agent_session_id,
|
|
247
|
+
"agent_type": "camel_chat_agent",
|
|
248
|
+
"model_type": str(self.model_type),
|
|
249
|
+
},
|
|
250
|
+
tags=["CAMEL-AI", str(self.model_type)],
|
|
251
|
+
)
|
|
252
|
+
|
|
216
253
|
# Ensure server is running
|
|
217
254
|
self._ensure_server_running()
|
|
218
255
|
|
|
@@ -230,9 +267,16 @@ class SGLangModel(BaseModelBackend):
|
|
|
230
267
|
model=self.model_type,
|
|
231
268
|
**self.model_config_dict,
|
|
232
269
|
)
|
|
233
|
-
|
|
270
|
+
update_current_observation(
|
|
271
|
+
usage_details={
|
|
272
|
+
"prompt_tokens": response.usage.prompt_tokens,
|
|
273
|
+
"completion_tokens": response.usage.completion_tokens,
|
|
274
|
+
"total_tokens": response.usage.total_tokens,
|
|
275
|
+
},
|
|
276
|
+
)
|
|
234
277
|
return response
|
|
235
278
|
|
|
279
|
+
@observe(as_type='generation')
|
|
236
280
|
def _run(
|
|
237
281
|
self,
|
|
238
282
|
messages: List[OpenAIMessage],
|
|
@@ -250,6 +294,27 @@ class SGLangModel(BaseModelBackend):
|
|
|
250
294
|
`ChatCompletion` in the non-stream mode, or
|
|
251
295
|
`Stream[ChatCompletionChunk]` in the stream mode.
|
|
252
296
|
"""
|
|
297
|
+
update_current_observation(
|
|
298
|
+
input={
|
|
299
|
+
"messages": messages,
|
|
300
|
+
"tools": tools,
|
|
301
|
+
},
|
|
302
|
+
model=str(self.model_type),
|
|
303
|
+
model_parameters=self.model_config_dict,
|
|
304
|
+
)
|
|
305
|
+
# Update Langfuse trace with current agent session and metadata
|
|
306
|
+
agent_session_id = get_current_agent_session_id()
|
|
307
|
+
if agent_session_id:
|
|
308
|
+
update_langfuse_trace(
|
|
309
|
+
session_id=agent_session_id,
|
|
310
|
+
metadata={
|
|
311
|
+
"source": "camel",
|
|
312
|
+
"agent_id": agent_session_id,
|
|
313
|
+
"agent_type": "camel_chat_agent",
|
|
314
|
+
"model_type": str(self.model_type),
|
|
315
|
+
},
|
|
316
|
+
tags=["CAMEL-AI", str(self.model_type)],
|
|
317
|
+
)
|
|
253
318
|
|
|
254
319
|
# Ensure server is running
|
|
255
320
|
self._ensure_server_running()
|
|
@@ -268,6 +333,13 @@ class SGLangModel(BaseModelBackend):
|
|
|
268
333
|
model=self.model_type,
|
|
269
334
|
**self.model_config_dict,
|
|
270
335
|
)
|
|
336
|
+
update_current_observation(
|
|
337
|
+
usage_details={
|
|
338
|
+
"prompt_tokens": response.usage.prompt_tokens,
|
|
339
|
+
"completion_tokens": response.usage.completion_tokens,
|
|
340
|
+
"total_tokens": response.usage.total_tokens,
|
|
341
|
+
},
|
|
342
|
+
)
|
|
271
343
|
|
|
272
344
|
return response
|
|
273
345
|
|
camel/models/watsonx_model.py
CHANGED
|
@@ -26,8 +26,19 @@ from camel.utils import (
|
|
|
26
26
|
BaseTokenCounter,
|
|
27
27
|
OpenAITokenCounter,
|
|
28
28
|
api_keys_required,
|
|
29
|
+
get_current_agent_session_id,
|
|
30
|
+
update_current_observation,
|
|
31
|
+
update_langfuse_trace,
|
|
29
32
|
)
|
|
30
33
|
|
|
34
|
+
if os.environ.get("LANGFUSE_ENABLED", "False").lower() == "true":
|
|
35
|
+
try:
|
|
36
|
+
from langfuse.decorators import observe
|
|
37
|
+
except ImportError:
|
|
38
|
+
from camel.utils import observe
|
|
39
|
+
else:
|
|
40
|
+
from camel.utils import observe
|
|
41
|
+
|
|
31
42
|
logger = get_logger(__name__)
|
|
32
43
|
|
|
33
44
|
|
|
@@ -151,6 +162,7 @@ class WatsonXModel(BaseModelBackend):
|
|
|
151
162
|
|
|
152
163
|
return request_config
|
|
153
164
|
|
|
165
|
+
@observe(as_type='generation')
|
|
154
166
|
def _run(
|
|
155
167
|
self,
|
|
156
168
|
messages: List[OpenAIMessage],
|
|
@@ -170,6 +182,27 @@ class WatsonXModel(BaseModelBackend):
|
|
|
170
182
|
Returns:
|
|
171
183
|
ChatCompletion.
|
|
172
184
|
"""
|
|
185
|
+
update_current_observation(
|
|
186
|
+
input={
|
|
187
|
+
"messages": messages,
|
|
188
|
+
"tools": tools,
|
|
189
|
+
},
|
|
190
|
+
model=str(self.model_type),
|
|
191
|
+
model_parameters=self.model_config_dict,
|
|
192
|
+
)
|
|
193
|
+
# Update Langfuse trace with current agent session and metadata
|
|
194
|
+
agent_session_id = get_current_agent_session_id()
|
|
195
|
+
if agent_session_id:
|
|
196
|
+
update_langfuse_trace(
|
|
197
|
+
session_id=agent_session_id,
|
|
198
|
+
metadata={
|
|
199
|
+
"source": "camel",
|
|
200
|
+
"agent_id": agent_session_id,
|
|
201
|
+
"agent_type": "camel_chat_agent",
|
|
202
|
+
"model_type": str(self.model_type),
|
|
203
|
+
},
|
|
204
|
+
tags=["CAMEL-AI", str(self.model_type)],
|
|
205
|
+
)
|
|
173
206
|
try:
|
|
174
207
|
request_config = self._prepare_request(
|
|
175
208
|
messages, response_format, tools
|
|
@@ -183,12 +216,16 @@ class WatsonXModel(BaseModelBackend):
|
|
|
183
216
|
)
|
|
184
217
|
|
|
185
218
|
openai_response = self._to_openai_response(response)
|
|
219
|
+
update_current_observation(
|
|
220
|
+
usage=openai_response.usage,
|
|
221
|
+
)
|
|
186
222
|
return openai_response
|
|
187
223
|
|
|
188
224
|
except Exception as e:
|
|
189
225
|
logger.error(f"Unexpected error when calling WatsonX API: {e!s}")
|
|
190
226
|
raise
|
|
191
227
|
|
|
228
|
+
@observe(as_type='generation')
|
|
192
229
|
async def _arun(
|
|
193
230
|
self,
|
|
194
231
|
messages: List[OpenAIMessage],
|
|
@@ -208,6 +245,28 @@ class WatsonXModel(BaseModelBackend):
|
|
|
208
245
|
Returns:
|
|
209
246
|
ChatCompletion.
|
|
210
247
|
"""
|
|
248
|
+
update_current_observation(
|
|
249
|
+
input={
|
|
250
|
+
"messages": messages,
|
|
251
|
+
"tools": tools,
|
|
252
|
+
},
|
|
253
|
+
model=str(self.model_type),
|
|
254
|
+
model_parameters=self.model_config_dict,
|
|
255
|
+
)
|
|
256
|
+
# Update Langfuse trace with current agent session and metadata
|
|
257
|
+
agent_session_id = get_current_agent_session_id()
|
|
258
|
+
if agent_session_id:
|
|
259
|
+
update_langfuse_trace(
|
|
260
|
+
session_id=agent_session_id,
|
|
261
|
+
metadata={
|
|
262
|
+
"source": "camel",
|
|
263
|
+
"agent_id": agent_session_id,
|
|
264
|
+
"agent_type": "camel_chat_agent",
|
|
265
|
+
"model_type": str(self.model_type),
|
|
266
|
+
},
|
|
267
|
+
tags=["CAMEL-AI", str(self.model_type)],
|
|
268
|
+
)
|
|
269
|
+
|
|
211
270
|
try:
|
|
212
271
|
request_config = self._prepare_request(
|
|
213
272
|
messages, response_format, tools
|
|
@@ -221,6 +280,9 @@ class WatsonXModel(BaseModelBackend):
|
|
|
221
280
|
)
|
|
222
281
|
|
|
223
282
|
openai_response = self._to_openai_response(response)
|
|
283
|
+
update_current_observation(
|
|
284
|
+
usage=openai_response.usage,
|
|
285
|
+
)
|
|
224
286
|
return openai_response
|
|
225
287
|
|
|
226
288
|
except Exception as e:
|
|
@@ -25,7 +25,7 @@ from camel.societies.workforce.prompts import (
|
|
|
25
25
|
ROLEPLAY_PROCESS_TASK_PROMPT,
|
|
26
26
|
ROLEPLAY_SUMMARIZE_PROMPT,
|
|
27
27
|
)
|
|
28
|
-
from camel.societies.workforce.utils import TaskResult
|
|
28
|
+
from camel.societies.workforce.utils import TaskResult, validate_task_content
|
|
29
29
|
from camel.societies.workforce.worker import Worker
|
|
30
30
|
from camel.tasks.task import Task, TaskState
|
|
31
31
|
from camel.utils import print_text_animated
|
|
@@ -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
|
|
@@ -182,6 +182,14 @@ class RolePlayingWorker(Worker):
|
|
|
182
182
|
)
|
|
183
183
|
result_dict = json.loads(response.msg.content)
|
|
184
184
|
task_result = TaskResult(**result_dict)
|
|
185
|
+
|
|
186
|
+
if not validate_task_content(task_result.content, task.id):
|
|
187
|
+
print(
|
|
188
|
+
f"{Fore.RED}Task {task.id}: Content validation failed - "
|
|
189
|
+
f"task marked as failed{Fore.RESET}"
|
|
190
|
+
)
|
|
191
|
+
return TaskState.FAILED
|
|
192
|
+
|
|
185
193
|
task.result = task_result.content
|
|
186
194
|
|
|
187
195
|
print(f"Task result: {task.result}\n")
|
|
@@ -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
|
|
|
@@ -20,7 +21,7 @@ from colorama import Fore
|
|
|
20
21
|
|
|
21
22
|
from camel.agents import ChatAgent
|
|
22
23
|
from camel.societies.workforce.prompts import PROCESS_TASK_PROMPT
|
|
23
|
-
from camel.societies.workforce.utils import TaskResult
|
|
24
|
+
from camel.societies.workforce.utils import TaskResult, validate_task_content
|
|
24
25
|
from camel.societies.workforce.worker import Worker
|
|
25
26
|
from camel.tasks.task import Task, TaskState
|
|
26
27
|
from camel.utils import print_text_animated
|
|
@@ -83,6 +84,28 @@ class SingleAgentWorker(Worker):
|
|
|
83
84
|
)
|
|
84
85
|
return TaskState.FAILED
|
|
85
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
|
+
|
|
86
109
|
print(f"======\n{Fore.GREEN}Reply from {self}:{Fore.RESET}")
|
|
87
110
|
|
|
88
111
|
result_dict = json.loads(response.msg.content)
|
|
@@ -97,5 +120,12 @@ class SingleAgentWorker(Worker):
|
|
|
97
120
|
if task_result.failed:
|
|
98
121
|
return TaskState.FAILED
|
|
99
122
|
|
|
123
|
+
if not validate_task_content(task_result.content, task.id):
|
|
124
|
+
print(
|
|
125
|
+
f"{Fore.RED}Task {task.id}: Content validation failed - "
|
|
126
|
+
f"task marked as failed{Fore.RESET}"
|
|
127
|
+
)
|
|
128
|
+
return TaskState.FAILED
|
|
129
|
+
|
|
100
130
|
task.result = task_result.content
|
|
101
131
|
return TaskState.DONE
|
|
@@ -16,6 +16,10 @@ from typing import Callable
|
|
|
16
16
|
|
|
17
17
|
from pydantic import BaseModel, Field
|
|
18
18
|
|
|
19
|
+
from camel.logger import get_logger
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
19
23
|
|
|
20
24
|
class WorkerConf(BaseModel):
|
|
21
25
|
r"""The configuration of a worker."""
|
|
@@ -71,3 +75,50 @@ def check_if_running(running: bool) -> Callable:
|
|
|
71
75
|
return wrapper
|
|
72
76
|
|
|
73
77
|
return decorator
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def validate_task_content(
|
|
81
|
+
content: str, task_id: str = "unknown", min_length: int = 10
|
|
82
|
+
) -> bool:
|
|
83
|
+
r"""Validates task result content to avoid silent failures.
|
|
84
|
+
It performs basic checks to ensure the content meets minimum
|
|
85
|
+
quality standards.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
content (str): The task result content to validate.
|
|
89
|
+
task_id (str): Task ID for logging purposes.
|
|
90
|
+
(default: :obj:`"unknown"`)
|
|
91
|
+
min_length (int): Minimum content length after stripping whitespace.
|
|
92
|
+
(default: :obj:`10`)
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
bool: True if content passes validation, False otherwise.
|
|
96
|
+
"""
|
|
97
|
+
# 1: Content must not be None
|
|
98
|
+
if content is None:
|
|
99
|
+
logger.warning(f"Task {task_id}: None content rejected")
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
# 2: Content must not be empty after stripping whitespace
|
|
103
|
+
stripped_content = content.strip()
|
|
104
|
+
if not stripped_content:
|
|
105
|
+
logger.warning(
|
|
106
|
+
f"Task {task_id}: Empty or whitespace-only content rejected."
|
|
107
|
+
)
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
# 3: Content must meet minimum meaningful length
|
|
111
|
+
if len(stripped_content) < min_length:
|
|
112
|
+
logger.warning(
|
|
113
|
+
f"Task {task_id}: Content too short ({len(stripped_content)} "
|
|
114
|
+
f"chars < {min_length} minimum). Content preview: "
|
|
115
|
+
f"'{stripped_content[:50]}...'"
|
|
116
|
+
)
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
# All validation checks passed
|
|
120
|
+
logger.debug(
|
|
121
|
+
f"Task {task_id}: Content validation passed "
|
|
122
|
+
f"({len(stripped_content)} chars)"
|
|
123
|
+
)
|
|
124
|
+
return True
|