camel-ai 0.2.64__py3-none-any.whl → 0.2.66__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

@@ -0,0 +1,465 @@
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
+ import argparse
15
+ import json
16
+ import logging
17
+ import os
18
+ import sys # Import the sys module
19
+ from logging.handlers import RotatingFileHandler
20
+ from typing import Any, Dict, List
21
+
22
+ app = None
23
+ products: List[Dict[str, Any]] = []
24
+ products_by_id: Dict[int, Dict[str, Any]] = {}
25
+ cart: List[Dict[str, Any]] = []
26
+ ACTION_COUNT: int = 0
27
+
28
+
29
+ def load_products(file_path: str = 'products.json') -> None:
30
+ global products, products_by_id
31
+ try:
32
+ # The products.json is expected to be
33
+ # in the same directory as this app.py
34
+ # or given by mock_web.py
35
+ script_dir = os.path.dirname(__file__)
36
+ abs_file_path = os.path.join(script_dir, file_path)
37
+ with open(abs_file_path, 'r') as f:
38
+ products = json.load(f)
39
+ products_by_id = {product['id']: product for product in products}
40
+ except FileNotFoundError:
41
+ sys.stderr.write(f"Error: {file_path} not found.\n")
42
+ products = []
43
+ products_by_id = {}
44
+ except json.JSONDecodeError:
45
+ sys.stderr.write(f"Error: Could not decode JSON from {file_path}.\n")
46
+ products = []
47
+ products_by_id = {}
48
+
49
+
50
+ # --- Logging Setup ---
51
+ def setup_logging(application):
52
+ log_formatter = logging.Formatter(
53
+ '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
54
+ )
55
+ # Log to a file in the same directory as this script
56
+ script_dir = os.path.dirname(__file__)
57
+ log_file = os.path.join(script_dir, 'app.log')
58
+
59
+ # File Handler
60
+ file_handler = RotatingFileHandler(
61
+ log_file, maxBytes=1024 * 1024 * 10, backupCount=5
62
+ ) # 10MB per file, 5 backups
63
+ file_handler.setFormatter(log_formatter)
64
+ file_handler.setLevel(logging.INFO)
65
+
66
+ # Stream Handler (for console output)
67
+ stream_handler = logging.StreamHandler(
68
+ sys.stdout
69
+ ) # Explicitly set to stdout
70
+ stream_handler.setFormatter(log_formatter)
71
+ stream_handler.setLevel(logging.INFO)
72
+
73
+ application.logger.addHandler(file_handler)
74
+ application.logger.addHandler(stream_handler)
75
+ application.logger.setLevel(logging.INFO)
76
+ application.logger.info(
77
+ f"Logging setup complete. Logs will be saved to {log_file}"
78
+ )
79
+
80
+
81
+ # --- End Logging Setup ---
82
+
83
+
84
+ # --- Task Mode Helper ---
85
+ def check_task_completion(current_cart_raw, ground_truth_spec):
86
+ if not ground_truth_spec: # No ground truth defined
87
+ return False
88
+
89
+ # Group current cart by product ID and count quantity
90
+ current_cart_grouped = {}
91
+ for item in current_cart_raw:
92
+ pid = item['id']
93
+ current_cart_grouped[pid] = current_cart_grouped.get(pid, 0) + 1
94
+
95
+ # Convert ground truth spec to a comparable dictionary {product_id:
96
+ # quantity}
97
+ ground_truth_dict = {}
98
+ for item_spec in ground_truth_spec:
99
+ ground_truth_dict[item_spec['id']] = item_spec['quantity']
100
+
101
+ # Check if current cart exactly matches ground truth
102
+ # 1. Same number of unique product types
103
+ if len(current_cart_grouped) != len(ground_truth_dict):
104
+ return False
105
+
106
+ # 2. Each product in current cart matches ground truth quantity, and all
107
+ # ground truth items are present
108
+ for pid, qty_spec in ground_truth_dict.items():
109
+ if (
110
+ pid not in current_cart_grouped
111
+ or current_cart_grouped[pid] != qty_spec
112
+ ):
113
+ return False
114
+
115
+ # Ensure no extra items in current_cart_grouped that are not in
116
+ # ground_truth_dict (already covered by length check if all ground truth
117
+ # items are found)
118
+ # For robustness, explicitly check this too:
119
+ for pid_current in current_cart_grouped.keys():
120
+ if pid_current not in ground_truth_dict:
121
+ return False
122
+
123
+ return True
124
+
125
+
126
+ # --- End Task Mode Helper ---
127
+
128
+
129
+ # --- Task Completion Helper ---
130
+ def _trigger_task_completion_check():
131
+ r"""Checks for task completion if in task mode and not already signaled."""
132
+ # Uses global `app` and `cart`
133
+ if app.config.get('TASK_ACTIVE') and not app.config.get(
134
+ 'TASK_COMPLETION_SIGNALED'
135
+ ):
136
+ if check_task_completion(cart, app.config.get('GROUND_TRUTH_CART')):
137
+ app.config['TASK_COMPLETION_SIGNALED'] = True
138
+ app.logger.info(
139
+ "TASK COMPLETED: Ground truth cart state achieved."
140
+ )
141
+
142
+
143
+ # --- End Task Completion Helper ---
144
+ def create_app():
145
+ global app
146
+ try:
147
+ from flask import Flask, jsonify, render_template, request
148
+ except ImportError:
149
+ raise ImportError(
150
+ "Flask not installed. Please install it with `pip install Flask`"
151
+ )
152
+
153
+ # Adjust template and static folder paths
154
+ script_dir = os.path.dirname(os.path.abspath(__file__))
155
+ app = Flask(
156
+ __name__,
157
+ template_folder=os.path.join(script_dir, 'templates'),
158
+ static_folder=os.path.join(script_dir, 'static'),
159
+ )
160
+ setup_logging(app)
161
+
162
+ # --- Global Task Config ---
163
+ app.config['TASK_ACTIVE'] = False
164
+ app.config['GROUND_TRUTH_CART'] = []
165
+ app.config['TASK_COMPLETION_SIGNALED'] = False
166
+
167
+ @app.route('/')
168
+ def home():
169
+ global ACTION_COUNT
170
+ ACTION_COUNT += 1
171
+ unique_categories = sorted({p["category"] for p in products})
172
+ products_by_category = {}
173
+ for product in products:
174
+ category = product["category"]
175
+ if category not in products_by_category:
176
+ products_by_category[category] = []
177
+ products_by_category[category].append(product)
178
+
179
+ app.logger.info(
180
+ "Home page requested. "
181
+ f"Categories: {unique_categories}. Cart count: {len(cart)}"
182
+ )
183
+ # Pass products_by_category to the template instead of the flat
184
+ # products list for main display
185
+ return render_template(
186
+ 'index.html',
187
+ products_by_category=products_by_category,
188
+ categories=unique_categories,
189
+ cart_count=len(cart),
190
+ all_products=products,
191
+ )
192
+
193
+ @app.route('/product/<int:product_id>')
194
+ def product_detail(product_id):
195
+ global ACTION_COUNT
196
+ ACTION_COUNT += 1
197
+ product = products_by_id.get(product_id)
198
+ app.logger.info(
199
+ f"Product detail page requested for ID: {product_id}. "
200
+ f"Cart count: {len(cart)}"
201
+ )
202
+ if product:
203
+ return render_template(
204
+ 'product-detail.html', product=product, cart_count=len(cart)
205
+ )
206
+ return "Product not found", 404
207
+
208
+ @app.route('/cart')
209
+ def view_cart():
210
+ global ACTION_COUNT
211
+ ACTION_COUNT += 1
212
+ total_price = sum(
213
+ item['price'] * item.get('quantity', 1) for item in cart
214
+ )
215
+ app.logger.info(f"Cart page requested. Cart count: {len(cart)}")
216
+ return render_template(
217
+ 'cart.html', cart_items=cart, total_price=total_price
218
+ )
219
+
220
+ @app.route('/api/products')
221
+ def get_products():
222
+ return jsonify(products)
223
+
224
+ @app.route('/api/cart', methods=['GET'])
225
+ def get_cart_api():
226
+ total_price = sum(
227
+ item['price'] * item.get('quantity', 1) for item in cart
228
+ )
229
+ return jsonify({'cart': cart, 'total_price': total_price})
230
+
231
+ @app.route('/api/cart/add', methods=['POST'])
232
+ def add_to_cart_api():
233
+ global ACTION_COUNT
234
+ data = request.json
235
+ product_id = data.get('productId')
236
+
237
+ if not product_id:
238
+ return jsonify(
239
+ {'success': False, 'message': 'Product ID is required'}
240
+ ), 400
241
+
242
+ product = products_by_id.get(product_id)
243
+ if not product:
244
+ return jsonify(
245
+ {'success': False, 'message': 'Product not found'}
246
+ ), 404
247
+
248
+ # Check if product is already in cart
249
+ for item in cart:
250
+ if item['id'] == product_id:
251
+ # If yes, just increment quantity
252
+ item.get('quantity', 1)
253
+ item['quantity'] = item.get('quantity', 1) + 1
254
+ app.logger.info(
255
+ f"Incremented quantity for product {product_id} in cart."
256
+ )
257
+ return jsonify(
258
+ {'success': True, 'cart': cart, 'cart_count': len(cart)}
259
+ )
260
+
261
+ # If not, add new item to cart
262
+ cart_item = product.copy()
263
+ cart_item['quantity'] = 1 # Start with quantity 1
264
+ cart.append(cart_item)
265
+ ACTION_COUNT += 1 # Increment on successful add
266
+
267
+ _trigger_task_completion_check()
268
+
269
+ app.logger.info(f"Added product {product_id} to cart.")
270
+ return jsonify(
271
+ {'success': True, 'cart': cart, 'cart_count': len(cart)}
272
+ )
273
+
274
+ @app.route('/api/cart/update', methods=['POST'])
275
+ def update_cart_item_api():
276
+ data = request.json
277
+ product_id = data.get('productId')
278
+ quantity = data.get('quantity')
279
+
280
+ if not product_id:
281
+ return jsonify(
282
+ {'success': False, 'message': 'Product ID is required'}
283
+ ), 400
284
+
285
+ try:
286
+ # Ensure quantity is a non-negative integer
287
+ quantity = int(quantity)
288
+ if quantity < 0:
289
+ raise ValueError
290
+ except (ValueError, TypeError):
291
+ return jsonify(
292
+ {'success': False, 'message': 'Invalid quantity'}
293
+ ), 400
294
+
295
+ found_item = False
296
+ for item in cart:
297
+ if item['id'] == product_id:
298
+ if quantity > 0:
299
+ item['quantity'] = quantity
300
+ app.logger.info(
301
+ "Updated quantity for product "
302
+ f"{product_id} to {quantity}."
303
+ )
304
+ else:
305
+ # If quantity is 0, remove the item
306
+ cart.remove(item)
307
+ app.logger.info(f"Removed product {product_id} from cart.")
308
+ found_item = True
309
+ break
310
+
311
+ if not found_item:
312
+ return jsonify(
313
+ {'success': False, 'message': 'Product not in cart'}
314
+ ), 404
315
+
316
+ _trigger_task_completion_check()
317
+
318
+ total_price = sum(
319
+ item['price'] * item.get('quantity', 1) for item in cart
320
+ )
321
+ return jsonify(
322
+ {
323
+ 'success': True,
324
+ 'cart': cart,
325
+ 'cart_count': len(cart),
326
+ 'total_price': total_price,
327
+ }
328
+ )
329
+
330
+ @app.route('/api/cart/remove', methods=['POST'])
331
+ def remove_from_cart_api():
332
+ global cart, ACTION_COUNT
333
+ data = request.json
334
+ product_id = data.get('productId')
335
+
336
+ if not product_id:
337
+ return jsonify(
338
+ {'success': False, 'message': 'Product ID is required'}
339
+ ), 400
340
+
341
+ original_cart_len = len(cart)
342
+ # List comprehension to create a new list excluding the item to be
343
+ # removed This is simpler than finding index and deleting
344
+ cart[:] = [item for item in cart if item['id'] != product_id]
345
+
346
+ if len(cart) < original_cart_len:
347
+ ACTION_COUNT += 1 # Increment on successful remove
348
+ app.logger.info(f"Removed product {product_id} from cart.")
349
+
350
+ _trigger_task_completion_check()
351
+
352
+ total_price = sum(
353
+ item['price'] * item.get('quantity', 1) for item in cart
354
+ )
355
+ return jsonify(
356
+ {
357
+ 'success': True,
358
+ 'cart': cart,
359
+ 'cart_count': len(cart),
360
+ 'total_price': total_price,
361
+ }
362
+ )
363
+
364
+ return jsonify(
365
+ {'success': False, 'message': 'Product not in cart'}
366
+ ), 404
367
+
368
+ @app.route('/task/start', methods=['POST'])
369
+ def start_task():
370
+ global cart, ACTION_COUNT
371
+ ACTION_COUNT = 0 # Reset action counter
372
+ data = request.json
373
+ ground_truth = data.get('ground_truth_cart')
374
+
375
+ if not isinstance(ground_truth, list):
376
+ return jsonify(
377
+ {
378
+ 'success': False,
379
+ 'message': '`ground_truth_cart` must be a list.',
380
+ }
381
+ ), 400
382
+
383
+ # Validate ground truth spec
384
+ for item_spec in ground_truth:
385
+ if not all(k in item_spec for k in ['id', 'quantity']):
386
+ return jsonify(
387
+ {
388
+ 'success': False,
389
+ 'message': (
390
+ 'Each item in `ground_truth_cart` must have '
391
+ '`id` and `quantity`.'
392
+ ),
393
+ }
394
+ ), 400
395
+
396
+ app.config['TASK_ACTIVE'] = True
397
+ app.config['GROUND_TRUTH_CART'] = ground_truth
398
+ app.config['TASK_COMPLETION_SIGNALED'] = False # Reset signal
399
+ cart = [] # Reset cart
400
+ app.logger.info(f"TASK MODE STARTED. Ground truth: {ground_truth}")
401
+ return jsonify(
402
+ {'success': True, 'message': 'Task mode started. Cart reset.'}
403
+ )
404
+
405
+ @app.route('/task/check', methods=['GET'])
406
+ def check_task():
407
+ global ACTION_COUNT
408
+ if not app.config.get('TASK_ACTIVE'):
409
+ return jsonify(
410
+ {'success': False, 'message': 'Task mode is not active.'}
411
+ ), 400
412
+
413
+ completed = app.config.get('TASK_COMPLETION_SIGNALED', False)
414
+ if completed:
415
+ message = "Task completed successfully."
416
+ else:
417
+ message = "Task not yet completed."
418
+
419
+ return jsonify(
420
+ {
421
+ 'success': True,
422
+ 'completed': completed,
423
+ 'steps': ACTION_COUNT,
424
+ 'message': message,
425
+ }
426
+ )
427
+
428
+ @app.route('/task/stop', methods=['POST'])
429
+ def stop_task():
430
+ global cart
431
+ app.config['TASK_ACTIVE'] = False
432
+ app.config['GROUND_TRUTH_CART'] = []
433
+ app.config['TASK_COMPLETION_SIGNALED'] = False
434
+ cart = [] # Reset cart
435
+ app.logger.info("TASK MODE STOPPED. Cart reset.")
436
+ return jsonify(
437
+ {'success': True, 'message': 'Task mode stopped. Cart reset.'}
438
+ )
439
+
440
+ return app
441
+
442
+
443
+ def main():
444
+ parser = argparse.ArgumentParser(
445
+ description="Run the mock website server."
446
+ )
447
+ parser.add_argument(
448
+ '--port', type=int, default=5000, help='Port to run the server on.'
449
+ )
450
+ args = parser.parse_args()
451
+
452
+ # Load products specific to this app
453
+ load_products()
454
+ flask_app = create_app()
455
+
456
+ if flask_app:
457
+ # Use 0.0.0.0 to make it accessible from the dispatcher
458
+ # Disable the reloader to prevent state loss on logging
459
+ flask_app.run(
460
+ debug=True, port=args.port, host='0.0.0.0', use_reloader=False
461
+ )
462
+
463
+
464
+ if __name__ == "__main__":
465
+ main()
@@ -0,0 +1,104 @@
1
+ {
2
+ "products": [
3
+ {
4
+ "id": 1,
5
+ "name": "Gaming Laptop",
6
+ "price": 1200,
7
+ "image": "assets/img/products/laptop.jpg",
8
+ "category": "Electronics",
9
+ "rating": 4.5,
10
+ "description": "High-performance gaming laptop with latest specs."
11
+ },
12
+ {
13
+ "id": 2,
14
+ "name": "Wireless Mouse",
15
+ "price": 25,
16
+ "image": "assets/img/products/mouse.jpg",
17
+ "category": "Electronics",
18
+ "rating": 4.0,
19
+ "description": "Ergonomic wireless mouse with long battery life."
20
+ },
21
+ {
22
+ "id": 3,
23
+ "name": "Mechanical Keyboard",
24
+ "price": 75,
25
+ "image": "assets/img/products/keyboard.jpg",
26
+ "category": "Electronics",
27
+ "rating": 4.8,
28
+ "description": "RGB mechanical keyboard with customizable keys."
29
+ },
30
+ {
31
+ "id": 4,
32
+ "name": "Coffee Maker",
33
+ "price": 50,
34
+ "image": "assets/img/products/coffeemaker.jpg",
35
+ "category": "Home Goods",
36
+ "rating": 3.9,
37
+ "description": "Drip coffee maker with programmable timer."
38
+ },
39
+ {
40
+ "id": 5,
41
+ "name": "Bluetooth Speaker",
42
+ "price": 45,
43
+ "image": "assets/img/products/speaker.jpg",
44
+ "category": "Electronics",
45
+ "rating": 4.2,
46
+ "description": "Portable Bluetooth speaker with rich sound."
47
+ },
48
+ {
49
+ "id": 6,
50
+ "name": "Smartphone",
51
+ "price": 699,
52
+ "image": "assets/img/products/smartphone.jpg",
53
+ "category": "Electronics",
54
+ "rating": 4.6,
55
+ "description": "Latest-gen smartphone with stunning display and fast performance."
56
+ },
57
+ {
58
+ "id": 7,
59
+ "name": "Air Purifier",
60
+ "price": 130,
61
+ "image": "assets/img/products/airpurifier.jpg",
62
+ "category": "Home Goods",
63
+ "rating": 4.3,
64
+ "description": "HEPA air purifier with quiet operation and multiple fan speeds."
65
+ },
66
+ {
67
+ "id": 8,
68
+ "name": "Fitness Tracker",
69
+ "price": 85,
70
+ "image": "assets/img/products/fitnesstracker.jpg",
71
+ "category": "Electronics",
72
+ "rating": 4.1,
73
+ "description": "Waterproof fitness tracker with heart rate and sleep monitoring."
74
+ },
75
+ {
76
+ "id": 9,
77
+ "name": "Electric Kettle",
78
+ "price": 35,
79
+ "image": "assets/img/products/kettle.jpg",
80
+ "category": "Home Goods",
81
+ "rating": 4.0,
82
+ "description": "Stainless steel electric kettle with auto shut-off feature."
83
+ },
84
+ {
85
+ "id": 10,
86
+ "name": "Noise Cancelling Headphones",
87
+ "price": 150,
88
+ "image": "assets/img/products/headphones.jpg",
89
+ "category": "Electronics",
90
+ "rating": 4.7,
91
+ "description": "Over-ear headphones with active noise cancellation and long battery life."
92
+ }
93
+ ],
94
+ "ground_truth_cart": [
95
+ {
96
+ "id": 1,
97
+ "quantity": 1
98
+ },
99
+ {
100
+ "id": 2,
101
+ "quantity": 1
102
+ }
103
+ ]
104
+ }
camel/datasets/models.py CHANGED
@@ -46,7 +46,7 @@ class DataPoint(BaseModel):
46
46
  Returns:
47
47
  Dict[str, Any]: Dictionary representation of the DataPoint.
48
48
  """
49
- return self.dict()
49
+ return self.model_dump()
50
50
 
51
51
  @classmethod
52
52
  def from_dict(cls, data: Dict[str, Any]) -> 'DataPoint':
@@ -13,6 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  import json
15
15
  import random
16
+ from collections.abc import Sequence
16
17
  from pathlib import Path
17
18
  from typing import (
18
19
  Any,
@@ -288,6 +289,11 @@ class StaticDataset(Dataset):
288
289
  )
289
290
  raw_data = []
290
291
 
292
+ if not isinstance(data, Sequence):
293
+ raise TypeError(
294
+ f"{type(data).__name__} does not support indexing."
295
+ )
296
+
291
297
  for i in range(len(data)):
292
298
  item = data[i]
293
299
  if not isinstance(item, dict):
@@ -142,6 +142,7 @@ class OpenAIModel(BaseModelBackend):
142
142
  ModelType.O3_MINI,
143
143
  ModelType.O3,
144
144
  ModelType.O4_MINI,
145
+ ModelType.O3_PRO,
145
146
  ]:
146
147
  warnings.warn(
147
148
  "Warning: You are using an reasoning model (O series), "
@@ -27,7 +27,7 @@ from camel.societies.workforce.prompts import (
27
27
  )
28
28
  from camel.societies.workforce.utils import TaskResult
29
29
  from camel.societies.workforce.worker import Worker
30
- from camel.tasks.task import Task, TaskState
30
+ from camel.tasks.task import Task, TaskState, validate_task_content
31
31
  from camel.utils import print_text_animated
32
32
 
33
33
 
@@ -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")
@@ -23,7 +23,7 @@ from camel.agents import ChatAgent
23
23
  from camel.societies.workforce.prompts import PROCESS_TASK_PROMPT
24
24
  from camel.societies.workforce.utils import TaskResult
25
25
  from camel.societies.workforce.worker import Worker
26
- from camel.tasks.task import Task, TaskState
26
+ from camel.tasks.task import Task, TaskState, validate_task_content
27
27
  from camel.utils import print_text_animated
28
28
 
29
29
 
@@ -120,5 +120,12 @@ class SingleAgentWorker(Worker):
120
120
  if task_result.failed:
121
121
  return TaskState.FAILED
122
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
+
123
130
  task.result = task_result.content
124
131
  return TaskState.DONE
@@ -40,7 +40,7 @@ from camel.societies.workforce.utils import (
40
40
  check_if_running,
41
41
  )
42
42
  from camel.societies.workforce.worker import Worker
43
- from camel.tasks.task import Task, TaskState
43
+ from camel.tasks.task import Task, TaskState, validate_task_content
44
44
  from camel.toolkits import CodeExecutionToolkit, SearchToolkit, ThinkingToolkit
45
45
  from camel.types import ModelPlatformType, ModelType
46
46
  from camel.utils import dependencies_required
@@ -211,6 +211,15 @@ class Workforce(BaseNode):
211
211
  Returns:
212
212
  Task: The updated task.
213
213
  """
