-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add current date in UTC to PromptBuilder #8233
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
4683c7c
initial commit
medsriha e24bce4
add unit tests
medsriha 6b7d769
add release notes
medsriha 1c9f334
update function name
medsriha 60e27be
Merge branch 'main' into current_date_template
medsriha 3e58425
Merge branch 'main' into current_date_template
medsriha a304aa5
Merge branch 'main' into current_date_template
medsriha 3a7aa52
replace utc_now with an extension
medsriha 98002cf
add header
medsriha 664ffbf
update comments
medsriha 424c13e
fix error with timezone
medsriha cc5b784
set default timezone to UTC and fix lint
medsriha bf9a61c
set default timezone to UTC
medsriha 014df2f
update constructor
medsriha f234be2
revert back to removing default timezone
medsriha 53deff1
reduce redundancy
medsriha File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai> | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
from typing import Any, List, Optional, Union | ||
|
||
import arrow | ||
from jinja2 import Environment, nodes | ||
from jinja2.ext import Extension | ||
|
||
|
||
class Jinja2TimeExtension(Extension): | ||
# Syntax for current date | ||
tags = {"now"} | ||
|
||
def __init__(self, environment: Environment): # pylint: disable=useless-parent-delegation | ||
""" | ||
Initializes the JinjaTimeExtension object. | ||
|
||
:param environment: The Jinja2 environment to initialize the extension with. | ||
It provides the context where the extension will operate. | ||
""" | ||
super().__init__(environment) | ||
|
||
@staticmethod | ||
def _get_datetime( | ||
timezone: str, | ||
operator: Optional[str] = None, | ||
offset: Optional[str] = None, | ||
datetime_format: Optional[str] = None, | ||
) -> str: | ||
""" | ||
Get the current datetime based on timezone, apply any offset if provided, and format the result. | ||
|
||
:param timezone: The timezone string (e.g., 'UTC' or 'America/New_York') for which the current | ||
time should be fetched. | ||
:param operator: The operator ('+' or '-') to apply to the offset (used for adding/subtracting intervals). | ||
Defaults to None if no offset is applied, otherwise default is '+'. | ||
:param offset: The offset string in the format 'interval=value' (e.g., 'hours=2,days=1') specifying how much | ||
to adjust the datetime. The intervals can be any valid interval accepted | ||
by Arrow (e.g., hours, days, weeks, months). Defaults to None if no adjustment is needed. | ||
:param datetime_format: The format string to use for formatting the output datetime. | ||
Defaults to '%Y-%m-%d %H:%M:%S' if not provided. | ||
""" | ||
try: | ||
dt = arrow.now(timezone) | ||
except Exception as e: | ||
raise ValueError(f"Invalid timezone {timezone}: {e}") | ||
|
||
if offset and operator: | ||
try: | ||
# Parse the offset and apply it to the datetime object | ||
replace_params = { | ||
interval.strip(): float(operator + value.strip()) | ||
for param in offset.split(",") | ||
for interval, value in [param.split("=")] | ||
} | ||
# Shift the datetime fields based on the parsed offset | ||
dt = dt.shift(**replace_params) | ||
except (ValueError, AttributeError) as e: | ||
raise ValueError(f"Invalid offset or operator {offset}, {operator}: {e}") | ||
|
||
# Use the provided format or fallback to the default one | ||
datetime_format = datetime_format or "%Y-%m-%d %H:%M:%S" | ||
|
||
return dt.strftime(datetime_format) | ||
|
||
def parse(self, parser: Any) -> Union[nodes.Node, List[nodes.Node]]: | ||
""" | ||
Parse the template expression to determine how to handle the datetime formatting. | ||
|
||
:param parser: The parser object that processes the template expressions and manages the syntax tree. | ||
It's used to interpret the template's structure. | ||
""" | ||
lineno = next(parser.stream).lineno | ||
node = parser.parse_expression() | ||
# Check if a custom datetime format is provided after a comma | ||
datetime_format = parser.parse_expression() if parser.stream.skip_if("comma") else nodes.Const(None) | ||
|
||
# Default Add when no operator is provided | ||
operator = "+" if isinstance(node, nodes.Add) else "-" | ||
# Call the _get_datetime method with the appropriate operator and offset, if exist | ||
call_method = self.call_method( | ||
"_get_datetime", | ||
[node.left, nodes.Const(operator), node.right, datetime_format] | ||
if isinstance(node, (nodes.Add, nodes.Sub)) | ||
else [node, nodes.Const(None), nodes.Const(None), datetime_format], | ||
lineno=lineno, | ||
) | ||
|
||
return nodes.Output([call_method], lineno=lineno) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
releasenotes/notes/add-current-date-promptbuilder-ff60c846f5a70dc6.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
enhancements: | ||
- | | ||
Add the current date inside a template in `PromptBuilder` using the `utc_now()`. | ||
Users can also specify the date format, such as `utc_now("%Y-%m-%d")`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai> | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import pytest | ||
from jinja2 import Environment | ||
import arrow | ||
from haystack.utils import Jinja2TimeExtension | ||
|
||
|
||
class TestJinja2TimeExtension: | ||
@pytest.fixture | ||
def jinja_env(self) -> Environment: | ||
return Environment(extensions=[Jinja2TimeExtension]) | ||
|
||
@pytest.fixture | ||
def jinja_extension(self, jinja_env: Environment) -> Jinja2TimeExtension: | ||
return Jinja2TimeExtension(jinja_env) | ||
|
||
def test_valid_datetime(self, jinja_extension: Jinja2TimeExtension) -> None: | ||
result = jinja_extension._get_datetime( | ||
"UTC", operator="+", offset="hours=2", datetime_format="%Y-%m-%d %H:%M:%S" | ||
) | ||
assert isinstance(result, str) | ||
assert len(result) == 19 | ||
|
||
def test_parse_valid_expression(self, jinja_env: Environment) -> None: | ||
template = "{% now 'UTC' + 'hours=2', '%Y-%m-%d %H:%M:%S' %}" | ||
result = jinja_env.from_string(template).render() | ||
assert isinstance(result, str) | ||
assert len(result) == 19 | ||
|
||
def test_get_datetime_no_offset(self, jinja_extension: Jinja2TimeExtension) -> None: | ||
result = jinja_extension._get_datetime("UTC") | ||
expected = arrow.now("UTC").strftime("%Y-%m-%d %H:%M:%S") | ||
assert result == expected | ||
|
||
def test_get_datetime_with_offset_add(self, jinja_extension: Jinja2TimeExtension) -> None: | ||
result = jinja_extension._get_datetime("UTC", operator="+", offset="hours=1") | ||
expected = arrow.now("UTC").shift(hours=1).strftime("%Y-%m-%d %H:%M:%S") | ||
assert result == expected | ||
|
||
def test_get_datetime_with_offset_subtract(self, jinja_extension: Jinja2TimeExtension) -> None: | ||
result = jinja_extension._get_datetime("UTC", operator="-", offset="days=1") | ||
expected = arrow.now("UTC").shift(days=-1).strftime("%Y-%m-%d %H:%M:%S") | ||
assert result == expected | ||
|
||
def test_get_datetime_with_offset_subtract_days_hours(self, jinja_extension: Jinja2TimeExtension) -> None: | ||
result = jinja_extension._get_datetime("UTC", operator="-", offset="days=1, hours=2") | ||
expected = arrow.now("UTC").shift(days=-1, hours=-2).strftime("%Y-%m-%d %H:%M:%S") | ||
assert result == expected | ||
|
||
def test_get_datetime_with_custom_format(self, jinja_extension: Jinja2TimeExtension) -> None: | ||
result = jinja_extension._get_datetime("UTC", datetime_format="%d-%m-%Y") | ||
expected = arrow.now("UTC").strftime("%d-%m-%Y") | ||
assert result == expected | ||
|
||
def test_get_datetime_new_york_timezone(self, jinja_env: Environment) -> None: | ||
template = jinja_env.from_string("{% now 'America/New_York' %}") | ||
result = template.render() | ||
expected = arrow.now("America/New_York").strftime("%Y-%m-%d %H:%M:%S") | ||
assert result == expected | ||
|
||
def test_parse_no_operator(self, jinja_env: Environment) -> None: | ||
template = jinja_env.from_string("{% now 'UTC' %}") | ||
result = template.render() | ||
expected = arrow.now("UTC").strftime("%Y-%m-%d %H:%M:%S") | ||
assert result == expected | ||
|
||
def test_parse_with_add(self, jinja_env: Environment) -> None: | ||
template = jinja_env.from_string("{% now 'UTC' + 'hours=2' %}") | ||
result = template.render() | ||
expected = arrow.now("UTC").shift(hours=2).strftime("%Y-%m-%d %H:%M:%S") | ||
assert result == expected | ||
|
||
def test_parse_with_subtract(self, jinja_env: Environment) -> None: | ||
template = jinja_env.from_string("{% now 'UTC' - 'days=1' %}") | ||
result = template.render() | ||
expected = arrow.now("UTC").shift(days=-1).strftime("%Y-%m-%d %H:%M:%S") | ||
assert result == expected | ||
|
||
def test_parse_with_custom_format(self, jinja_env: Environment) -> None: | ||
template = jinja_env.from_string("{% now 'UTC', '%d-%m-%Y' %}") | ||
result = template.render() | ||
expected = arrow.now("UTC").strftime("%d-%m-%Y") | ||
assert result == expected | ||
|
||
def test_default_format(self, jinja_env: Environment) -> None: | ||
template = jinja_env.from_string("{% now 'UTC'%}") | ||
result = template.render() | ||
expected = arrow.now("UTC").strftime("%Y-%m-%d %H:%M:%S") # default format | ||
assert result == expected | ||
|
||
def test_invalid_timezone(self, jinja_extension: Jinja2TimeExtension) -> None: | ||
with pytest.raises(ValueError, match="Invalid timezone"): | ||
jinja_extension._get_datetime("Invalid/Timezone") | ||
|
||
def test_invalid_offset(self, jinja_extension: Jinja2TimeExtension) -> None: | ||
with pytest.raises(ValueError, match="Invalid offset or operator"): | ||
jinja_extension._get_datetime("UTC", operator="+", offset="invalid_format") | ||
|
||
def test_invalid_operator(self, jinja_extension: Jinja2TimeExtension) -> None: | ||
with pytest.raises(ValueError, match="Invalid offset or operator"): | ||
jinja_extension._get_datetime("UTC", operator="*", offset="hours=2") |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this read "# Default Minus when ..." since the else statement is
"-"
?