mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-07 15:18:29 +00:00
Compare commits
15 Commits
fix/base-f
...
pr-1762
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f26833f751 | ||
|
|
5fe15a8dba | ||
|
|
d8f5a9fb71 | ||
|
|
55883c6083 | ||
|
|
072f0cbef6 | ||
|
|
7beb511206 | ||
|
|
5b2e41b8eb | ||
|
|
e6f620877d | ||
|
|
43cb2d1f66 | ||
|
|
4e9b70201e | ||
|
|
059b0cf5b4 | ||
|
|
652ddcc1c5 | ||
|
|
964d4bfdbf | ||
|
|
c103d7eab7 | ||
|
|
4fe9f5d8bd |
@@ -1,13 +1,25 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import uuid
|
import uuid
|
||||||
from concurrent.futures import Future
|
from concurrent.futures import Future
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
ClassVar,
|
||||||
|
Dict,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Set,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
|
Union,
|
||||||
|
)
|
||||||
|
|
||||||
from opentelemetry.trace import Span
|
from opentelemetry.trace import Span
|
||||||
from pydantic import (
|
from pydantic import (
|
||||||
@@ -51,6 +63,7 @@ class Task(BaseModel):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__hash__ = object.__hash__ # type: ignore
|
__hash__ = object.__hash__ # type: ignore
|
||||||
|
logger: ClassVar[logging.Logger] = logging.getLogger(__name__)
|
||||||
used_tools: int = 0
|
used_tools: int = 0
|
||||||
tools_errors: int = 0
|
tools_errors: int = 0
|
||||||
delegations: int = 0
|
delegations: int = 0
|
||||||
@@ -389,7 +402,18 @@ class Task(BaseModel):
|
|||||||
|
|
||||||
if inputs:
|
if inputs:
|
||||||
self.description = self._original_description.format(**inputs)
|
self.description = self._original_description.format(**inputs)
|
||||||
self.expected_output = self._original_expected_output.format(**inputs)
|
self.expected_output = self.interpolate_only(
|
||||||
|
input_string=self._original_expected_output, inputs=inputs
|
||||||
|
)
|
||||||
|
|
||||||
|
def interpolate_only(self, input_string: str, inputs: Dict[str, Any]) -> str:
|
||||||
|
"""Interpolate placeholders (e.g., {key}) in a string while leaving JSON untouched."""
|
||||||
|
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)
|
||||||
|
|
||||||
def increment_tools_errors(self) -> None:
|
def increment_tools_errors(self) -> None:
|
||||||
"""Increment the tools errors counter."""
|
"""Increment the tools errors counter."""
|
||||||
@@ -471,22 +495,33 @@ class Task(BaseModel):
|
|||||||
return OutputFormat.RAW
|
return OutputFormat.RAW
|
||||||
|
|
||||||
def _save_file(self, result: Any) -> None:
|
def _save_file(self, result: Any) -> None:
|
||||||
|
"""Save task output to a file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
result: The result to save to the file. Can be a dict or any stringifiable object.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If output_file is not set
|
||||||
|
RuntimeError: If there is an error writing to the file
|
||||||
|
"""
|
||||||
if self.output_file is None:
|
if self.output_file is None:
|
||||||
raise ValueError("output_file is not set.")
|
raise ValueError("output_file is not set.")
|
||||||
|
|
||||||
resolved_path = Path(self.output_file).expanduser().resolve()
|
try:
|
||||||
directory = resolved_path.parent
|
resolved_path = Path(self.output_file).expanduser().resolve()
|
||||||
|
directory = resolved_path.parent
|
||||||
|
|
||||||
if not directory.exists():
|
if not directory.exists():
|
||||||
directory.mkdir(parents=True, exist_ok=True)
|
directory.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with resolved_path.open("w", encoding="utf-8") as file:
|
with resolved_path.open("w", encoding="utf-8") as file:
|
||||||
if isinstance(result, dict):
|
if isinstance(result, dict):
|
||||||
import json
|
import json
|
||||||
|
json.dump(result, file, ensure_ascii=False, indent=2)
|
||||||
json.dump(result, file, ensure_ascii=False, indent=2)
|
else:
|
||||||
else:
|
file.write(str(result))
|
||||||
file.write(str(result))
|
except (OSError, IOError) as e:
|
||||||
|
raise RuntimeError(f"Failed to save output file: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|||||||
@@ -736,6 +736,48 @@ def test_interpolate_inputs():
|
|||||||
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."
|
||||||
|
|
||||||
|
|
||||||
|
def test_interpolate_only():
|
||||||
|
"""Test the interpolate_only method for various scenarios including JSON structure preservation."""
|
||||||
|
task = Task(
|
||||||
|
description="Unused in this test",
|
||||||
|
expected_output="Unused in this test"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test JSON structure preservation
|
||||||
|
json_string = '{"info": "Look at {placeholder}", "nested": {"val": "{nestedVal}"}}'
|
||||||
|
result = task.interpolate_only(
|
||||||
|
input_string=json_string,
|
||||||
|
inputs={"placeholder": "the data", "nestedVal": "something else"}
|
||||||
|
)
|
||||||
|
assert '"info": "Look at the data"' in result
|
||||||
|
assert '"val": "something else"' in result
|
||||||
|
assert "{placeholder}" not in result
|
||||||
|
assert "{nestedVal}" not in result
|
||||||
|
|
||||||
|
# Test normal string interpolation
|
||||||
|
normal_string = "Hello {name}, welcome to {place}!"
|
||||||
|
result = task.interpolate_only(
|
||||||
|
input_string=normal_string,
|
||||||
|
inputs={"name": "John", "place": "CrewAI"}
|
||||||
|
)
|
||||||
|
assert result == "Hello John, welcome to CrewAI!"
|
||||||
|
|
||||||
|
# Test empty string
|
||||||
|
result = task.interpolate_only(
|
||||||
|
input_string="",
|
||||||
|
inputs={"unused": "value"}
|
||||||
|
)
|
||||||
|
assert result == ""
|
||||||
|
|
||||||
|
# Test string with no placeholders
|
||||||
|
no_placeholders = "Hello, this is a test"
|
||||||
|
result = task.interpolate_only(
|
||||||
|
input_string=no_placeholders,
|
||||||
|
inputs={"unused": "value"}
|
||||||
|
)
|
||||||
|
assert result == no_placeholders
|
||||||
|
|
||||||
|
|
||||||
def test_task_output_str_with_pydantic():
|
def test_task_output_str_with_pydantic():
|
||||||
from crewai.tasks.output_format import OutputFormat
|
from crewai.tasks.output_format import OutputFormat
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user