camel-ai 0.2.76a14__py3-none-any.whl → 0.2.78__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.

@@ -42,6 +42,36 @@ class TaskResult(BaseModel):
42
42
  )
43
43
 
44
44
 
45
+ class QualityEvaluation(BaseModel):
46
+ r"""Quality evaluation result for a completed task.
47
+
48
+ .. deprecated::
49
+ Use :class:`TaskAnalysisResult` instead. This class is kept for
50
+ backward compatibility.
51
+ """
52
+
53
+ quality_sufficient: bool = Field(
54
+ description="Whether the task result meets quality standards."
55
+ )
56
+ quality_score: int = Field(
57
+ description="Quality score from 0 to 100.", ge=0, le=100
58
+ )
59
+ issues: List[str] = Field(
60
+ default_factory=list,
61
+ description="List of quality issues found in the result.",
62
+ )
63
+ recovery_strategy: Optional[str] = Field(
64
+ default=None,
65
+ description="Recommended recovery strategy if quality is "
66
+ "insufficient: "
67
+ "'retry', 'reassign', 'replan', or 'decompose'.",
68
+ )
69
+ modified_task_content: Optional[str] = Field(
70
+ default=None,
71
+ description="Modified task content for replan strategy.",
72
+ )
73
+
74
+
45
75
  class TaskAssignment(BaseModel):
46
76
  r"""An individual task assignment within a batch."""
47
77
 
@@ -52,7 +82,8 @@ class TaskAssignment(BaseModel):
52
82
  dependencies: List[str] = Field(
53
83
  default_factory=list,
54
84
  description="List of task IDs that must complete before this task. "
55
- "This is critical for the task decomposition and execution.",
85
+ "This is critical for the task decomposition and "
86
+ "execution.",
56
87
  )
57
88
 
58
89
  # Allow LLMs to output dependencies as a comma-separated string or empty
@@ -60,7 +91,8 @@ class TaskAssignment(BaseModel):
60
91
  # downstream logic does not break with validation errors.
61
92
  @staticmethod
62
93
  def _split_and_strip(dep_str: str) -> List[str]:
63
- r"""Utility to split a comma separated string and strip whitespace."""
94
+ r"""Utility to split a comma separated string and strip
95
+ whitespace."""
64
96
  return [d.strip() for d in dep_str.split(',') if d.strip()]
65
97
 
66
98
  @field_validator("dependencies", mode="before")
@@ -74,7 +106,8 @@ class TaskAssignment(BaseModel):
74
106
 
75
107
 
76
108
  class TaskAssignResult(BaseModel):
77
- r"""The result of task assignment for both single and batch assignments."""
109
+ r"""The result of task assignment for both single and batch
110
+ assignments."""
78
111
 
79
112
  assignments: List[TaskAssignment] = Field(
80
113
  description="List of task assignments."
@@ -88,6 +121,7 @@ class RecoveryStrategy(str, Enum):
88
121
  REPLAN = "replan"
89
122
  DECOMPOSE = "decompose"
90
123
  CREATE_WORKER = "create_worker"
124
+ REASSIGN = "reassign"
91
125
 
92
126
  def __str__(self):
93
127
  return self.value
@@ -116,17 +150,75 @@ class FailureContext(BaseModel):
116
150
  )
117
151
 
118
152
 
119
- class RecoveryDecision(BaseModel):
120
- r"""Decision on how to recover from a task failure."""
153
+ class TaskAnalysisResult(BaseModel):
154
+ r"""Unified result for task failure analysis and quality evaluation.
155
+
156
+ This model combines both failure recovery decisions and quality evaluation
157
+ results into a single structure. For failure analysis, only the recovery
158
+ strategy and reasoning fields are populated. For quality evaluation, all
159
+ fields including quality_score and issues are populated.
160
+ """
161
+
162
+ # Common fields - always populated
163
+ reasoning: str = Field(
164
+ description="Explanation for the analysis result or recovery "
165
+ "decision"
166
+ )
121
167
 
122
- strategy: RecoveryStrategy = Field(
123
- description="The chosen recovery strategy"
168
+ recovery_strategy: Optional[RecoveryStrategy] = Field(
169
+ default=None,
170
+ description="Recommended recovery strategy: 'retry', 'replan', "
171
+ "'decompose', 'create_worker', or 'reassign'. None indicates no "
172
+ "recovery needed (quality sufficient).",
124
173
  )
125
- reasoning: str = Field(description="Explanation for the chosen strategy")
174
+
126
175
  modified_task_content: Optional[str] = Field(
127
- default=None, description="Modified task content if strategy is REPLAN"
176
+ default=None,
177
+ description="Modified task content if strategy requires replan",
128
178
  )
129
179
 
180
+ # Quality-specific fields - populated only for quality evaluation
181
+ quality_score: Optional[int] = Field(
182
+ default=None,
183
+ description="Quality score from 0 to 100 (only for quality "
184
+ "evaluation). "
185
+ "None indicates this is a failure analysis, "
186
+ "not quality evaluation.",
187
+ ge=0,
188
+ le=100,
189
+ )
190
+
191
+ issues: List[str] = Field(
192
+ default_factory=list,
193
+ description="List of issues found. For failures: error details. "
194
+ "For quality evaluation: quality issues.",
195
+ )
196
+
197
+ @property
198
+ def is_quality_evaluation(self) -> bool:
199
+ r"""Check if this is a quality evaluation result.
200
+
201
+ Returns:
202
+ bool: True if this is a quality evaluation (has quality_score),
203
+ False if this is a failure analysis.
204
+ """
205
+ return self.quality_score is not None
206
+
207
+ @property
208
+ def quality_sufficient(self) -> bool:
209
+ r"""For quality evaluations, check if quality meets standards.
210
+
211
+ Returns:
212
+ bool: True if quality is sufficient (score >= 70 and no recovery
213
+ strategy recommended), False otherwise. Always False for
214
+ failure analysis results.
215
+ """
216
+ return (
217
+ self.quality_score is not None
218
+ and self.quality_score >= 70
219
+ and self.recovery_strategy is None
220
+ )
221
+
130
222
 
131
223
  def check_if_running(
132
224
  running: bool,
@@ -178,7 +270,7 @@ def check_if_running(
178
270
  if retries < max_retries:
179
271
  logger.warning(
180
272
  f"{error_msg} Retrying in {retry_delay}s... "
181
- f"(Attempt {retries+1}/{max_retries})"
273
+ f"(Attempt {retries + 1}/{max_retries})"
182
274
  )
183
275
  time.sleep(retry_delay)
184
276
  retries += 1
@@ -200,7 +292,7 @@ def check_if_running(
200
292
  logger.warning(
201
293
  f"Exception in {func.__name__}: {e}. "
202
294
  f"Retrying in {retry_delay}s... "
203
- f"(Attempt {retries+1}/{max_retries})"
295
+ f"(Attempt {retries + 1}/{max_retries})"
204
296
  )
205
297
  time.sleep(retry_delay)
206
298
  retries += 1
@@ -218,7 +310,8 @@ def check_if_running(
218
310
  # This should not be reached, but just in case
219
311
  if handle_exceptions:
220
312
  logger.error(
221
- f"Unexpected failure in {func.__name__}: {last_exception}"
313
+ f"Unexpected failure in {func.__name__}: "
314
+ f"{last_exception}"
222
315
  )
223
316
  return None
224
317
  else: