From dda445924542c1434952ba4a9f6fed511d8cc52c Mon Sep 17 00:00:00 2001 From: Matthew Zhou Date: Thu, 4 Sep 2025 15:26:49 -0700 Subject: [PATCH] feat: Make end date inclusive on conversation search (#4431) Make end date inclusive --- letta/functions/function_sets/base.py | 15 ++++++++++++--- letta/helpers/tpuf_client.py | 18 ++++++++++++++++-- letta/services/message_manager.py | 2 +- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/letta/functions/function_sets/base.py b/letta/functions/function_sets/base.py index cccd5ab5..d077fe47 100644 --- a/letta/functions/function_sets/base.py +++ b/letta/functions/function_sets/base.py @@ -35,8 +35,8 @@ def conversation_search( query (str): String to search for using both text matching and semantic similarity. roles (Optional[List[Literal["assistant", "user", "tool"]]]): Optional list of message roles to filter by. limit (Optional[int]): Maximum number of results to return. Uses system default if not specified. - start_date (Optional[str]): Filter results to messages created after this date. ISO 8601 format: "YYYY-MM-DD" or "YYYY-MM-DDTHH:MM". Examples: "2024-01-15", "2024-01-15T14:30". - end_date (Optional[str]): Filter results to messages created before this date. ISO 8601 format: "YYYY-MM-DD" or "YYYY-MM-DDTHH:MM". Examples: "2024-01-20", "2024-01-20T17:00". + start_date (Optional[str]): Filter results to messages created on or after this date (INCLUSIVE). When using date-only format (e.g., "2024-01-15"), includes messages starting from 00:00:00 of that day. ISO 8601 format: "YYYY-MM-DD" or "YYYY-MM-DDTHH:MM". Examples: "2024-01-15" (from start of Jan 15), "2024-01-15T14:30" (from 2:30 PM on Jan 15). + end_date (Optional[str]): Filter results to messages created on or before this date (INCLUSIVE). When using date-only format (e.g., "2024-01-20"), includes all messages from that entire day. ISO 8601 format: "YYYY-MM-DD" or "YYYY-MM-DDTHH:MM". Examples: "2024-01-20" (includes all of Jan 20), "2024-01-20T17:00" (up to 5 PM on Jan 20). Examples: # Search all messages @@ -45,8 +45,17 @@ def conversation_search( # Search only assistant messages conversation_search(query="error handling", roles=["assistant"]) - # Search with date range + # Search with date range (inclusive of both dates) conversation_search(query="meetings", start_date="2024-01-15", end_date="2024-01-20") + # This includes all messages from Jan 15 00:00:00 through Jan 20 23:59:59 + + # Search messages from a specific day (inclusive) + conversation_search(query="bug reports", start_date="2024-09-04", end_date="2024-09-04") + # This includes ALL messages from September 4, 2024 + + # Search with specific time boundaries + conversation_search(query="deployment", start_date="2024-01-15T09:00", end_date="2024-01-15T17:30") + # This includes messages from 9 AM to 5:30 PM on Jan 15 # Search with limit conversation_search(query="debugging", limit=10) diff --git a/letta/helpers/tpuf_client.py b/letta/helpers/tpuf_client.py index 72e4ed55..0dc1a8be 100644 --- a/letta/helpers/tpuf_client.py +++ b/letta/helpers/tpuf_client.py @@ -406,7 +406,7 @@ class TurbopufferClient: vector_weight: Weight for vector search results in hybrid mode (default: 0.5) fts_weight: Weight for FTS results in hybrid mode (default: 0.5) start_date: Optional datetime to filter passages created after this date - end_date: Optional datetime to filter passages created before this date + end_date: Optional datetime to filter passages created on or before this date (inclusive) Returns: List of (passage, score, metadata) tuples with relevance rankings @@ -439,6 +439,13 @@ class TurbopufferClient: if start_date: date_filters.append(("created_at", "Gte", start_date)) if end_date: + # if end_date has no time component (is at midnight), adjust to end of day + # to make the filter inclusive of the entire day + if end_date.hour == 0 and end_date.minute == 0 and end_date.second == 0 and end_date.microsecond == 0: + from datetime import timedelta + + # add 1 day and subtract 1 microsecond to get 23:59:59.999999 + end_date = end_date + timedelta(days=1) - timedelta(microseconds=1) date_filters.append(("created_at", "Lte", end_date)) # combine all filters @@ -531,7 +538,7 @@ class TurbopufferClient: vector_weight: Weight for vector search results in hybrid mode (default: 0.5) fts_weight: Weight for FTS results in hybrid mode (default: 0.5) start_date: Optional datetime to filter messages created after this date - end_date: Optional datetime to filter messages created before this date + end_date: Optional datetime to filter messages created on or before this date (inclusive) Returns: List of (message_dict, score, metadata) tuples where: @@ -563,6 +570,13 @@ class TurbopufferClient: if start_date: date_filters.append(("created_at", "Gte", start_date)) if end_date: + # if end_date has no time component (is at midnight), adjust to end of day + # to make the filter inclusive of the entire day + if end_date.hour == 0 and end_date.minute == 0 and end_date.second == 0 and end_date.microsecond == 0: + from datetime import timedelta + + # add 1 day and subtract 1 microsecond to get 23:59:59.999999 + end_date = end_date + timedelta(days=1) - timedelta(microseconds=1) date_filters.append(("created_at", "Lte", end_date)) # combine all filters diff --git a/letta/services/message_manager.py b/letta/services/message_manager.py index 57cf7cc5..13df051d 100644 --- a/letta/services/message_manager.py +++ b/letta/services/message_manager.py @@ -1126,7 +1126,7 @@ class MessageManager: roles: Optional list of message roles to filter by limit: Maximum number of results to return start_date: Optional filter for messages created after this date - end_date: Optional filter for messages created before this date + end_date: Optional filter for messages created on or before this date (inclusive) embedding_config: Optional embedding configuration for generating query embedding Returns: