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

Files changed (36) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +1533 -135
  3. camel/agents/repo_agent.py +2 -1
  4. camel/benchmarks/browsecomp.py +6 -6
  5. camel/logger.py +1 -1
  6. camel/messages/base.py +12 -1
  7. camel/models/azure_openai_model.py +96 -7
  8. camel/models/base_model.py +68 -10
  9. camel/models/deepseek_model.py +5 -0
  10. camel/models/gemini_model.py +5 -0
  11. camel/models/litellm_model.py +48 -16
  12. camel/models/model_manager.py +24 -6
  13. camel/models/openai_compatible_model.py +109 -5
  14. camel/models/openai_model.py +117 -8
  15. camel/societies/workforce/prompts.py +68 -5
  16. camel/societies/workforce/role_playing_worker.py +65 -7
  17. camel/societies/workforce/single_agent_worker.py +72 -18
  18. camel/societies/workforce/structured_output_handler.py +500 -0
  19. camel/societies/workforce/utils.py +67 -2
  20. camel/societies/workforce/workforce.py +527 -114
  21. camel/societies/workforce/workforce_logger.py +0 -8
  22. camel/tasks/task.py +3 -1
  23. camel/toolkits/__init__.py +2 -0
  24. camel/toolkits/file_write_toolkit.py +526 -121
  25. camel/toolkits/hybrid_browser_toolkit/actions.py +235 -60
  26. camel/toolkits/hybrid_browser_toolkit/agent.py +25 -8
  27. camel/toolkits/hybrid_browser_toolkit/browser_session.py +574 -164
  28. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +996 -126
  29. camel/toolkits/hybrid_browser_toolkit/stealth_config.py +116 -0
  30. camel/toolkits/hybrid_browser_toolkit/stealth_script.js +0 -0
  31. camel/toolkits/message_agent_toolkit.py +608 -0
  32. camel/toolkits/note_taking_toolkit.py +7 -13
  33. {camel_ai-0.2.71a4.dist-info → camel_ai-0.2.71a6.dist-info}/METADATA +6 -4
  34. {camel_ai-0.2.71a4.dist-info → camel_ai-0.2.71a6.dist-info}/RECORD +36 -32
  35. {camel_ai-0.2.71a4.dist-info → camel_ai-0.2.71a6.dist-info}/WHEEL +0 -0
  36. {camel_ai-0.2.71a4.dist-info → camel_ai-0.2.71a6.dist-info}/licenses/LICENSE +0 -0
@@ -15,7 +15,6 @@ from __future__ import annotations
15
15
 
16
16
  import asyncio
17
17
  import datetime
18
- import json
19
18
  import time
20
19
  from collections import deque
21
20
  from typing import Any, List, Optional
@@ -24,6 +23,9 @@ from colorama import Fore
24
23
 
25
24
  from camel.agents import ChatAgent
26
25
  from camel.societies.workforce.prompts import PROCESS_TASK_PROMPT
26
+ from camel.societies.workforce.structured_output_handler import (
27
+ StructuredOutputHandler,
28
+ )
27
29
  from camel.societies.workforce.utils import TaskResult
28
30
  from camel.societies.workforce.worker import Worker
29
31
  from camel.tasks.task import Task, TaskState, is_task_result_insufficient
@@ -186,6 +188,14 @@ class SingleAgentWorker(Worker):
186
188
  (default: :obj:`10`)
187
189
  auto_scale_pool (bool): Whether to auto-scale the agent pool.
188
190
  (default: :obj:`True`)
191
+ use_structured_output_handler (bool, optional): Whether to use the
192
+ structured output handler instead of native structured output.
193
+ When enabled, the workforce will use prompts with structured
194
+ output instructions and regex extraction to parse responses.
195
+ This ensures compatibility with agents that don't reliably
196
+ support native structured output. When disabled, the workforce
197
+ uses the native response_format parameter.
198
+ (default: :obj:`True`)
189
199
  """
190
200
 
191
201
  def __init__(
@@ -196,12 +206,19 @@ class SingleAgentWorker(Worker):
196
206
  pool_initial_size: int = 1,
197
207
  pool_max_size: int = 10,
198
208
  auto_scale_pool: bool = True,
209
+ use_structured_output_handler: bool = True,
199
210
  ) -> None:
200
211
  node_id = worker.agent_id
201
212
  super().__init__(
202
213
  description,
203
214
  node_id=node_id,
204
215
  )
216
+ self.use_structured_output_handler = use_structured_output_handler
217
+ self.structured_handler = (
218
+ StructuredOutputHandler()
219
+ if use_structured_output_handler
220
+ else None
221
+ )
205
222
  self.worker = worker
206
223
  self.use_agent_pool = use_agent_pool
207
224
 
@@ -273,13 +290,48 @@ class SingleAgentWorker(Worker):
273
290
  dependency_tasks_info = self._get_dep_tasks_info(dependencies)
274
291
  prompt = PROCESS_TASK_PROMPT.format(
275
292
  content=task.content,
293
+ parent_task_content=task.parent.content if task.parent else "",
276
294
  dependency_tasks_info=dependency_tasks_info,
277
295
  additional_info=task.additional_info,
278
296
  )
279
297
 
280
- response = await worker_agent.astep(
281
- prompt, response_format=TaskResult
282
- )
298
+ if self.use_structured_output_handler and self.structured_handler:
299
+ # Use structured output handler for prompt-based extraction
300
+ enhanced_prompt = (
301
+ self.structured_handler.generate_structured_prompt(
302
+ base_prompt=prompt,
303
+ schema=TaskResult,
304
+ examples=[
305
+ {
306
+ "content": "I have successfully completed the "
307
+ "task...",
308
+ "failed": False,
309
+ }
310
+ ],
311
+ additional_instructions="Ensure you provide a clear "
312
+ "description of what was done and whether the task "
313
+ "succeeded or failed.",
314
+ )
315
+ )
316
+ response = await worker_agent.astep(enhanced_prompt)
317
+ task_result = (
318
+ self.structured_handler.parse_structured_response(
319
+ response_text=response.msg.content
320
+ if response.msg
321
+ else "",
322
+ schema=TaskResult,
323
+ fallback_values={
324
+ "content": "Task processing failed",
325
+ "failed": True,
326
+ },
327
+ )
328
+ )
329
+ else:
330
+ # Use native structured output if supported
331
+ response = await worker_agent.astep(
332
+ prompt, response_format=TaskResult
333
+ )
334
+ task_result = response.msg.parsed
283
335
 
284
336
  # Get token usage from the response
285
337
  usage_info = response.info.get("usage") or response.info.get(
@@ -330,25 +382,27 @@ class SingleAgentWorker(Worker):
330
382
 
331
383
  print(f"======\n{Fore.GREEN}Response from {self}:{Fore.RESET}")
332
384
 
333
- try:
334
- result_dict = json.loads(response.msg.content)
335
- task_result = TaskResult(**result_dict)
336
- except json.JSONDecodeError as e:
337
- print(
338
- f"{Fore.RED}JSON parsing error for task {task.id}: "
339
- f"Invalid response format - {e}{Fore.RESET}"
340
- )
341
- return TaskState.FAILED
342
-
343
- color = Fore.RED if task_result.failed else Fore.GREEN
385
+ if not self.use_structured_output_handler:
386
+ # Handle native structured output parsing
387
+ if task_result is None:
388
+ print(
389
+ f"{Fore.RED}Error in worker step execution: Invalid "
390
+ f"task result{Fore.RESET}"
391
+ )
392
+ task_result = TaskResult(
393
+ content="Failed to generate valid task result.",
394
+ failed=True,
395
+ )
396
+
397
+ color = Fore.RED if task_result.failed else Fore.GREEN # type: ignore[union-attr]
344
398
  print(
345
- f"\n{color}{task_result.content}{Fore.RESET}\n======",
399
+ f"\n{color}{task_result.content}{Fore.RESET}\n======", # type: ignore[union-attr]
346
400
  )
347
401
 
348
- if task_result.failed:
402
+ if task_result.failed: # type: ignore[union-attr]
349
403
  return TaskState.FAILED
350
404
 
351
- task.result = task_result.content
405
+ task.result = task_result.content # type: ignore[union-attr]
352
406
 
353
407
  if is_task_result_insufficient(task):
354
408
  print(
@@ -0,0 +1,500 @@
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 json
15
+ import re
16
+ from typing import Any, ClassVar, Dict, List, Optional, Type, Union
17
+
18
+ from pydantic import BaseModel, ValidationError
19
+
20
+ from camel.logger import get_logger
21
+ from camel.societies.workforce.utils import (
22
+ RecoveryDecision,
23
+ RecoveryStrategy,
24
+ TaskAssignResult,
25
+ WorkerConf,
26
+ )
27
+
28
+ logger = get_logger(__name__)
29
+
30
+
31
+ class StructuredOutputHandler:
32
+ r"""Handler for generating prompts and extracting structured output from
33
+ agent responses.
34
+
35
+ This handler provides functionality to:
36
+ - Generate prompts that guide agents to produce structured output
37
+ - Extract structured data from agent responses using regex patterns
38
+ - Provide fallback mechanisms when extraction fails
39
+ - Support the existing structured output schemas used in workforce.py
40
+ """
41
+
42
+ # Common JSON extraction patterns
43
+ JSON_PATTERNS: ClassVar[List[str]] = [
44
+ # Pattern 1: Standard JSON block
45
+ r'```json\s*\n(.*?)\n```',
46
+ # Pattern 2: JSON without code block
47
+ r'(\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\})',
48
+ # Pattern 3: JSON with potential nested objects
49
+ r'(\{(?:[^{}]|(?:\{[^{}]*\}))*\})',
50
+ ]
51
+
52
+ # Schema-specific patterns for more targeted extraction
53
+ SCHEMA_PATTERNS: ClassVar[Dict[str, List[str]]] = {
54
+ 'TaskAssignResult': [
55
+ r'"assignments"\s*:\s*\[(.*?)\]',
56
+ r'assignments.*?:\s*\[(.*?)\]',
57
+ ],
58
+ 'WorkerConf': [
59
+ (
60
+ r'"role"\s*:\s*"([^"]+)".*?"sys_msg"\s*:\s*"([^"]+)".*?'
61
+ r'"description"\s*:\s*"([^"]+)"'
62
+ ),
63
+ (
64
+ r'role.*?:\s*"([^"]+)".*?sys_msg.*?:\s*"([^"]+)".*?'
65
+ r'description.*?:\s*"([^"]+)"'
66
+ ),
67
+ ],
68
+ 'RecoveryDecision': [
69
+ r'"strategy"\s*:\s*"([^"]+)".*?"reasoning"\s*:\s*"([^"]+)"',
70
+ r'strategy.*?:\s*"([^"]+)".*?reasoning.*?:\s*"([^"]+)"',
71
+ ],
72
+ }
73
+
74
+ @staticmethod
75
+ def generate_structured_prompt(
76
+ base_prompt: str,
77
+ schema: Type[BaseModel],
78
+ examples: Optional[List[Dict[str, Any]]] = None,
79
+ additional_instructions: Optional[str] = None,
80
+ ) -> str:
81
+ r"""Generate a prompt that guides agents to produce structured output.
82
+
83
+ Args:
84
+ base_prompt (str): The base prompt content.
85
+ schema (Type[BaseModel]): The Pydantic model schema for the
86
+ expected output.
87
+ examples (Optional[List[Dict[str, Any]]]): Optional examples of
88
+ valid output.
89
+ additional_instructions (Optional[str]): Additional instructions
90
+ for output formatting.
91
+
92
+ Returns:
93
+ str: The enhanced prompt with structured output instructions.
94
+ """
95
+ # Get schema information
96
+ schema_name = schema.__name__
97
+ schema_json = schema.model_json_schema()
98
+ required_fields = schema_json.get('required', [])
99
+ properties = schema_json.get('properties', {})
100
+
101
+ # Build field descriptions
102
+ field_descriptions = []
103
+ for field_name, field_info in properties.items():
104
+ description = field_info.get('description', 'No description')
105
+ field_type = field_info.get('type', 'unknown')
106
+ required = field_name in required_fields
107
+ field_descriptions.append(
108
+ f"- {field_name} ({field_type}{'*' if required else ''}): "
109
+ f"{description}"
110
+ )
111
+
112
+ # Build structured output section
113
+ structured_section = f"""
114
+ **STRUCTURED OUTPUT REQUIREMENTS:**
115
+
116
+ You must return a valid JSON object that conforms to the {schema_name} schema.
117
+
118
+ Required fields:
119
+ {chr(10).join(field_descriptions)}
120
+
121
+ Fields marked with * are required and must be present in your response.
122
+
123
+ **FORMAT YOUR RESPONSE AS:**
124
+ ```json
125
+ {{
126
+ // Your JSON response here following the schema
127
+ }}
128
+ ```
129
+ """
130
+
131
+ # Add examples if provided
132
+ if examples:
133
+ examples_section = "\n**VALID EXAMPLES:**\n"
134
+ for i, example in enumerate(examples, 1):
135
+ examples_section += f"\nExample {i}:\n```json\n"
136
+ examples_section += json.dumps(example, indent=2)
137
+ examples_section += "\n```\n"
138
+ structured_section += examples_section
139
+
140
+ # Add additional instructions if provided
141
+ if additional_instructions:
142
+ structured_section += "\n**ADDITIONAL INSTRUCTIONS:**\n"
143
+ structured_section += additional_instructions + "\n"
144
+
145
+ # Add critical reminder
146
+ structured_section += """
147
+ **CRITICAL**: Your response must contain ONLY the JSON object within the code
148
+ block.
149
+ Do not include any explanatory text, comments, or content outside the JSON
150
+ structure.
151
+ Ensure the JSON is valid and properly formatted.
152
+ """
153
+
154
+ # Combine with base prompt
155
+ return base_prompt + "\n\n" + structured_section
156
+
157
+ @staticmethod
158
+ def extract_json(
159
+ text: str,
160
+ schema: Optional[Type[BaseModel]] = None,
161
+ ) -> Optional[Dict[str, Any]]:
162
+ r"""Extract JSON data from text using multiple patterns.
163
+
164
+ Args:
165
+ text (str): The text containing JSON data.
166
+ schema (Optional[Type[BaseModel]]): Optional schema for targeted
167
+ extraction.
168
+
169
+ Returns:
170
+ Optional[Dict[str, Any]]: Extracted JSON data or None if extraction
171
+ fails.
172
+ """
173
+ if not text:
174
+ return None
175
+
176
+ # Try standard JSON patterns first
177
+ for pattern in StructuredOutputHandler.JSON_PATTERNS:
178
+ matches = re.findall(pattern, text, re.DOTALL | re.IGNORECASE)
179
+ for match in matches:
180
+ try:
181
+ # Clean up the match
182
+ json_str = match.strip()
183
+ # Remove any trailing commas
184
+ json_str = re.sub(r',\s*}', '}', json_str)
185
+ json_str = re.sub(r',\s*]', ']', json_str)
186
+
187
+ data = json.loads(json_str)
188
+ if isinstance(data, dict):
189
+ return data
190
+ except json.JSONDecodeError:
191
+ continue
192
+
193
+ # Try direct JSON parsing of the entire text
194
+ try:
195
+ data = json.loads(text.strip())
196
+ if isinstance(data, dict):
197
+ return data
198
+ except json.JSONDecodeError:
199
+ pass
200
+
201
+ # Try schema-specific patterns if schema is provided
202
+ if (
203
+ schema
204
+ and schema.__name__ in StructuredOutputHandler.SCHEMA_PATTERNS
205
+ ):
206
+ return StructuredOutputHandler._extract_with_schema_patterns(
207
+ text, schema
208
+ )
209
+
210
+ return None
211
+
212
+ @staticmethod
213
+ def _extract_with_schema_patterns(
214
+ text: str,
215
+ schema: Type[BaseModel],
216
+ ) -> Optional[Dict[str, Any]]:
217
+ r"""Extract data using schema-specific patterns.
218
+
219
+ Args:
220
+ text (str): The text to extract from.
221
+ schema (Type[BaseModel]): The schema to use for extraction.
222
+
223
+ Returns:
224
+ Optional[Dict[str, Any]]: Extracted data or None.
225
+ """
226
+ schema_name = schema.__name__
227
+ patterns = StructuredOutputHandler.SCHEMA_PATTERNS.get(schema_name, [])
228
+
229
+ if schema_name == 'WorkerConf':
230
+ for pattern in patterns:
231
+ match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
232
+ if match:
233
+ try:
234
+ return {
235
+ 'role': match.group(1),
236
+ 'sys_msg': match.group(2),
237
+ 'description': match.group(3),
238
+ }
239
+ except (IndexError, AttributeError):
240
+ continue
241
+
242
+ elif schema_name == 'RecoveryDecision':
243
+ for pattern in patterns:
244
+ match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
245
+ if match:
246
+ try:
247
+ strategy = match.group(1)
248
+ reasoning = match.group(2)
249
+ # Look for modified_task_content
250
+ content_match = re.search(
251
+ r'"modified_task_content"\s*:\s*"([^"]*)"',
252
+ text,
253
+ re.IGNORECASE,
254
+ )
255
+ return {
256
+ 'strategy': strategy,
257
+ 'reasoning': reasoning,
258
+ 'modified_task_content': (
259
+ content_match.group(1)
260
+ if content_match
261
+ else None
262
+ ),
263
+ }
264
+ except (IndexError, AttributeError):
265
+ continue
266
+
267
+ return None
268
+
269
+ @staticmethod
270
+ def parse_structured_response(
271
+ response_text: str,
272
+ schema: Type[BaseModel],
273
+ fallback_values: Optional[Dict[str, Any]] = None,
274
+ ) -> Union[BaseModel, Dict[str, Any]]:
275
+ r"""Parse agent response into structured data with fallback support.
276
+
277
+ Args:
278
+ response_text (str): The agent's response text.
279
+ schema (Type[BaseModel]): The expected schema.
280
+ fallback_values (Optional[Dict[str, Any]]): Fallback values to use
281
+ if parsing fails.
282
+
283
+ Returns:
284
+ Union[BaseModel, Dict[str, Any]]: Parsed data as schema instance
285
+ or fallback dictionary.
286
+ """
287
+ # Try to extract JSON
288
+ extracted_data = StructuredOutputHandler.extract_json(
289
+ response_text, schema
290
+ )
291
+
292
+ if extracted_data:
293
+ try:
294
+ # Validate against schema
295
+ return schema(**extracted_data)
296
+ except ValidationError as e:
297
+ logger.warning(
298
+ f"Validation error for {schema.__name__}: {e}. "
299
+ f"Attempting to fix common issues."
300
+ )
301
+
302
+ # Try to fix common validation issues
303
+ fixed_data = StructuredOutputHandler._fix_common_issues(
304
+ extracted_data, schema
305
+ )
306
+ if fixed_data:
307
+ try:
308
+ return schema(**fixed_data)
309
+ except ValidationError:
310
+ pass
311
+
312
+ # Use fallback values if provided
313
+ if fallback_values:
314
+ logger.warning(
315
+ f"Failed to parse {schema.__name__} from response. "
316
+ f"Using fallback values."
317
+ )
318
+ try:
319
+ return schema(**fallback_values)
320
+ except ValidationError:
321
+ return fallback_values
322
+
323
+ # Return default instance if possible
324
+ try:
325
+ logger.warning(f"Creating default {schema.__name__} instance.")
326
+ return StructuredOutputHandler._create_default_instance(schema)
327
+ except Exception:
328
+ logger.error(
329
+ f"Failed to create default instance for {schema.__name__}"
330
+ )
331
+ return {}
332
+
333
+ @staticmethod
334
+ def _fix_common_issues(
335
+ data: Dict[str, Any],
336
+ schema: Type[BaseModel],
337
+ ) -> Optional[Dict[str, Any]]:
338
+ r"""Attempt to fix common validation issues in extracted data.
339
+
340
+ Args:
341
+ data (Dict[str, Any]): The extracted data.
342
+ schema (Type[BaseModel]): The target schema.
343
+
344
+ Returns:
345
+ Optional[Dict[str, Any]]: Fixed data or None if unfixable.
346
+ """
347
+ fixed_data = data.copy()
348
+ schema_name = schema.__name__
349
+
350
+ if schema_name == 'TaskAssignResult':
351
+ # Ensure assignments is a list
352
+ if 'assignments' not in fixed_data:
353
+ fixed_data['assignments'] = []
354
+ elif not isinstance(fixed_data['assignments'], list):
355
+ fixed_data['assignments'] = [fixed_data['assignments']]
356
+
357
+ # Fix each assignment
358
+ for _i, assignment in enumerate(fixed_data['assignments']):
359
+ if isinstance(assignment, dict):
360
+ # Ensure dependencies is a list
361
+ if 'dependencies' not in assignment:
362
+ assignment['dependencies'] = []
363
+ elif isinstance(assignment['dependencies'], str):
364
+ # Handle comma-separated string
365
+ deps = assignment['dependencies'].strip()
366
+ if deps:
367
+ assignment['dependencies'] = [
368
+ d.strip() for d in deps.split(',')
369
+ ]
370
+ else:
371
+ assignment['dependencies'] = []
372
+
373
+ elif schema_name == 'RecoveryDecision':
374
+ # Ensure strategy is valid
375
+ if 'strategy' in fixed_data:
376
+ strategy = fixed_data['strategy'].lower()
377
+ valid_strategies = [
378
+ 'retry',
379
+ 'replan',
380
+ 'decompose',
381
+ 'create_worker',
382
+ ]
383
+ if strategy not in valid_strategies:
384
+ # Try to match partial
385
+ for valid in valid_strategies:
386
+ if valid.startswith(strategy) or strategy in valid:
387
+ fixed_data['strategy'] = valid
388
+ break
389
+
390
+ return fixed_data
391
+
392
+ @staticmethod
393
+ def _create_default_instance(schema: Type[BaseModel]) -> BaseModel:
394
+ r"""Create a default instance of the schema with minimal required
395
+ fields.
396
+
397
+ Args:
398
+ schema (Type[BaseModel]): The schema to instantiate.
399
+
400
+ Returns:
401
+ BaseModel: Default instance of the schema.
402
+ """
403
+ schema_name = schema.__name__
404
+
405
+ if schema_name == 'TaskAssignResult':
406
+ return TaskAssignResult(assignments=[])
407
+ elif schema_name == 'WorkerConf':
408
+ return WorkerConf(
409
+ role="General Assistant",
410
+ sys_msg="You are a helpful assistant.",
411
+ description="A general-purpose worker",
412
+ )
413
+ elif schema_name == 'RecoveryDecision':
414
+ return RecoveryDecision(
415
+ strategy=RecoveryStrategy.RETRY,
416
+ reasoning="Unable to parse response, defaulting to retry",
417
+ modified_task_content=None,
418
+ )
419
+ else:
420
+ # Try to create with empty dict and let defaults handle it
421
+ return schema()
422
+
423
+ @staticmethod
424
+ def validate_response(
425
+ response: Union[BaseModel, Dict[str, Any]],
426
+ schema: Type[BaseModel],
427
+ ) -> bool:
428
+ r"""Validate that a response conforms to the expected schema.
429
+
430
+ Args:
431
+ response: The response to validate.
432
+ schema (Type[BaseModel]): The expected schema.
433
+
434
+ Returns:
435
+ bool: True if valid, False otherwise.
436
+ """
437
+ if isinstance(response, schema):
438
+ return True
439
+
440
+ if isinstance(response, dict):
441
+ try:
442
+ schema(**response)
443
+ return True
444
+ except ValidationError:
445
+ return False
446
+
447
+ return False
448
+
449
+ @staticmethod
450
+ def create_fallback_response(
451
+ schema: Type[BaseModel],
452
+ error_message: str,
453
+ context: Optional[Dict[str, Any]] = None,
454
+ ) -> BaseModel:
455
+ r"""Create a fallback response for a given schema with error context.
456
+
457
+ Args:
458
+ schema (Type[BaseModel]): The schema to create a response for.
459
+ error_message (str): The error message to include.
460
+ context (Optional[Dict[str, Any]]): Additional context for the
461
+ fallback.
462
+
463
+ Returns:
464
+ BaseModel: A valid instance of the schema with fallback values.
465
+ """
466
+ schema_name = schema.__name__
467
+
468
+ if schema_name == 'TaskAssignResult':
469
+ # Return empty assignments
470
+ return TaskAssignResult(assignments=[])
471
+
472
+ elif schema_name == 'WorkerConf':
473
+ # Create a generic worker config
474
+ task_content = ""
475
+ if context and 'task_content' in context:
476
+ task_content = context['task_content'][:50]
477
+
478
+ return WorkerConf(
479
+ role="General Assistant",
480
+ sys_msg=f"You are a general assistant. Error during "
481
+ f"configuration: {error_message}",
482
+ description=f"Fallback worker for task: {task_content}...",
483
+ )
484
+
485
+ elif schema_name == 'RecoveryDecision':
486
+ # Default to retry strategy
487
+ return RecoveryDecision(
488
+ strategy=RecoveryStrategy.RETRY,
489
+ reasoning=f"Fallback decision due to: {error_message}",
490
+ modified_task_content=None,
491
+ )
492
+
493
+ else:
494
+ # Generic fallback
495
+ try:
496
+ return schema()
497
+ except Exception as e:
498
+ raise ValueError(
499
+ f"Cannot create fallback for unknown schema: {schema_name}"
500
+ ) from e