refactor: update date injection functionality in agents (#6850)

* refactor: update date injection functionality in agents

- Changed the description of the  parameter to clarify that it injects the current date into the agent's prompt instead of tasks.
- Removed the  method as it was no longer needed.
- Implemented a new  method in the  class to handle date injection directly into the prompt.
- Updated tests to ensure the date is correctly injected into the system prompt and user messages based on the  flag.

* translations

* nit
This commit is contained in:
Lorenze Jay
2026-08-11 10:36:47 -07:00
committed by GitHub
parent 11890e6701
commit 6c19669d63
7 changed files with 195 additions and 131 deletions

View File

@@ -6,7 +6,6 @@ import asyncio
from collections.abc import Callable, Coroutine, Sequence
import concurrent.futures
import contextvars
from datetime import datetime
import inspect
import json
import os
@@ -264,7 +263,7 @@ class Agent(BaseAgent):
)
inject_date: bool = Field(
default=False,
description="Whether to automatically inject the current date into tasks.",
description="Whether to automatically inject the current date into the agent's prompt.",
)
date_format: str = Field(
default="%Y-%m-%d",
@@ -545,7 +544,7 @@ class Agent(BaseAgent):
) -> str:
"""Prepare common setup for task execution shared by sync and async paths.
Handles reasoning, date injection, prompt building, and memory retrieval.
Handles reasoning, prompt building, and memory retrieval.
Args:
task: Task to execute.
@@ -556,8 +555,6 @@ class Agent(BaseAgent):
"""
get_env_context()
self._inject_date_to_task(task)
self.reset_tool_failures()
if self.tools_handler:
@@ -1332,32 +1329,6 @@ class Agent(BaseAgent):
]
)
def _inject_date_to_task(self, task: Task) -> None:
"""Inject the current date into the task description if inject_date is enabled."""
if self.inject_date:
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 = datetime.now().strftime(self.date_format)
task.description += f"\n\nCurrent Date: {current_date}"
except Exception as e:
self._logger.log("warning", f"Failed to inject date: {e!s}")
def _validate_docker_installation(self) -> None:
"""Deprecated: No-op. CodeInterpreterTool is no longer available."""
warnings.warn(

View File

@@ -2,6 +2,7 @@
from __future__ import annotations
from datetime import datetime
from typing import Any, Literal
from pydantic import BaseModel, Field
@@ -9,6 +10,20 @@ from pydantic import BaseModel, Field
from crewai.utilities.i18n import I18N_DEFAULT
VALID_DATE_FORMAT_CODES = (
"%Y",
"%m",
"%d",
"%H",
"%M",
"%S",
"%B",
"%b",
"%A",
"%a",
)
class StandardPromptResult(BaseModel):
"""Result with only prompt field for standard mode."""
@@ -87,7 +102,11 @@ class Prompts(BaseModel):
slices.append("tools")
else:
slices.append("no_tools")
system: str = self._build_prompt(slices) + self._build_skill_block()
system: str = (
self._build_prompt(slices)
+ self._build_skill_block()
+ self._build_date_block()
)
task_slice: COMPONENTS
if self.use_native_tool_calling:
@@ -106,7 +125,9 @@ class Prompts(BaseModel):
return SystemPromptResult(
system=system,
user=self._build_prompt([task_slice]),
prompt=self._build_prompt(slices) + self._build_skill_block(),
prompt=self._build_prompt(slices)
+ self._build_skill_block()
+ self._build_date_block(),
)
return StandardPromptResult(
prompt=self._build_prompt(
@@ -116,8 +137,31 @@ class Prompts(BaseModel):
self.response_template,
)
+ self._build_skill_block()
+ self._build_date_block()
)
def _build_date_block(self) -> str:
"""Render the current date when the agent has ``inject_date`` enabled.
Kept at the tail of the prompt so the stable prefix ahead of it stays
usable as a prompt-cache anchor.
"""
if not getattr(self.agent, "inject_date", False):
return ""
date_format = getattr(self.agent, "date_format", "%Y-%m-%d")
try:
if not any(code in date_format for code in VALID_DATE_FORMAT_CODES):
raise ValueError(f"Invalid date format: {date_format}")
current_date = datetime.now().strftime(date_format)
except Exception as e:
logger = getattr(self.agent, "_logger", None)
if logger is not None:
logger.log("warning", f"Failed to inject date: {e!s}")
return ""
return f"\n\nCurrent Date: {current_date}"
def _build_skill_block(self) -> str:
"""Render always-on instructions and the available skill catalog.

View File

@@ -1,116 +1,165 @@
"""Tests for the agent ``inject_date`` flag.
These assert against the messages the LLM actually receives, so they fail if the
date stops reaching the wire for either execution entry point: crew/task
execution via ``execute_task`` and standalone execution via ``kickoff``.
"""
from datetime import datetime
from typing import Any
from unittest.mock import patch
from crewai.agent import Agent
from crewai.llms.base_llm import BaseLLM
from crewai.task import Task
MOCK_TARGET = "crewai.agent.core.datetime"
MOCK_TARGET = "crewai.utilities.prompts.datetime"
FROZEN_NOW = datetime(2025, 1, 1)
def test_agent_inject_date():
"""Test that the inject_date flag injects the current date into the task.
class _RecordingLLM(BaseLLM):
"""Deterministic LLM that captures every message list it is handed."""
Tests that when inject_date=True, the current date is added to the task description.
"""
with patch(MOCK_TARGET) as mock_datetime:
mock_datetime.now.return_value = datetime(2025, 1, 1)
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self.calls: list[Any] = []
agent = Agent(
role="test_agent",
goal="test_goal",
backstory="test_backstory",
inject_date=True,
def call(self, messages: Any, **kwargs: Any) -> str:
self.calls.append(messages)
return "Thought: Done.\nFinal Answer: done"
def supports_function_calling(self) -> bool:
return False
def supports_stop_words(self) -> bool:
return False
def get_context_window_size(self) -> int:
return 8_192
def contents_for(self, role: str) -> list[str]:
"""Every message body sent under ``role`` across all calls."""
return [
str(message.get("content", ""))
for messages in self.calls
if not isinstance(messages, str)
for message in messages
if message.get("role") == role
]
@property
def everything_sent(self) -> str:
return "\n".join(
str(message.get("content", ""))
for messages in self.calls
if not isinstance(messages, str)
for message in messages
)
task = Task(
description="Test task",
expected_output="Test output",
agent=agent,
)
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(
def _agent(llm: BaseLLM, **kwargs: Any) -> Agent:
return Agent(
role="test_agent",
goal="test_goal",
backstory="test_backstory",
llm=llm,
max_iter=2,
**kwargs,
)
task = Task(
description="Test task",
expected_output="Test output",
def _task(agent: Agent) -> Task:
return Task(
description="What is the date today?",
expected_output="The date.",
agent=agent,
)
original_description = task.description
agent._inject_date_to_task(task)
def test_inject_date_reaches_system_prompt_on_kickoff() -> None:
"""Standalone ``kickoff`` must put the date in the system message."""
llm = _RecordingLLM(model="date-test")
agent = _agent(llm, inject_date=True)
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(MOCK_TARGET) as mock_datetime:
mock_datetime.now.return_value = datetime(2025, 1, 1)
mock_datetime.now.return_value = FROZEN_NOW
agent.kickoff("What is the date today?")
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,
)
original_description = task.description
agent._inject_date_to_task(task)
assert "Current Date: 01/01/2025" in task.description
assert task.description != original_description
system_prompts = llm.contents_for("system")
assert system_prompts
assert all("Current Date: 2025-01-01" in prompt for prompt in system_prompts)
def test_agent_inject_date_invalid_format():
"""Test error handling with invalid date format.
def test_inject_date_reaches_system_prompt_on_task_execution() -> None:
"""Crew/task execution must keep putting the date in front of the model."""
llm = _RecordingLLM(model="date-test")
agent = _agent(llm, inject_date=True)
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",
with patch(MOCK_TARGET) as mock_datetime:
mock_datetime.now.return_value = FROZEN_NOW
agent.execute_task(_task(agent))
system_prompts = llm.contents_for("system")
assert system_prompts
assert all("Current Date: 2025-01-01" in prompt for prompt in system_prompts)
def test_inject_date_reaches_prompt_without_system_prompt() -> None:
"""With ``use_system_prompt=False`` the whole prompt is one user message."""
llm = _RecordingLLM(model="date-test")
agent = _agent(llm, inject_date=True, use_system_prompt=False)
with patch(MOCK_TARGET) as mock_datetime:
mock_datetime.now.return_value = FROZEN_NOW
agent.kickoff("What is the date today?")
assert not llm.contents_for("system")
assert "Current Date: 2025-01-01" in llm.contents_for("user")[0]
def test_inject_date_custom_format() -> None:
llm = _RecordingLLM(model="date-test")
agent = _agent(llm, inject_date=True, date_format="%d/%m/%Y")
with patch(MOCK_TARGET) as mock_datetime:
mock_datetime.now.return_value = FROZEN_NOW
agent.kickoff("What is the date today?")
assert "Current Date: 01/01/2025" in llm.contents_for("system")[0]
def test_without_inject_date_no_date_is_sent() -> None:
llm = _RecordingLLM(model="date-test")
agent = _agent(llm)
agent.kickoff("What is the date today?")
assert llm.calls
assert "Current Date:" not in llm.everything_sent
def test_inject_date_invalid_format_is_skipped() -> None:
"""An unusable format should drop the date, not break execution."""
llm = _RecordingLLM(model="date-test")
agent = _agent(llm, inject_date=True, date_format="invalid")
output = agent.kickoff("What is the date today?")
assert output.raw == "done"
assert "Current Date:" not in llm.everything_sent
def test_inject_date_does_not_accumulate_across_runs() -> None:
"""Re-running the same task must not stack up repeated date lines."""
llm = _RecordingLLM(model="date-test")
agent = _agent(llm, inject_date=True)
task = _task(agent)
with patch(MOCK_TARGET) as mock_datetime:
mock_datetime.now.return_value = FROZEN_NOW
agent.execute_task(task)
agent.execute_task(task)
assert all(
prompt.count("Current Date:") == 1 for prompt in llm.contents_for("system")
)
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