214
+ if not validate_task_content(task.content, task.id):
215
+ task.state = TaskState.FAILED
216
+ task.result = "Task failed: Invalid or empty content provided"
217
+ logger.warning(
218
+ f"Task {task.id} rejected: Invalid or empty content. "
219
+ f"Content preview: '{task.content[:50]}...'"
220
+ )
221
+ return task
222
+
214
223
  self.reset()
215
224
  self._task = task
216
225
  task.state = TaskState.FAILED
@@ -681,8 +690,8 @@ class Workforce(BaseNode):
681
690
  task_id (str, optional): Unique identifier for the task. If
682
691
  None, a UUID will be automatically generated.
683
692
  (default: :obj:`None`)
684
- additional_info (str, optional): Additional information or
685
- context for the task. (default: :obj:`None`)
693
+ additional_info (Optional[Dict[str, Any]]): Additional
694
+ information or context for the task. (default: :obj:`None`)
686
695
 
687
696
  Returns:
688
697
  Dict[str, Any]: A dictionary containing the processing result
@@ -701,7 +710,7 @@ class Workforce(BaseNode):
701
710
  task = Task(
702
711
  content=task_content,
703
712
  id=task_id or str(uuid.uuid4()),
704
- additional_info=additional_info or "",
713
+ additional_info=additional_info,
705
714
  )
706
715
 
707
716
  try: