mirror of
https://github.com/crewAIInc/crewAI.git
synced 2025-12-18 13:28:31 +00:00
Compare commits
7 Commits
docs/train
...
devin/1735
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edaa966e26 | ||
|
|
c38354c189 | ||
|
|
bdb32dd3e6 | ||
|
|
83b4e0b4c0 | ||
|
|
7c2b307217 | ||
|
|
ce4a730f76 | ||
|
|
8871d9a6cd |
@@ -179,6 +179,7 @@ class Task(BaseModel):
|
|||||||
_execution_span: Optional[Span] = PrivateAttr(default=None)
|
_execution_span: Optional[Span] = PrivateAttr(default=None)
|
||||||
_original_description: Optional[str] = PrivateAttr(default=None)
|
_original_description: Optional[str] = PrivateAttr(default=None)
|
||||||
_original_expected_output: Optional[str] = PrivateAttr(default=None)
|
_original_expected_output: Optional[str] = PrivateAttr(default=None)
|
||||||
|
_original_output_file: Optional[str] = PrivateAttr(default=None)
|
||||||
_thread: Optional[threading.Thread] = PrivateAttr(default=None)
|
_thread: Optional[threading.Thread] = PrivateAttr(default=None)
|
||||||
_execution_time: Optional[float] = PrivateAttr(default=None)
|
_execution_time: Optional[float] = PrivateAttr(default=None)
|
||||||
|
|
||||||
@@ -213,8 +214,46 @@ class Task(BaseModel):
|
|||||||
|
|
||||||
@field_validator("output_file")
|
@field_validator("output_file")
|
||||||
@classmethod
|
@classmethod
|
||||||
def output_file_validation(cls, value: str) -> str:
|
def output_file_validation(cls, value: Optional[str]) -> Optional[str]:
|
||||||
"""Validate the output file path by removing the / from the beginning of the path."""
|
"""Validate the output file path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The output file path to validate. Can be None or a string.
|
||||||
|
If the path contains template variables (e.g. {var}), leading slashes are preserved.
|
||||||
|
For regular paths, leading slashes are stripped.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The validated and potentially modified path, or None if no path was provided.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the path contains invalid characters, path traversal attempts,
|
||||||
|
or other security concerns.
|
||||||
|
"""
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Basic security checks
|
||||||
|
if ".." in value:
|
||||||
|
raise ValueError("Path traversal attempts are not allowed in output_file paths")
|
||||||
|
|
||||||
|
# Check for shell expansion first
|
||||||
|
if value.startswith('~') or value.startswith('$'):
|
||||||
|
raise ValueError("Shell expansion characters are not allowed in output_file paths")
|
||||||
|
|
||||||
|
# Then check other shell special characters
|
||||||
|
if any(char in value for char in ['|', '>', '<', '&', ';']):
|
||||||
|
raise ValueError("Shell special characters are not allowed in output_file paths")
|
||||||
|
|
||||||
|
# Don't strip leading slash if it's a template path with variables
|
||||||
|
if "{" in value or "}" in value:
|
||||||
|
# Validate template variable format
|
||||||
|
template_vars = [part.split("}")[0] for part in value.split("{")[1:]]
|
||||||
|
for var in template_vars:
|
||||||
|
if not var.isidentifier():
|
||||||
|
raise ValueError(f"Invalid template variable name: {var}")
|
||||||
|
return value
|
||||||
|
|
||||||
|
# Strip leading slash for regular paths
|
||||||
if value.startswith("/"):
|
if value.startswith("/"):
|
||||||
return value[1:]
|
return value[1:]
|
||||||
return value
|
return value
|
||||||
@@ -393,27 +432,89 @@ class Task(BaseModel):
|
|||||||
tasks_slices = [self.description, output]
|
tasks_slices = [self.description, output]
|
||||||
return "\n".join(tasks_slices)
|
return "\n".join(tasks_slices)
|
||||||
|
|
||||||
def interpolate_inputs(self, inputs: Dict[str, Any]) -> None:
|
def interpolate_inputs(self, inputs: Dict[str, Union[str, int, float]]) -> None:
|
||||||
"""Interpolate inputs into the task description and expected output."""
|
"""Interpolate inputs into the task description, expected output, and output file path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
inputs: Dictionary mapping template variables to their values.
|
||||||
|
Supported value types are strings, integers, and floats.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If a required template variable is missing from inputs.
|
||||||
|
"""
|
||||||
if self._original_description is None:
|
if self._original_description is None:
|
||||||
self._original_description = self.description
|
self._original_description = self.description
|
||||||
if self._original_expected_output is None:
|
if self._original_expected_output is None:
|
||||||
self._original_expected_output = self.expected_output
|
self._original_expected_output = self.expected_output
|
||||||
|
if self.output_file is not None and self._original_output_file is None:
|
||||||
|
self._original_output_file = self.output_file
|
||||||
|
|
||||||
if inputs:
|
if not inputs:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
self.description = self._original_description.format(**inputs)
|
self.description = self._original_description.format(**inputs)
|
||||||
|
except KeyError as e:
|
||||||
|
raise ValueError(f"Missing required template variable '{e.args[0]}' in description") from e
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValueError(f"Error interpolating description: {str(e)}") from e
|
||||||
|
|
||||||
|
try:
|
||||||
self.expected_output = self.interpolate_only(
|
self.expected_output = self.interpolate_only(
|
||||||
input_string=self._original_expected_output, inputs=inputs
|
input_string=self._original_expected_output, inputs=inputs
|
||||||
)
|
)
|
||||||
|
except (KeyError, ValueError) as e:
|
||||||
|
raise ValueError(f"Error interpolating expected_output: {str(e)}") from e
|
||||||
|
|
||||||
def interpolate_only(self, input_string: str, inputs: Dict[str, Any]) -> str:
|
if self.output_file is not None:
|
||||||
"""Interpolate placeholders (e.g., {key}) in a string while leaving JSON untouched."""
|
try:
|
||||||
escaped_string = input_string.replace("{", "{{").replace("}", "}}")
|
self.output_file = self.interpolate_only(
|
||||||
|
input_string=self._original_output_file, inputs=inputs
|
||||||
|
)
|
||||||
|
except (KeyError, ValueError) as e:
|
||||||
|
raise ValueError(f"Error interpolating output_file path: {str(e)}") from e
|
||||||
|
|
||||||
for key in inputs.keys():
|
def interpolate_only(self, input_string: Optional[str], inputs: Dict[str, Union[str, int, float]]) -> str:
|
||||||
escaped_string = escaped_string.replace(f"{{{{{key}}}}}", f"{{{key}}}")
|
"""Interpolate placeholders (e.g., {key}) in a string while leaving JSON untouched.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
input_string: The string containing template variables to interpolate.
|
||||||
|
Can be None or empty, in which case an empty string is returned.
|
||||||
|
inputs: Dictionary mapping template variables to their values.
|
||||||
|
Supported value types are strings, integers, and floats.
|
||||||
|
If input_string is empty or has no placeholders, inputs can be empty.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The interpolated string with all template variables replaced with their values.
|
||||||
|
Empty string if input_string is None or empty.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If a required template variable is missing from inputs.
|
||||||
|
KeyError: If a template variable is not found in the inputs dictionary.
|
||||||
|
"""
|
||||||
|
if input_string is None or not input_string:
|
||||||
|
return ""
|
||||||
|
if "{" not in input_string and "}" not in input_string:
|
||||||
|
return input_string
|
||||||
|
if not inputs:
|
||||||
|
raise ValueError("Inputs dictionary cannot be empty when interpolating variables")
|
||||||
|
|
||||||
return escaped_string.format(**inputs)
|
try:
|
||||||
|
# Validate input types
|
||||||
|
for key, value in inputs.items():
|
||||||
|
if not isinstance(value, (str, int, float)):
|
||||||
|
raise ValueError(f"Value for key '{key}' must be a string, integer, or float, got {type(value).__name__}")
|
||||||
|
|
||||||
|
escaped_string = input_string.replace("{", "{{").replace("}", "}}")
|
||||||
|
|
||||||
|
for key in inputs.keys():
|
||||||
|
escaped_string = escaped_string.replace(f"{{{{{key}}}}}", f"{{{key}}}")
|
||||||
|
|
||||||
|
return escaped_string.format(**inputs)
|
||||||
|
except KeyError as e:
|
||||||
|
raise KeyError(f"Template variable '{e.args[0]}' not found in inputs dictionary") from e
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValueError(f"Error during string interpolation: {str(e)}") from e
|
||||||
|
|
||||||
def increment_tools_errors(self) -> None:
|
def increment_tools_errors(self) -> None:
|
||||||
"""Increment the tools errors counter."""
|
"""Increment the tools errors counter."""
|
||||||
|
|||||||
243
tests/cassettes/test_crew_output_file_end_to_end.yaml
Normal file
243
tests/cassettes/test_crew_output_file_end_to_end.yaml
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: !!binary |
|
||||||
|
CuIcCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkSuRwKEgoQY3Jld2FpLnRl
|
||||||
|
bGVtZXRyeRKjBwoQXK7w4+uvyEkrI9D5qyvcJxII5UmQ7hmczdIqDENyZXcgQ3JlYXRlZDABOfxQ
|
||||||
|
/hs4jBUYQUi3DBw4jBUYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuODYuMEoaCg5weXRob25fdmVy
|
||||||
|
c2lvbhIICgYzLjEyLjdKLgoIY3Jld19rZXkSIgogYzk3YjVmZWI1ZDFiNjZiYjU5MDA2YWFhMDFh
|
||||||
|
MjljZDZKMQoHY3Jld19pZBImCiRkZjY3NGMwYi1hOTc0LTQ3NTAtYjlkMS0yZWQxNjM3MzFiNTZK
|
||||||
|
HAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdf
|
||||||
|
bnVtYmVyX29mX3Rhc2tzEgIYAUobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgBStECCgtjcmV3
|
||||||
|
X2FnZW50cxLBAgq+Alt7ImtleSI6ICIwN2Q5OWI2MzA0MTFkMzVmZDkwNDdhNTMyZDUzZGRhNyIs
|
||||||
|
ICJpZCI6ICI5MDYwYTQ2Zi02MDY3LTQ1N2MtOGU3ZC04NjAyN2YzY2U5ZDUiLCAicm9sZSI6ICJS
|
||||||
|
ZXNlYXJjaGVyIiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6IDIwLCAibWF4X3JwbSI6
|
||||||
|
IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAibGxtIjogImdwdC00by1taW5pIiwg
|
||||||
|
ImRlbGVnYXRpb25fZW5hYmxlZD8iOiBmYWxzZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZh
|
||||||
|
bHNlLCAibWF4X3JldHJ5X2xpbWl0IjogMiwgInRvb2xzX25hbWVzIjogW119XUr/AQoKY3Jld190
|
||||||
|
YXNrcxLwAQrtAVt7ImtleSI6ICI2Mzk5NjUxN2YzZjNmMWM5NGQ2YmI2MTdhYTBiMWM0ZiIsICJp
|
||||||
|
ZCI6ICJjYTA4ZjkyOS0yMmI0LTQyZmQtYjViMC05N2M3MjM0ZDk5OTEiLCAiYXN5bmNfZXhlY3V0
|
||||||
|
aW9uPyI6IGZhbHNlLCAiaHVtYW5faW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogIlJlc2Vh
|
||||||
|
cmNoZXIiLCAiYWdlbnRfa2V5IjogIjA3ZDk5YjYzMDQxMWQzNWZkOTA0N2E1MzJkNTNkZGE3Iiwg
|
||||||
|
InRvb2xzX25hbWVzIjogW119XXoCGAGFAQABAAASjgIKEOTJZh9R45IwgGVg9cinZmISCJopKRMf
|
||||||
|
bpMJKgxUYXNrIENyZWF0ZWQwATlG+zQcOIwVGEHk0zUcOIwVGEouCghjcmV3X2tleRIiCiBjOTdi
|
||||||
|
NWZlYjVkMWI2NmJiNTkwMDZhYWEwMWEyOWNkNkoxCgdjcmV3X2lkEiYKJGRmNjc0YzBiLWE5NzQt
|
||||||
|
NDc1MC1iOWQxLTJlZDE2MzczMWI1NkouCgh0YXNrX2tleRIiCiA2Mzk5NjUxN2YzZjNmMWM5NGQ2
|
||||||
|
YmI2MTdhYTBiMWM0ZkoxCgd0YXNrX2lkEiYKJGNhMDhmOTI5LTIyYjQtNDJmZC1iNWIwLTk3Yzcy
|
||||||
|
MzRkOTk5MXoCGAGFAQABAAASowcKEEvwrN8+tNMIBwtnA+ip7jASCI78Hrh2wlsBKgxDcmV3IENy
|
||||||
|
ZWF0ZWQwATkcRqYeOIwVGEE8erQeOIwVGEoaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjg2LjBKGgoO
|
||||||
|
cHl0aG9uX3ZlcnNpb24SCAoGMy4xMi43Si4KCGNyZXdfa2V5EiIKIDhjMjc1MmY0OWU1YjlkMmI2
|
||||||
|
OGNiMzVjYWM4ZmNjODZkSjEKB2NyZXdfaWQSJgokZmRkYzA4ZTMtNDUyNi00N2Q2LThlNWMtNjY0
|
||||||
|
YzIyMjc4ZDgyShwKDGNyZXdfcHJvY2VzcxIMCgpzZXF1ZW50aWFsShEKC2NyZXdfbWVtb3J5EgIQ
|
||||||
|
AEoaChRjcmV3X251bWJlcl9vZl90YXNrcxICGAFKGwoVY3Jld19udW1iZXJfb2ZfYWdlbnRzEgIY
|
||||||
|
AUrRAgoLY3Jld19hZ2VudHMSwQIKvgJbeyJrZXkiOiAiOGJkMjEzOWI1OTc1MTgxNTA2ZTQxZmQ5
|
||||||
|
YzQ1NjNkNzUiLCAiaWQiOiAiY2UxNjA2YjktMjdiOS00ZDc4LWEyODctNDZiMDNlZDg3ZTA1Iiwg
|
||||||
|
InJvbGUiOiAiUmVzZWFyY2hlciIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwg
|
||||||
|
Im1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQt
|
||||||
|
NG8tbWluaSIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4ZWN1
|
||||||
|
dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtdfV1K
|
||||||
|
/wEKCmNyZXdfdGFza3MS8AEK7QFbeyJrZXkiOiAiMGQ2ODVhMjE5OTRkOTQ5MDk3YmM1YTU2ZDcz
|
||||||
|
N2U2ZDEiLCAiaWQiOiAiNDdkMzRjZjktMGYxZS00Y2JkLTgzMzItNzRjZjY0YWRlOThlIiwgImFz
|
||||||
|
eW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9s
|
||||||
|
ZSI6ICJSZXNlYXJjaGVyIiwgImFnZW50X2tleSI6ICI4YmQyMTM5YjU5NzUxODE1MDZlNDFmZDlj
|
||||||
|
NDU2M2Q3NSIsICJ0b29sc19uYW1lcyI6IFtdfV16AhgBhQEAAQAAEo4CChAf4TXS782b0PBJ4NSB
|
||||||
|
JXwsEgjXnd13GkMzlyoMVGFzayBDcmVhdGVkMAE5mb/cHjiMFRhBGRTiHjiMFRhKLgoIY3Jld19r
|
||||||
|
ZXkSIgogOGMyNzUyZjQ5ZTViOWQyYjY4Y2IzNWNhYzhmY2M4NmRKMQoHY3Jld19pZBImCiRmZGRj
|
||||||
|
MDhlMy00NTI2LTQ3ZDYtOGU1Yy02NjRjMjIyNzhkODJKLgoIdGFza19rZXkSIgogMGQ2ODVhMjE5
|
||||||
|
OTRkOTQ5MDk3YmM1YTU2ZDczN2U2ZDFKMQoHdGFza19pZBImCiQ0N2QzNGNmOS0wZjFlLTRjYmQt
|
||||||
|
ODMzMi03NGNmNjRhZGU5OGV6AhgBhQEAAQAAEqMHChAyBGKhzDhROB5pmAoXrikyEgj6SCwzj1dU
|
||||||
|
LyoMQ3JldyBDcmVhdGVkMAE5vkjTHziMFRhBRDbhHziMFRhKGgoOY3Jld2FpX3ZlcnNpb24SCAoG
|
||||||
|
MC44Ni4wShoKDnB5dGhvbl92ZXJzaW9uEggKBjMuMTIuN0ouCghjcmV3X2tleRIiCiBiNjczNjg2
|
||||||
|
ZmM4MjJjMjAzYzdlODc5YzY3NTQyNDY5OUoxCgdjcmV3X2lkEiYKJGYyYWVlYTYzLTU2OWUtNDUz
|
||||||
|
NS1iZTY0LTRiZjYzZmU5NjhjN0ocCgxjcmV3X3Byb2Nlc3MSDAoKc2VxdWVudGlhbEoRCgtjcmV3
|
||||||
|
X21lbW9yeRICEABKGgoUY3Jld19udW1iZXJfb2ZfdGFza3MSAhgBShsKFWNyZXdfbnVtYmVyX29m
|
||||||
|
X2FnZW50cxICGAFK0QIKC2NyZXdfYWdlbnRzEsECCr4CW3sia2V5IjogImI1OWNmNzdiNmU3NjU4
|
||||||
|
NDg3MGViMWMzODgyM2Q3ZTI4IiwgImlkIjogImJiZjNkM2E4LWEwMjUtNGI0ZC1hY2Q0LTFmNzcz
|
||||||
|
NTI3MWJmMCIsICJyb2xlIjogIlJlc2VhcmNoZXIiLCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9p
|
||||||
|
dGVyIjogMjAsICJtYXhfcnBtIjogbnVsbCwgImZ1bmN0aW9uX2NhbGxpbmdfbGxtIjogIiIsICJs
|
||||||
|
bG0iOiAiZ3B0LTRvLW1pbmkiLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3df
|
||||||
|
Y29kZV9leGVjdXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFt
|
||||||
|
ZXMiOiBbXX1dSv8BCgpjcmV3X3Rhc2tzEvABCu0BW3sia2V5IjogImE1ZTVjNThjZWExYjlkMDAz
|
||||||
|
MzJlNjg0NDFkMzI3YmRmIiwgImlkIjogIjBiOTRiMTY0LTM5NTktNGFmYS05Njg4LWJjNmEwZWMy
|
||||||
|
MWYzOCIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwg
|
||||||
|
ImFnZW50X3JvbGUiOiAiUmVzZWFyY2hlciIsICJhZ2VudF9rZXkiOiAiYjU5Y2Y3N2I2ZTc2NTg0
|
||||||
|
ODcwZWIxYzM4ODIzZDdlMjgiLCAidG9vbHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABKOAgoQyYfi
|
||||||
|
Ftim717svttBZY3p5hIIUxR5bBHzWWkqDFRhc2sgQ3JlYXRlZDABOV4OBiA4jBUYQbLjBiA4jBUY
|
||||||
|
Si4KCGNyZXdfa2V5EiIKIGI2NzM2ODZmYzgyMmMyMDNjN2U4NzljNjc1NDI0Njk5SjEKB2NyZXdf
|
||||||
|
aWQSJgokZjJhZWVhNjMtNTY5ZS00NTM1LWJlNjQtNGJmNjNmZTk2OGM3Si4KCHRhc2tfa2V5EiIK
|
||||||
|
IGE1ZTVjNThjZWExYjlkMDAzMzJlNjg0NDFkMzI3YmRmSjEKB3Rhc2tfaWQSJgokMGI5NGIxNjQt
|
||||||
|
Mzk1OS00YWZhLTk2ODgtYmM2YTBlYzIxZjM4egIYAYUBAAEAAA==
|
||||||
|
headers:
|
||||||
|
Accept:
|
||||||
|
- '*/*'
|
||||||
|
Accept-Encoding:
|
||||||
|
- gzip, deflate
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Length:
|
||||||
|
- '3685'
|
||||||
|
Content-Type:
|
||||||
|
- application/x-protobuf
|
||||||
|
User-Agent:
|
||||||
|
- OTel-OTLP-Exporter-Python/1.27.0
|
||||||
|
method: POST
|
||||||
|
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||||
|
response:
|
||||||
|
body:
|
||||||
|
string: "\n\0"
|
||||||
|
headers:
|
||||||
|
Content-Length:
|
||||||
|
- '2'
|
||||||
|
Content-Type:
|
||||||
|
- application/x-protobuf
|
||||||
|
Date:
|
||||||
|
- Sun, 29 Dec 2024 04:43:27 GMT
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "system", "content": "You are Researcher. You have
|
||||||
|
extensive AI research experience.\nYour personal goal is: Analyze AI topics\nTo
|
||||||
|
give my best complete final answer to the task use the exact following format:\n\nThought:
|
||||||
|
I now can give a great answer\nFinal Answer: Your final answer must be the great
|
||||||
|
and the most complete as possible, it must be outcome described.\n\nI MUST use
|
||||||
|
these formats, my job depends on it!"}, {"role": "user", "content": "\nCurrent
|
||||||
|
Task: Explain the advantages of AI.\n\nThis is the expect criteria for your
|
||||||
|
final answer: A summary of the main advantages, bullet points recommended.\nyou
|
||||||
|
MUST return the actual complete content as the final answer, not a summary.\n\nBegin!
|
||||||
|
This is VERY important to you, use the tools available and give your best Final
|
||||||
|
Answer, your job depends on it!\n\nThought:"}], "model": "gpt-4o-mini", "stop":
|
||||||
|
["\nObservation:"], "stream": false}'
|
||||||
|
headers:
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- gzip, deflate
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '922'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
cookie:
|
||||||
|
- _cfuvid=eff7OIkJ0zWRunpA6z67LHqscmSe6XjNxXiPw1R3xCc-1733770413538-0.0.1.1-604800000
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
user-agent:
|
||||||
|
- OpenAI/Python 1.52.1
|
||||||
|
x-stainless-arch:
|
||||||
|
- x64
|
||||||
|
x-stainless-async:
|
||||||
|
- 'false'
|
||||||
|
x-stainless-lang:
|
||||||
|
- python
|
||||||
|
x-stainless-os:
|
||||||
|
- Linux
|
||||||
|
x-stainless-package-version:
|
||||||
|
- 1.52.1
|
||||||
|
x-stainless-raw-response:
|
||||||
|
- 'true'
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.12.7
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
content: "{\n \"id\": \"chatcmpl-AjfR6FDuTw7NGzy8w7sxjvOkUQlru\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1735447404,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": \"I now can give a great answer \\nFinal
|
||||||
|
Answer: \\n**Advantages of AI** \\n\\n1. **Increased Efficiency and Productivity**
|
||||||
|
\ \\n - AI systems can process large amounts of data quickly and accurately,
|
||||||
|
leading to faster decision-making and increased productivity in various sectors.\\n\\n2.
|
||||||
|
**Cost Savings** \\n - Automation of repetitive and time-consuming tasks
|
||||||
|
reduces labor costs and increases operational efficiency, allowing businesses
|
||||||
|
to allocate resources more effectively.\\n\\n3. **Enhanced Data Analysis** \\n
|
||||||
|
\ - AI excels at analyzing big data, identifying patterns, and providing insights
|
||||||
|
that support better strategic planning and business decision-making.\\n\\n4.
|
||||||
|
**24/7 Availability** \\n - AI solutions, such as chatbots and virtual assistants,
|
||||||
|
operate continuously without breaks, offering constant support and customer
|
||||||
|
service, enhancing user experience.\\n\\n5. **Personalization** \\n - AI
|
||||||
|
enables the customization of content, products, and services based on user preferences
|
||||||
|
and behaviors, leading to improved customer satisfaction and loyalty.\\n\\n6.
|
||||||
|
**Improved Accuracy** \\n - AI technologies, such as machine learning algorithms,
|
||||||
|
reduce the likelihood of human error in various processes, leading to greater
|
||||||
|
accuracy and reliability.\\n\\n7. **Enhanced Innovation** \\n - AI fosters
|
||||||
|
innovative solutions by providing new tools and approaches to problem-solving,
|
||||||
|
enabling companies to develop cutting-edge products and services.\\n\\n8. **Scalability**
|
||||||
|
\ \\n - AI can be scaled to handle varying amounts of workloads without significant
|
||||||
|
changes to infrastructure, making it easier for organizations to expand operations.\\n\\n9.
|
||||||
|
**Predictive Capabilities** \\n - Advanced analytics powered by AI can anticipate
|
||||||
|
trends and outcomes, allowing businesses to proactively adjust strategies and
|
||||||
|
improve forecasting.\\n\\n10. **Health Benefits** \\n - In healthcare, AI
|
||||||
|
assists in diagnostics, personalized treatment plans, and predictive analytics,
|
||||||
|
leading to better patient care and improved health outcomes.\\n\\n11. **Safety
|
||||||
|
and Risk Mitigation** \\n - AI can enhance safety in various industries
|
||||||
|
by taking over dangerous tasks, monitoring for hazards, and predicting maintenance
|
||||||
|
needs for critical machinery, thereby preventing accidents.\\n\\n12. **Reduced
|
||||||
|
Environmental Impact** \\n - AI can optimize resource usage in areas such
|
||||||
|
as energy consumption and supply chain logistics, contributing to sustainability
|
||||||
|
efforts and reducing overall environmental footprints.\",\n \"refusal\":
|
||||||
|
null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n
|
||||||
|
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 168,\n \"completion_tokens\":
|
||||||
|
440,\n \"total_tokens\": 608,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": {\n
|
||||||
|
\ \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"system_fingerprint\":
|
||||||
|
\"fp_0aa8d3e20b\"\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-Cache-Status:
|
||||||
|
- DYNAMIC
|
||||||
|
CF-RAY:
|
||||||
|
- 8f9721053d1eb9f1-SEA
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Encoding:
|
||||||
|
- gzip
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Sun, 29 Dec 2024 04:43:32 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Set-Cookie:
|
||||||
|
- __cf_bm=5enubNIoQSGMYEgy8Q2FpzzhphA0y.0lXukRZrWFvMk-1735447412-1.0.1.1-FIK1sMkUl3YnW1gTC6ftDtb2mKsbosb4mwabdFAlWCfJ6pXeavYq.bPsfKNvzAb5WYq60yVGH5lHsJT05bhSgw;
|
||||||
|
path=/; expires=Sun, 29-Dec-24 05:13:32 GMT; domain=.api.openai.com; HttpOnly;
|
||||||
|
Secure; SameSite=None
|
||||||
|
- _cfuvid=63wmKMTuFamkLN8FBI4fP8JZWbjWiRxWm7wb3kz.z_A-1735447412038-0.0.1.1-604800000;
|
||||||
|
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- nosniff
|
||||||
|
access-control-expose-headers:
|
||||||
|
- X-Request-ID
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
openai-organization:
|
||||||
|
- crewai-iuxna1
|
||||||
|
openai-processing-ms:
|
||||||
|
- '7577'
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
strict-transport-security:
|
||||||
|
- max-age=31536000; includeSubDomains; preload
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- '30000'
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- '150000000'
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- '29999'
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- '149999793'
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- 2ms
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- 0s
|
||||||
|
x-request-id:
|
||||||
|
- req_55b8d714656e8f10f4e23cbe9034d66b
|
||||||
|
http_version: HTTP/1.1
|
||||||
|
status_code: 200
|
||||||
|
version: 1
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
interactions:
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "system", "content": "You are TestAgent. Testing
|
||||||
|
interpolation logic\nYour personal goal is: Validate output_file interpolation
|
||||||
|
with kickoff_for_each\nTo give my best complete final answer to the task use
|
||||||
|
the exact following format:\n\nThought: I now can give a great answer\nFinal
|
||||||
|
Answer: Your final answer must be the great and the most complete as possible,
|
||||||
|
it must be outcome described.\n\nI MUST use these formats, my job depends on
|
||||||
|
it!"}, {"role": "user", "content": "\nCurrent Task: Create a brief summary for
|
||||||
|
the ID: alpha\n\nThis is the expect criteria for your final answer: Should write
|
||||||
|
a file with the ID included in the path\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary.\n\nBegin! This is VERY important
|
||||||
|
to you, use the tools available and give your best Final Answer, your job depends
|
||||||
|
on it!\n\nThought:"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"], "stream":
|
||||||
|
false}'
|
||||||
|
headers:
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- gzip, deflate
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '948'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
user-agent:
|
||||||
|
- OpenAI/Python 1.52.1
|
||||||
|
x-stainless-arch:
|
||||||
|
- x64
|
||||||
|
x-stainless-async:
|
||||||
|
- 'false'
|
||||||
|
x-stainless-lang:
|
||||||
|
- python
|
||||||
|
x-stainless-os:
|
||||||
|
- Linux
|
||||||
|
x-stainless-package-version:
|
||||||
|
- 1.52.1
|
||||||
|
x-stainless-raw-response:
|
||||||
|
- 'true'
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.12.7
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
content: "{\n \"id\": \"chatcmpl-AjflQm9Vq8QvtY5ORMahjQVL061VL\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1735448664,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": \"I now can give a great answer \\nFinal
|
||||||
|
Answer: The content for the file with ID 'alpha' is as follows:\\n\\n```\\nThis
|
||||||
|
is the content specific to ID: alpha. This file serves as a reference for the
|
||||||
|
alpha ID and may include various information relevant to its usage. The ID aids
|
||||||
|
in categorization and organization of data. \\n```\\n\\nThe file should be
|
||||||
|
created with the path including the ID, for example: `./output/alpha_file.txt`.\",\n
|
||||||
|
\ \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\":
|
||||||
|
\"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 175,\n \"completion_tokens\":
|
||||||
|
94,\n \"total_tokens\": 269,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": {\n
|
||||||
|
\ \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"system_fingerprint\":
|
||||||
|
\"fp_0aa8d3e20b\"\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-Cache-Status:
|
||||||
|
- DYNAMIC
|
||||||
|
CF-RAY:
|
||||||
|
- 8f973fc8a8e176cd-SEA
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Encoding:
|
||||||
|
- gzip
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Sun, 29 Dec 2024 05:04:25 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Set-Cookie:
|
||||||
|
- __cf_bm=Td9pau1aHgSMMxQpKkO8mpGk5DWBDjUinUYlfJPsgcw-1735448665-1.0.1.1-wH0kiPhZtNwT_fIqYCb3obZgU2VIxoPL.QrruewXdLVLT9FeI4b6BU.sHqsBjMODx6Tvtuzz9GQRvoY1nWs6Fw;
|
||||||
|
path=/; expires=Sun, 29-Dec-24 05:34:25 GMT; domain=.api.openai.com; HttpOnly;
|
||||||
|
Secure; SameSite=None
|
||||||
|
- _cfuvid=P0VQx4Uj_TpVJBRs2gSXmq3srUR2DW7oj5Vmq54U47M-1735448665828-0.0.1.1-604800000;
|
||||||
|
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- nosniff
|
||||||
|
access-control-expose-headers:
|
||||||
|
- X-Request-ID
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
openai-organization:
|
||||||
|
- crewai-iuxna1
|
||||||
|
openai-processing-ms:
|
||||||
|
- '1288'
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
strict-transport-security:
|
||||||
|
- max-age=31536000; includeSubDomains; preload
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- '30000'
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- '150000000'
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- '29999'
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- '149999786'
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- 2ms
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- 0s
|
||||||
|
x-request-id:
|
||||||
|
- req_9dd7c0e64935fade58b5b8359fcc9c02
|
||||||
|
http_version: HTTP/1.1
|
||||||
|
status_code: 200
|
||||||
|
- request:
|
||||||
|
body: '{"messages": [{"role": "system", "content": "You are TestAgent. Testing
|
||||||
|
interpolation logic\nYour personal goal is: Validate output_file interpolation
|
||||||
|
with kickoff_for_each\nTo give my best complete final answer to the task use
|
||||||
|
the exact following format:\n\nThought: I now can give a great answer\nFinal
|
||||||
|
Answer: Your final answer must be the great and the most complete as possible,
|
||||||
|
it must be outcome described.\n\nI MUST use these formats, my job depends on
|
||||||
|
it!"}, {"role": "user", "content": "\nCurrent Task: Create a brief summary for
|
||||||
|
the ID: beta\n\nThis is the expect criteria for your final answer: Should write
|
||||||
|
a file with the ID included in the path\nyou MUST return the actual complete
|
||||||
|
content as the final answer, not a summary.\n\nBegin! This is VERY important
|
||||||
|
to you, use the tools available and give your best Final Answer, your job depends
|
||||||
|
on it!\n\nThought:"}], "model": "gpt-4o-mini", "stop": ["\nObservation:"], "stream":
|
||||||
|
false}'
|
||||||
|
headers:
|
||||||
|
accept:
|
||||||
|
- application/json
|
||||||
|
accept-encoding:
|
||||||
|
- gzip, deflate
|
||||||
|
connection:
|
||||||
|
- keep-alive
|
||||||
|
content-length:
|
||||||
|
- '947'
|
||||||
|
content-type:
|
||||||
|
- application/json
|
||||||
|
cookie:
|
||||||
|
- __cf_bm=Td9pau1aHgSMMxQpKkO8mpGk5DWBDjUinUYlfJPsgcw-1735448665-1.0.1.1-wH0kiPhZtNwT_fIqYCb3obZgU2VIxoPL.QrruewXdLVLT9FeI4b6BU.sHqsBjMODx6Tvtuzz9GQRvoY1nWs6Fw;
|
||||||
|
_cfuvid=P0VQx4Uj_TpVJBRs2gSXmq3srUR2DW7oj5Vmq54U47M-1735448665828-0.0.1.1-604800000
|
||||||
|
host:
|
||||||
|
- api.openai.com
|
||||||
|
user-agent:
|
||||||
|
- OpenAI/Python 1.52.1
|
||||||
|
x-stainless-arch:
|
||||||
|
- x64
|
||||||
|
x-stainless-async:
|
||||||
|
- 'false'
|
||||||
|
x-stainless-lang:
|
||||||
|
- python
|
||||||
|
x-stainless-os:
|
||||||
|
- Linux
|
||||||
|
x-stainless-package-version:
|
||||||
|
- 1.52.1
|
||||||
|
x-stainless-raw-response:
|
||||||
|
- 'true'
|
||||||
|
x-stainless-retry-count:
|
||||||
|
- '0'
|
||||||
|
x-stainless-runtime:
|
||||||
|
- CPython
|
||||||
|
x-stainless-runtime-version:
|
||||||
|
- 3.12.7
|
||||||
|
method: POST
|
||||||
|
uri: https://api.openai.com/v1/chat/completions
|
||||||
|
response:
|
||||||
|
content: "{\n \"id\": \"chatcmpl-AjflSvzNtfBXfJSerUuWwQM47tE4N\",\n \"object\":
|
||||||
|
\"chat.completion\",\n \"created\": 1735448666,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||||
|
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||||
|
\"assistant\",\n \"content\": \"I now can give a great answer \\nFinal
|
||||||
|
Answer: Here is the content to be written to the file for ID: beta\\n\\n```\\nThis
|
||||||
|
is the content specifically related to the ID: beta.\\n```\\n\\nThe file will
|
||||||
|
be created at the following path: `/path/to/directory/beta.txt` \\nThank you
|
||||||
|
for the opportunity to complete this task!\",\n \"refusal\": null\n },\n
|
||||||
|
\ \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n
|
||||||
|
\ \"usage\": {\n \"prompt_tokens\": 175,\n \"completion_tokens\": 72,\n
|
||||||
|
\ \"total_tokens\": 247,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||||
|
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\": {\n
|
||||||
|
\ \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||||
|
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"system_fingerprint\":
|
||||||
|
\"fp_0aa8d3e20b\"\n}\n"
|
||||||
|
headers:
|
||||||
|
CF-Cache-Status:
|
||||||
|
- DYNAMIC
|
||||||
|
CF-RAY:
|
||||||
|
- 8f973fd1af7576cd-SEA
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Content-Encoding:
|
||||||
|
- gzip
|
||||||
|
Content-Type:
|
||||||
|
- application/json
|
||||||
|
Date:
|
||||||
|
- Sun, 29 Dec 2024 05:04:27 GMT
|
||||||
|
Server:
|
||||||
|
- cloudflare
|
||||||
|
Transfer-Encoding:
|
||||||
|
- chunked
|
||||||
|
X-Content-Type-Options:
|
||||||
|
- nosniff
|
||||||
|
access-control-expose-headers:
|
||||||
|
- X-Request-ID
|
||||||
|
alt-svc:
|
||||||
|
- h3=":443"; ma=86400
|
||||||
|
openai-organization:
|
||||||
|
- crewai-iuxna1
|
||||||
|
openai-processing-ms:
|
||||||
|
- '1475'
|
||||||
|
openai-version:
|
||||||
|
- '2020-10-01'
|
||||||
|
strict-transport-security:
|
||||||
|
- max-age=31536000; includeSubDomains; preload
|
||||||
|
x-ratelimit-limit-requests:
|
||||||
|
- '30000'
|
||||||
|
x-ratelimit-limit-tokens:
|
||||||
|
- '150000000'
|
||||||
|
x-ratelimit-remaining-requests:
|
||||||
|
- '29999'
|
||||||
|
x-ratelimit-remaining-tokens:
|
||||||
|
- '149999785'
|
||||||
|
x-ratelimit-reset-requests:
|
||||||
|
- 2ms
|
||||||
|
x-ratelimit-reset-tokens:
|
||||||
|
- 0s
|
||||||
|
x-request-id:
|
||||||
|
- req_dc9a842ed35e213e52c2d6fe9f11c36a
|
||||||
|
http_version: HTTP/1.1
|
||||||
|
status_code: 200
|
||||||
|
version: 1
|
||||||
@@ -1183,6 +1183,53 @@ def test_kickoff_for_each_error_handling():
|
|||||||
crew.kickoff_for_each(inputs=inputs)
|
crew.kickoff_for_each(inputs=inputs)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||||
|
def test_kickoff_for_each_output_file_interpolation(tmp_path):
|
||||||
|
"""Test that output_file paths are correctly interpolated when using kickoff_for_each."""
|
||||||
|
# Create test directories
|
||||||
|
output_dir = tmp_path / "output_files"
|
||||||
|
output_dir.mkdir()
|
||||||
|
|
||||||
|
agent = Agent(
|
||||||
|
role="TestAgent",
|
||||||
|
goal="Validate output_file interpolation with kickoff_for_each",
|
||||||
|
backstory="Testing interpolation logic",
|
||||||
|
allow_code_execution=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
task = Task(
|
||||||
|
description="Create a brief summary for the ID: {id_var}",
|
||||||
|
expected_output="Should write a file with the ID included in the path",
|
||||||
|
agent=agent,
|
||||||
|
output_file=str(output_dir / "summary_{id_var}.md")
|
||||||
|
)
|
||||||
|
|
||||||
|
crew = Crew(agents=[agent], tasks=[task], verbose=True)
|
||||||
|
|
||||||
|
inputs = [
|
||||||
|
{"id_var": "alpha"},
|
||||||
|
{"id_var": "beta"},
|
||||||
|
]
|
||||||
|
|
||||||
|
# Use kickoff_for_each
|
||||||
|
results = crew.kickoff_for_each(inputs=inputs)
|
||||||
|
|
||||||
|
# Verify each iteration's output_file was properly interpolated
|
||||||
|
for input_data, result in zip(inputs, results):
|
||||||
|
expected_path = output_dir / f"summary_{input_data['id_var']}.md"
|
||||||
|
assert expected_path.exists(), f"Expected output file {expected_path} was not created"
|
||||||
|
|
||||||
|
# Verify the output file was created with the correct name
|
||||||
|
assert expected_path.exists(), (
|
||||||
|
f"Expected output file {expected_path} was not created"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify file contains some content
|
||||||
|
assert expected_path.stat().st_size > 0, (
|
||||||
|
f"Output file {expected_path} is empty"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_kickoff_async_basic_functionality_and_output():
|
async def test_kickoff_async_basic_functionality_and_output():
|
||||||
@@ -1941,6 +1988,90 @@ def test_crew_log_file_output(tmp_path):
|
|||||||
assert test_file.exists()
|
assert test_file.exists()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||||
|
def test_crew_output_file_end_to_end(tmp_path):
|
||||||
|
"""Test output file functionality in a full crew context."""
|
||||||
|
# Create an agent
|
||||||
|
agent = Agent(
|
||||||
|
role="Researcher",
|
||||||
|
goal="Analyze AI topics",
|
||||||
|
backstory="You have extensive AI research experience.",
|
||||||
|
allow_delegation=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a task with dynamic output file path
|
||||||
|
dynamic_path = tmp_path / "output_{topic}.txt"
|
||||||
|
task = Task(
|
||||||
|
description="Explain the advantages of {topic}.",
|
||||||
|
expected_output="A summary of the main advantages, bullet points recommended.",
|
||||||
|
agent=agent,
|
||||||
|
output_file=str(dynamic_path),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create and run the crew
|
||||||
|
crew = Crew(
|
||||||
|
agents=[agent],
|
||||||
|
tasks=[task],
|
||||||
|
process=Process.sequential,
|
||||||
|
)
|
||||||
|
crew.kickoff(inputs={"topic": "AI"})
|
||||||
|
|
||||||
|
# Verify file creation and cleanup
|
||||||
|
expected_file = tmp_path / "output_AI.txt"
|
||||||
|
assert expected_file.exists(), f"Output file {expected_file} was not created"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||||
|
def test_crew_output_file_validation_failures():
|
||||||
|
"""Test output file validation failures in a crew context."""
|
||||||
|
agent = Agent(
|
||||||
|
role="Researcher",
|
||||||
|
goal="Analyze data",
|
||||||
|
backstory="You analyze data files.",
|
||||||
|
allow_delegation=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test path traversal
|
||||||
|
with pytest.raises(ValueError, match="Path traversal"):
|
||||||
|
task = Task(
|
||||||
|
description="Analyze data",
|
||||||
|
expected_output="Analysis results",
|
||||||
|
agent=agent,
|
||||||
|
output_file="../output.txt"
|
||||||
|
)
|
||||||
|
Crew(agents=[agent], tasks=[task]).kickoff()
|
||||||
|
|
||||||
|
# Test shell special characters
|
||||||
|
with pytest.raises(ValueError, match="Shell special characters"):
|
||||||
|
task = Task(
|
||||||
|
description="Analyze data",
|
||||||
|
expected_output="Analysis results",
|
||||||
|
agent=agent,
|
||||||
|
output_file="output.txt | rm -rf /"
|
||||||
|
)
|
||||||
|
Crew(agents=[agent], tasks=[task]).kickoff()
|
||||||
|
|
||||||
|
# Test shell expansion
|
||||||
|
with pytest.raises(ValueError, match="Shell expansion"):
|
||||||
|
task = Task(
|
||||||
|
description="Analyze data",
|
||||||
|
expected_output="Analysis results",
|
||||||
|
agent=agent,
|
||||||
|
output_file="~/output.txt"
|
||||||
|
)
|
||||||
|
Crew(agents=[agent], tasks=[task]).kickoff()
|
||||||
|
|
||||||
|
# Test invalid template variable
|
||||||
|
with pytest.raises(ValueError, match="Invalid template variable"):
|
||||||
|
task = Task(
|
||||||
|
description="Analyze data",
|
||||||
|
expected_output="Analysis results",
|
||||||
|
agent=agent,
|
||||||
|
output_file="{invalid-name}/output.txt"
|
||||||
|
)
|
||||||
|
Crew(agents=[agent], tasks=[task]).kickoff()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.vcr(filter_headers=["authorization"])
|
@pytest.mark.vcr(filter_headers=["authorization"])
|
||||||
def test_manager_agent():
|
def test_manager_agent():
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
@@ -3125,4 +3256,4 @@ def test_multimodal_agent_live_image_analysis():
|
|||||||
# Verify we got a meaningful response
|
# Verify we got a meaningful response
|
||||||
assert isinstance(result.raw, str)
|
assert isinstance(result.raw, str)
|
||||||
assert len(result.raw) > 100 # Expecting a detailed analysis
|
assert len(result.raw) > 100 # Expecting a detailed analysis
|
||||||
assert "error" not in result.raw.lower() # No error messages in response
|
assert "error" not in result.raw.lower() # No error messages in response
|
||||||
|
|||||||
@@ -719,21 +719,24 @@ def test_interpolate_inputs():
|
|||||||
task = Task(
|
task = Task(
|
||||||
description="Give me a list of 5 interesting ideas about {topic} to explore for an article, what makes them unique and interesting.",
|
description="Give me a list of 5 interesting ideas about {topic} to explore for an article, what makes them unique and interesting.",
|
||||||
expected_output="Bullet point list of 5 interesting ideas about {topic}.",
|
expected_output="Bullet point list of 5 interesting ideas about {topic}.",
|
||||||
|
output_file="/tmp/{topic}/output_{date}.txt"
|
||||||
)
|
)
|
||||||
|
|
||||||
task.interpolate_inputs(inputs={"topic": "AI"})
|
task.interpolate_inputs(inputs={"topic": "AI", "date": "2024"})
|
||||||
assert (
|
assert (
|
||||||
task.description
|
task.description
|
||||||
== "Give me a list of 5 interesting ideas about AI to explore for an article, what makes them unique and interesting."
|
== "Give me a list of 5 interesting ideas about AI to explore for an article, what makes them unique and interesting."
|
||||||
)
|
)
|
||||||
assert task.expected_output == "Bullet point list of 5 interesting ideas about AI."
|
assert task.expected_output == "Bullet point list of 5 interesting ideas about AI."
|
||||||
|
assert task.output_file == "/tmp/AI/output_2024.txt"
|
||||||
|
|
||||||
task.interpolate_inputs(inputs={"topic": "ML"})
|
task.interpolate_inputs(inputs={"topic": "ML", "date": "2025"})
|
||||||
assert (
|
assert (
|
||||||
task.description
|
task.description
|
||||||
== "Give me a list of 5 interesting ideas about ML to explore for an article, what makes them unique and interesting."
|
== "Give me a list of 5 interesting ideas about ML to explore for an article, what makes them unique and interesting."
|
||||||
)
|
)
|
||||||
assert task.expected_output == "Bullet point list of 5 interesting ideas about ML."
|
assert task.expected_output == "Bullet point list of 5 interesting ideas about ML."
|
||||||
|
assert task.output_file == "/tmp/ML/output_2025.txt"
|
||||||
|
|
||||||
|
|
||||||
def test_interpolate_only():
|
def test_interpolate_only():
|
||||||
@@ -872,3 +875,61 @@ def test_key():
|
|||||||
assert (
|
assert (
|
||||||
task.key == hash
|
task.key == hash
|
||||||
), "The key should be the hash of the non-interpolated description."
|
), "The key should be the hash of the non-interpolated description."
|
||||||
|
|
||||||
|
|
||||||
|
def test_output_file_validation():
|
||||||
|
"""Test output file path validation."""
|
||||||
|
# Valid paths
|
||||||
|
assert Task(
|
||||||
|
description="Test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
output_file="output.txt"
|
||||||
|
).output_file == "output.txt"
|
||||||
|
assert Task(
|
||||||
|
description="Test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
output_file="/tmp/output.txt"
|
||||||
|
).output_file == "tmp/output.txt"
|
||||||
|
assert Task(
|
||||||
|
description="Test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
output_file="{dir}/output_{date}.txt"
|
||||||
|
).output_file == "{dir}/output_{date}.txt"
|
||||||
|
|
||||||
|
# Invalid paths
|
||||||
|
with pytest.raises(ValueError, match="Path traversal"):
|
||||||
|
Task(
|
||||||
|
description="Test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
output_file="../output.txt"
|
||||||
|
)
|
||||||
|
with pytest.raises(ValueError, match="Path traversal"):
|
||||||
|
Task(
|
||||||
|
description="Test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
output_file="folder/../output.txt"
|
||||||
|
)
|
||||||
|
with pytest.raises(ValueError, match="Shell special characters"):
|
||||||
|
Task(
|
||||||
|
description="Test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
output_file="output.txt | rm -rf /"
|
||||||
|
)
|
||||||
|
with pytest.raises(ValueError, match="Shell expansion"):
|
||||||
|
Task(
|
||||||
|
description="Test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
output_file="~/output.txt"
|
||||||
|
)
|
||||||
|
with pytest.raises(ValueError, match="Shell expansion"):
|
||||||
|
Task(
|
||||||
|
description="Test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
output_file="$HOME/output.txt"
|
||||||
|
)
|
||||||
|
with pytest.raises(ValueError, match="Invalid template variable"):
|
||||||
|
Task(
|
||||||
|
description="Test task",
|
||||||
|
expected_output="Test output",
|
||||||
|
output_file="{invalid-name}/output.txt"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user