diff --git a/langfuse/_utils/prompt_cache.py b/langfuse/_utils/prompt_cache.py index e286dca49..b4632b11f 100644 --- a/langfuse/_utils/prompt_cache.py +++ b/langfuse/_utils/prompt_cache.py @@ -27,11 +27,13 @@ def __init__(self, prompt: PromptClient, ttl_seconds: int): self._expiry = ttl_seconds + self.get_epoch_seconds() def is_expired(self) -> bool: - return self.get_epoch_seconds() > self._expiry + """Return whether the full cache lifetime has elapsed.""" + return self.get_epoch_seconds() >= self._expiry @staticmethod - def get_epoch_seconds() -> int: - return int(datetime.now().timestamp()) + def get_epoch_seconds() -> float: + """Keep fractional seconds so expiry reflects the actual insertion time.""" + return datetime.now().timestamp() class PromptCacheRefreshConsumer(Thread): diff --git a/tests/unit/test_prompt.py b/tests/unit/test_prompt.py index eadfb8221..33724784a 100644 --- a/tests/unit/test_prompt.py +++ b/tests/unit/test_prompt.py @@ -748,3 +748,14 @@ def test_get_fresh_prompt_when_version_changes(langfuse: Langfuse): result_call_2 = langfuse.get_prompt(prompt_name, version=2) assert mock_server_call.call_count == 2 assert result_call_2 == version_changed_prompt_client + + +def test_prompt_cache_item_expires_at_exact_ttl_without_rounding(): + """A cached prompt stays fresh only until its full TTL has elapsed.""" + with patch("langfuse._utils.prompt_cache.datetime") as clock: + clock.now.return_value.timestamp.return_value = 100.75 + item = PromptCacheItem(prompt=Mock(), ttl_seconds=1) + clock.now.return_value.timestamp.return_value = 101.70 + assert not item.is_expired() + clock.now.return_value.timestamp.return_value = 101.75 + assert item.is_expired()