Compare commits

...

7 Commits

Author SHA1 Message Date
João Moura
10b5082c0a new tests 2025-05-21 04:12:55 -07:00
João Moura
bddfe1c780 unnecesary 2025-05-21 03:13:14 -07:00
Devin AI
c3dc839b12 docs: Update documentation for inject_date feature
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-05-21 04:03:52 +00:00
Devin AI
270a473d5d fix: Add date format validation to prevent invalid formats
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-05-21 03:44:51 +00:00
Devin AI
98df434eb9 fix: Update test implementation for inject_date feature
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-05-21 03:40:31 +00:00
Devin AI
9973011be5 feat: Add date_format parameter and error handling to inject_date feature
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-05-21 03:30:20 +00:00
Devin AI
547e46b8cf feat: Add inject_date flag to Agent for automatic date injection
Co-Authored-By: Joe Moura <joao@crewai.com>
2025-05-21 03:24:06 +00:00
11 changed files with 1277 additions and 0 deletions

View File

@@ -58,6 +58,8 @@ The Visual Agent Builder enables:
| **Embedder** _(optional)_ | `embedder` | `Optional[Dict[str, Any]]` | Configuration for the embedder used by the agent. |
| **Knowledge Sources** _(optional)_ | `knowledge_sources` | `Optional[List[BaseKnowledgeSource]]` | Knowledge sources available to the agent. |
| **Use System Prompt** _(optional)_ | `use_system_prompt` | `Optional[bool]` | Whether to use system prompt (for o1 model support). Default is True. |
| **Inject Date** _(optional)_ | `inject_date` | `bool` | Whether to automatically inject the current date into tasks. Default is False. |
| **Date Format** _(optional)_ | `date_format` | `str` | Format string for date when inject_date is enabled. Default is "%Y-%m-%d" (ISO format). |
## Creating Agents
@@ -226,6 +228,18 @@ custom_agent = Agent(
)
```
#### Date-Aware Agent
```python Code
date_aware_agent = Agent(
role="Market Analyst",
goal="Track market movements with precise date references",
backstory="Expert in time-sensitive financial analysis and reporting",
inject_date=True, # Automatically inject current date into tasks
date_format="%B %d, %Y", # Format as "May 21, 2025"
verbose=True
)
```
### Parameter Details
#### Critical Parameters
@@ -332,6 +346,12 @@ When `memory` is enabled, the agent will maintain context across multiple intera
- Main `llm` for complex reasoning
- `function_calling_llm` for efficient tool usage
### Date Awareness
- Use `inject_date: true` to provide agents with current date awareness
- Customize the date format with `date_format` using standard Python datetime format codes
- Valid format codes include: %Y (year), %m (month), %d (day), %B (full month name), etc.
- Invalid date formats will be logged as warnings and will not modify the task description
### Model Compatibility
- Set `use_system_prompt: false` for older models that don't support system messages
- Ensure your chosen `llm` supports the features you need (like function calling)

View File

@@ -115,6 +115,14 @@ class Agent(BaseAgent):
default=False,
description="Whether the agent is multimodal.",
)
inject_date: bool = Field(
default=False,
description="Whether to automatically inject the current date into tasks.",
)
date_format: str = Field(
default="%Y-%m-%d",
description="Format string for date when inject_date is enabled.",
)
code_execution_mode: Literal["safe", "unsafe"] = Field(
default="safe",
description="Mode for code execution: 'safe' (using Docker) or 'unsafe' (direct execution).",
@@ -248,6 +256,8 @@ class Agent(BaseAgent):
else:
print(f"Error during reasoning process: {str(e)}")
self._inject_date_to_task(task)
if self.tools_handler:
self.tools_handler.last_used_tool = {} # type: ignore # Incompatible types in assignment (expression has type "dict[Never, Never]", variable has type "ToolCalling")
@@ -608,6 +618,26 @@ class Agent(BaseAgent):
return description
def _inject_date_to_task(self, task):
"""Inject the current date into the task description if inject_date is enabled."""
if self.inject_date:
from datetime import datetime
try:
valid_format_codes = ['%Y', '%m', '%d', '%H', '%M', '%S', '%B', '%b', '%A', '%a']
is_valid = any(code in self.date_format for code in valid_format_codes)
if not is_valid:
raise ValueError(f"Invalid date format: {self.date_format}")
current_date: str = datetime.now().strftime(self.date_format)
task.description += f"\n\nCurrent Date: {current_date}"
except Exception as e:
if hasattr(self, '_logger'):
self._logger.log("warning", f"Failed to inject date: {str(e)}")
else:
print(f"Warning: Failed to inject date: {str(e)}")
def _validate_docker_installation(self) -> None:
"""Check if Docker is installed and running."""
if not shutil.which("docker"):

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -78,4 +78,134 @@ interactions:
status:
code: 200
message: OK
- request:
body: '{"messages": [{"role": "system", "content": "You are Researcher. You''re
an expert researcher, specialized in technology, software engineering, AI and
startups. You work as a freelancer and are now working on doing research and
analysis for a new customer.\nYour personal goal is: Make the best research
and analysis on content about AI and AI agents. Use the tool provided to you.\nYou
ONLY have access to the following tools, and should NEVER make up tools that
are not listed here:\n\nTool Name: what amazing tool\nTool Arguments: {}\nTool
Description: My tool\n\nIMPORTANT: Use the following format in your response:\n\n```\nThought:
you should always think about what to do\nAction: the action to take, only one
name of [what amazing tool], just the name, exactly as it''s written.\nAction
Input: the input to the action, just a simple JSON object, enclosed in curly
braces, using \" to wrap keys and values.\nObservation: the result of the action\n```\n\nOnce
all necessary information is gathered, return the following format:\n\n```\nThought:
I now know the final answer\nFinal Answer: the final answer to the original
input question\n```"}, {"role": "user", "content": "\nCurrent Task: Give me
a list of 5 interesting ideas to explore for an article, what makes them unique
and interesting.\n\nThis is the expected criteria for your final answer: Bullet
point list of 5 interesting ideas.\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:"]}'
headers:
accept:
- application/json
accept-encoding:
- gzip, deflate
connection:
- keep-alive
content-length:
- '1666'
content-type:
- application/json
cookie:
- _cfuvid=CW_cKQGYWY3cL.S6Xo5z0cmkmWHy5Q50OA_KjPEijNk-1735926034530-0.0.1.1-604800000
host:
- api.openai.com
user-agent:
- OpenAI/Python 1.68.2
x-stainless-arch:
- arm64
x-stainless-async:
- 'false'
x-stainless-lang:
- python
x-stainless-os:
- MacOS
x-stainless-package-version:
- 1.68.2
x-stainless-raw-response:
- 'true'
x-stainless-read-timeout:
- '600.0'
x-stainless-retry-count:
- '0'
x-stainless-runtime:
- CPython
x-stainless-runtime-version:
- 3.11.7
method: POST
uri: https://api.openai.com/v1/chat/completions
response:
body:
string: !!binary |
H4sIAAAAAAAAAwAAAP//jJNPb9swDMXv+RSELrskRZukSZNbBxRbUWwYdhmwtTAYiba5ypQn0f2z
It+9kNPW6dYBu9iAfnzP5BP9MAIw7MwajK1RbdP6yfvvG7r79PnDxYK/LPni5uO35uxstfp6fTvz
1oyzImx+ktVn1YENTetJOcgO20iolF2PlvPlyfR4NZ/1oAmOfJZVrU7mYdKw8GR6OJ1PDpeTo5Mn
dR3YUjJr+DECAHjon7lPcXRn1nA4fj5pKCWsyKxfigBMDD6fGEyJk6KoGQ/QBlGSvvVzECIHGqAi
oYhKgOA5KYQSWJQiJWWpAKOy9QTsCBNE8nm4rDs9BxTXvyoSTWMog+1S1gQBrYkjdMK/OhJKqa+N
5OkGxRKwgAaH9+8SKNlagg8VW/TgUVyy2NLBpVzKqc25ruG2RgVs8Hd21xA8wDOEc2k7XcPDFmB/
1khllzDnLZ33ewBFgmKW9ilfPZHtS64+VG0Mm/SH1JQsnOoiEqYgOcOkoTU93Y4Arvr7615diWlj
aFotNFxT/7nZdLHzM8PaDHR+9AQ1KPo91WI5fsOvcKTIPu1tgLFoa3KDdFgX7ByHPTDam/rvbt7y
3k3OUv2P/QCspVbJFW0kx/b1xENZpPxX/avsJeW+YZMo3rClQplivglHJXZ+t+sm3SelpihZKopt
5N3Cl21xPKfNfOMWq5kZbUePAAAA//8DALOFcXD+AwAA
headers:
CF-RAY:
- 9433a372ec1069e6-LAS
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Wed, 21 May 2025 11:12:24 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=SC.7rKr584CqggyyZVMEQ5_zFD.g4Q5djrKS1Kg2ifs-1747825944-1.0.1.1-M3vY0AX_JtRWZBGWsq8v4VWUTYLoQRB5_X2WbagS7emC73L80mIv3OUlMwPOwh7ag8LdkVtbkQ_hpAdM9kVJ_wyV7mhTNCoCPLE._sZWMeI;
path=/; expires=Wed, 21-May-25 11:42:24 GMT; domain=.api.openai.com; HttpOnly;
Secure; SameSite=None
- _cfuvid=LMbhtXYRu2foKMlmDSxZF0LlpAWtafPdjq_4PWulGz0-1747825944424-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
cf-cache-status:
- DYNAMIC
openai-organization:
- crewai-iuxna1
openai-processing-ms:
- '819'
openai-version:
- '2020-10-01'
strict-transport-security:
- max-age=31536000; includeSubDomains; preload
x-envoy-upstream-service-time:
- '827'
x-ratelimit-limit-requests:
- '30000'
x-ratelimit-limit-tokens:
- '150000000'
x-ratelimit-remaining-requests:
- '29999'
x-ratelimit-remaining-tokens:
- '149999620'
x-ratelimit-reset-requests:
- 2ms
x-ratelimit-reset-tokens:
- 0s
x-request-id:
- req_93e3b862779b855ab166d171911fa9e0
status:
code: 200
message: OK
version: 1

View File

@@ -46,4 +46,6 @@ def setup_test_environment():
def vcr_config(request) -> dict:
return {
"cassette_library_dir": "tests/cassettes",
"record_mode": "new_episodes",
"filter_headers": [("authorization", "AUTHORIZATION-XXX")],
}

View File

@@ -398,6 +398,79 @@ def test_output_json_hierarchical():
assert result.json == '{"score": 4}'
assert result.to_dict() == {"score": 4}
@pytest.mark.vcr(filter_headers=["authorization"])
def test_inject_date():
reporter = Agent(
role="Reporter",
goal="Report the date",
backstory="You're an expert reporter, specialized in reporting the date.",
allow_delegation=False,
inject_date=True,
)
task = Task(
description="What is the date today?",
expected_output="The date today as you were told, same format as the date you were told.",
agent=reporter,
)
crew = Crew(
agents=[reporter],
tasks=[task],
process=Process.sequential,
)
result = crew.kickoff()
assert "2025-05-21" in result.raw
@pytest.mark.vcr(filter_headers=["authorization"])
def test_inject_date_custom_format():
reporter = Agent(
role="Reporter",
goal="Report the date",
backstory="You're an expert reporter, specialized in reporting the date.",
allow_delegation=False,
inject_date=True,
date_format="%B %d, %Y",
)
task = Task(
description="What is the date today?",
expected_output="The date today.",
agent=reporter,
)
crew = Crew(
agents=[reporter],
tasks=[task],
process=Process.sequential,
)
result = crew.kickoff()
assert "May 21, 2025" in result.raw
@pytest.mark.vcr(filter_headers=["authorization"])
def test_no_inject_date():
reporter = Agent(
role="Reporter",
goal="Report the date",
backstory="You're an expert reporter, specialized in reporting the date.",
allow_delegation=False,
inject_date=False,
)
task = Task(
description="What is the date today?",
expected_output="The date today.",
agent=reporter,
)
crew = Crew(
agents=[reporter],
tasks=[task],
process=Process.sequential,
)
result = crew.kickoff()
assert "2025-05-21" not in result.raw
@pytest.mark.vcr(filter_headers=["authorization"])
def test_json_property_without_output_json():

View File

@@ -0,0 +1,117 @@
from datetime import datetime
from unittest.mock import patch
from crewai.agent import Agent
from crewai.task import Task
def test_agent_inject_date():
"""Test that the inject_date flag injects the current date into the task.
Tests that when inject_date=True, the current date is added to the task description.
"""
with patch('datetime.datetime') as mock_datetime:
mock_datetime.now.return_value = datetime(2025, 1, 1)
agent = Agent(
role="test_agent",
goal="test_goal",
backstory="test_backstory",
inject_date=True,
)
task = Task(
description="Test task",
expected_output="Test output",
agent=agent,
)
# Store original description
original_description = task.description
agent._inject_date_to_task(task)
assert "Current Date: 2025-01-01" in task.description
assert task.description != original_description
def test_agent_without_inject_date():
"""Test that without inject_date flag, no date is injected.
Tests that when inject_date=False (default), no date is added to the task description.
"""
agent = Agent(
role="test_agent",
goal="test_goal",
backstory="test_backstory",
# inject_date is False by default
)
task = Task(
description="Test task",
expected_output="Test output",
agent=agent,
)
original_description = task.description
agent._inject_date_to_task(task)
assert task.description == original_description
def test_agent_inject_date_custom_format():
"""Test that the inject_date flag with custom date_format works correctly.
Tests that when inject_date=True with a custom date_format, the date is formatted correctly.
"""
with patch('datetime.datetime') as mock_datetime:
mock_datetime.now.return_value = datetime(2025, 1, 1)
agent = Agent(
role="test_agent",
goal="test_goal",
backstory="test_backstory",
inject_date=True,
date_format="%d/%m/%Y",
)
task = Task(
description="Test task",
expected_output="Test output",
agent=agent,
)
# Store original description
original_description = task.description
agent._inject_date_to_task(task)
assert "Current Date: 01/01/2025" in task.description
assert task.description != original_description
def test_agent_inject_date_invalid_format():
"""Test error handling with invalid date format.
Tests that when an invalid date_format is provided, the task description remains unchanged.
"""
agent = Agent(
role="test_agent",
goal="test_goal",
backstory="test_backstory",
inject_date=True,
date_format="invalid",
)
task = Task(
description="Test task",
expected_output="Test output",
agent=agent,
)
original_description = task.description
agent._inject_date_to_task(task)
assert task.description == original_description