mirror of
https://github.com/crewAIInc/crewAI.git
synced 2025-12-16 04:18:35 +00:00
Add inject_date flag to Agent for automatic date injection (#2870)
* feat: Add inject_date flag to Agent for automatic date injection Co-Authored-By: Joe Moura <joao@crewai.com> * feat: Add date_format parameter and error handling to inject_date feature Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Update test implementation for inject_date feature Co-Authored-By: Joe Moura <joao@crewai.com> * fix: Add date format validation to prevent invalid formats Co-Authored-By: Joe Moura <joao@crewai.com> * docs: Update documentation for inject_date feature Co-Authored-By: Joe Moura <joao@crewai.com> * unnecesary * new tests --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com> Co-authored-by: João Moura <joaomdmoura@gmail.com>
This commit is contained in:
committed by
GitHub
parent
9945da7dbe
commit
c1672613bc
@@ -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)
|
||||
|
||||
@@ -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
227
tests/cassettes/test_inject_date.yaml
Normal file
227
tests/cassettes/test_inject_date.yaml
Normal file
File diff suppressed because one or more lines are too long
232
tests/cassettes/test_inject_date_custom_format.yaml
Normal file
232
tests/cassettes/test_inject_date_custom_format.yaml
Normal file
File diff suppressed because one or more lines are too long
226
tests/cassettes/test_no_inject_date.yaml
Normal file
226
tests/cassettes/test_no_inject_date.yaml
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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
|
||||
|
||||
@@ -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")],
|
||||
}
|
||||
|
||||
@@ -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():
|
||||
|
||||
117
tests/test_agent_inject_date.py
Normal file
117
tests/test_agent_inject_date.py
Normal 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
|
||||
Reference in New Issue
Block a user