You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Feature]: Date range picker on Knowledge page #2346
On /knowledge, users can search and filter by source, type, and owner, but not by when a document was created or ingested. Finding “files from last week” or “everything ingested in March” means paging through the grid.
Proposed Solution
Add a date-range picker to the Knowledge toolbar (next to KnowledgeSearchBar / the existing filter controls). Selecting a range should re-query the file list; clearing it should restore the unfiltered list.
Date field (decide in implementation, default below):
Default: created_time (OpenSearch date on chunks; already set by connectors).
Alternative: modified_time, or ingest time if we add/persist one for local uploads.
Inclusive range: start 00:00:00 local → end 23:59:59.999 local, sent to the API as UTC ISO-8601.
Documents with no timestamp should be excluded when a range is active (or listed under a documented “unknown date” behavior — pick one and test it).
Scope
In
Range picker UI on /knowledge (from + to, clear, optional presets: Last 7 days, Last 30 days, This month).
GET /files (and /v2/files if it shares the handler) query params, e.g. created_after / created_before.
OpenSearch range filter in FileServiceV2.list_files so pagination/total stay correct.
Wire through useListFiles and reset page/cursor when the range changes (same pattern as buildFilterPageResetKey).
Empty state when nothing matches the range.
Unit tests for the API filter; Playwright coverage on the Knowledge page.
Out
Saving the range as part of a Knowledge Filter (can be a follow-up).
Chat / retrieval date filtering.
Changing how connectors write created_time / modified_time.
Use Case
As a user, I want to pick a start and end date on the Knowledge page so the file grid only shows documents in that range.
Alternatives Considered
No response
Priority
Nice to have
Additional Context
No response
Contribution
I would be willing to help implement this feature.
I can help test this feature once implemented.
Checklist
I have searched existing issues and discussions to ensure this feature hasn't been requested before.
Add created_after/created_before to the list endpoints and translate them into a single OpenSearch range clause on created_time inside FileServiceV2._build_filter_query, reusing the existing shared query so totals and pagination stay consistent.
Build a new date-range picker on the existing Radix Popover with react-day-picker, store the range as toolbar-level context state (not part of a saved filter), and convert local day boundaries to inclusive UTC ISO-8601 bounds.
Thread the bounds through useListFiles, include them in the pagination reset key, and keep them out of the search-mode switch so the filter stays confined to the list path and does not affect chat/agent retrieval.
Cover the API filter with direct query-builder and handler-forwarding unit tests, and cover the Knowledge page with a Playwright test for applying and clearing the range.
Design ChoicesDesign Choice 1: Which date field backs the filter?
Options Considered:
Use created_time. This matches the ticket default. Documents without created_time (local uploads and legacy chunks) are excluded when a range is active.
Use indexed_time. This includes local uploads because the field is always present, but it represents ingest time, not document creation time.
Chosen Option: 1
Rationale: The ticket names created_time as the default and states that documents with no timestamp must be excluded when a range is active. This stays in scope and requires no changes to how connectors or uploads write timestamps.
Design Choice 2: Does the date filter apply to semantic search mode?
Options Considered:
Apply the date filter only in list mode (useListFiles/GET /files). Keep the date range as separate toolbar state, not part of parsedFilterData, so it never routes through search_service.
Thread the date range through both list mode and search mode. This requires changes to search_service.py, which also affects chat and agent retrieval.
Chosen Option: 1
Rationale: The ticket explicitly excludes chat/retrieval date filtering. search_service.search() is shared with the agent, so modifying it would leak date filtering into retrieval. Confining the filter to the list path keeps the change in scope.
Design Choice 3: How is the range picker UI built, given no date library exists?
Options Considered:
Add react-day-picker and build a shadcn-style Calendar on top of the existing Radix Popover. This gives a standard two-month range calendar and presets.
Hand-roll two native <input type="date"> controls inside a Popover. This avoids a new dependency but gives a weaker range UX.
Chosen Option: 1
Rationale:react-day-picker is the standard companion for the existing Radix Popover pattern and provides proper range selection and preset support with minimal custom code.
💡 User Tips
Regenerate the plan with different choices with @coderabbitai <feedback>.
Implementation Steps
Phase 1: Backend date-range filter
This phase adds the created_after/created_before parameters to the list endpoints and translates them into an OpenSearch range clause. The clause joins the existing bool.filter list, so totals, composite-aggregation pagination, and the chunk-count path all scope to the same filtered set automatically.
Task 1: Add a reusable date-range query helper
Provide a single, testable builder for a range clause on a date field.
Create build_date_range_query(field, gte=None, lte=None) in src/utils/opensearch_queries.py, following the style of the existing filename builders.
Return a {"range": {field: {...}}} dict. Include gte only when created_after is set and lte only when created_before is set.
Return None when both bounds are absent, so callers can skip appending an empty clause.
Task 2: Apply the range clause in FileServiceV2
Thread the new bounds through the query builder and the list path.
Update FileServiceV2._build_filter_query in src/services/file_service_v2.py to accept created_after and created_before.
Use the helper from Task 1.1 to append the range clause on created_time to filter_clauses.
Add created_after and created_before parameters to FileServiceV2.list_files and pass them to _build_filter_query.
Do not change _get_file_count, _build_composite_aggregation, or the chunk-count path; they already reuse the same query, so totals and pagination stay correct.
Task 3: Add the API query parameters
Expose the new bounds on both the internal and public list handlers.
Add created_after: str | None = Query(None) and created_before: str | None = Query(None) to list_files in src/api/files.py and forward them to FileServiceV2.list_files.
Mirror the same parameters in the /v2/files wrapper in src/api/v2/files.py so the internal and public signatures stay in parity.
Treat the values as opaque ISO-8601 strings and pass them through unchanged. Do not add server-side timezone conversion.
🤖 Prompt for AI agents
Implement the backend date-range filter for the file list endpoints. Goal: add
`created_after`/`created_before` support and translate them into an OpenSearch
`range` clause that joins the existing `bool.filter` list.
- Create `build_date_range_query(field, gte=None, lte=None)` in
`src/utils/opensearch_queries.py`, following the style of the existing filename
builders. Return `{"range": {field: {...}}}`, include `gte` only when
`created_after` is set and `lte` only when `created_before` is set, and return
`None` when both bounds are absent.
- Update `FileServiceV2._build_filter_query` in
`src/services/file_service_v2.py` to accept `created_after` and
`created_before`, and use the new helper to append the `range` clause on
`created_time` to `filter_clauses`.
- Add `created_after` and `created_before` parameters to
`FileServiceV2.list_files` and pass them to `_build_filter_query`. Do not change
`_get_file_count`, `_build_composite_aggregation`, or the chunk-count path,
since they already reuse the same `query`.
- Add `created_after: str | None = Query(None)` and `created_before: str | None
= Query(None)` to `list_files` in `src/api/files.py` and forward them to
`FileServiceV2.list_files`.
- Mirror the same two parameters in the `/v2/files` wrapper in
`src/api/v2/files.py` to keep the internal and public signatures in parity.
- Treat the values as opaque ISO-8601 strings and pass them through unchanged.
Do not add server-side timezone conversion.
Phase 2: Frontend date-range picker and data wiring
This phase adds the toolbar control, the local-to-UTC conversion, the data-fetch parameters, the pagination reset, and the filter-aware empty state. The date range is toolbar-level state and is not part of a saved Knowledge Filter.
Task 1: Add local-day-to-UTC conversion helpers
Convert a selected local calendar day range into inclusive UTC ISO-8601 bounds.
Add exported helpers to frontend/lib/time-utils.ts, for example toUtcDayStartISOString(date) and toUtcDayEndISOString(date).
Map the start date to local 00:00:00.000 and the end date to local 23:59:59.999, then emit toISOString() (UTC).
Use native Date APIs. Do not add a date library for this conversion.
Task 2: Add date-range state to the filter context
Store the selected range separately from saved-filter state.
Extend KnowledgeFilterContext in frontend/contexts/knowledge-filter-context.tsx with dateRange: { from?: Date; to?: Date } | null and a setDateRange setter.
Do not add the date range to ParsedQueryData or parsedFilterData.filters. Saving the range as part of a Knowledge Filter is out of scope.
Initialize dateRange to null (unfiltered).
Task 3: Build the calendar and range picker components
Add the missing UI primitives on top of the existing Radix Popover.
Add react-day-picker to frontend/package.json.
Create frontend/components/ui/calendar.tsx as a shadcn-style wrapper over react-day-picker, styled with cn(...) to match existing components/ui conventions.
Create frontend/components/knowledge-date-range-filter.tsx. Use Popover + PopoverTrigger + PopoverContent, following frontend/components/filter-icon-popover.tsx.
Support a from/to range, a clear control that resets the range to null, and optional presets ("Last 7 days", "Last 30 days", "This month").
Read and write the range through useKnowledgeFilter() (dateRange/setDateRange).
Task 4: Place the picker in the toolbar
Add the control next to the existing filter controls in both brand layouts.
In the non-cloud branch of frontend/app/knowledge/page.tsx, add KnowledgeDateRangeFilter as a flex-shrink-0 item in the toolbar row, near the search input.
In the cloud branch, add the control to the <form> row in frontend/components/knowledge-search-bar.tsx, alongside the existing buttons before KnowledgeDropdown.
Task 5: Wire the range into the list query and pagination reset
Send the bounds to the list endpoint and reset the cursor when the range changes.
Add createdAfter?: string and createdBefore?: string to ListFilesParams in frontend/app/api/queries/useListFiles.ts and serialize them to created_after/created_before.
In frontend/app/knowledge/page.tsx, convert dateRange with the Task 2.1 helpers and pass createdAfter/createdBefore into the useListFiles params.
Include the converted bounds in buildFilterPageResetKey (or add them as dependencies of the reset useEffect) so the cursor cache clears and currentPage resets to 1 when the range changes.
Keep the date range out of hasActiveFilters/isWildcardQuery so that setting a range does not switch the page into semantic search mode.
Task 6: Make the empty state filter-aware
Show clear feedback when a range matches no documents.
Update the noRowsOverlayComponent in frontend/app/knowledge/page.tsx (both grid branches) to show a "no results for the selected date range" message when a date range is active and serverTotal === 0.
Keep the existing "No knowledge" copy for the true-empty-library case (no active range).
🤖 Prompt for AI agents
Implement the frontend date-range picker for the Knowledge page toolbar and wire
it into the list data fetch. Goal: let users select a date range that filters
the file grid, without affecting saved Knowledge Filters or semantic search
mode.
- Add exported helpers `toUtcDayStartISOString(date)` and
`toUtcDayEndISOString(date)` to `frontend/lib/time-utils.ts`. Map the start date
to local `00:00:00.000` and the end date to local `23:59:59.999`, then emit
`toISOString()` (UTC). Use native `Date` APIs only; do not add a date library
for this conversion.
- Extend `KnowledgeFilterContext` in
`frontend/contexts/knowledge-filter-context.tsx` with `dateRange: { from?: Date;
to?: Date } | null` and a `setDateRange` setter, initialized to `null`. Do not
add the date range to `ParsedQueryData` or `parsedFilterData.filters`.
- Add `react-day-picker` to `frontend/package.json`. Create
`frontend/components/ui/calendar.tsx` as a shadcn-style wrapper over
`react-day-picker`, styled with `cn(...)` to match existing `components/ui`
conventions.
- Create `frontend/components/knowledge-date-range-filter.tsx` using `Popover` +
`PopoverTrigger` + `PopoverContent`, following the pattern in
`frontend/components/filter-icon-popover.tsx`. Support a from/to range, a clear
control that resets the range to `null`, and optional presets ("Last 7 days",
"Last 30 days", "This month"). Read and write the range through
`useKnowledgeFilter()`.
- Place `KnowledgeDateRangeFilter` in the toolbar: as a `flex-shrink-0` item
near the search input in the non-cloud branch of
`frontend/app/knowledge/page.tsx`, and in the `<form>` row of
`frontend/components/knowledge-search-bar.tsx` (alongside the existing buttons,
before `KnowledgeDropdown`) for the cloud branch.
- Add `createdAfter?: string` and `createdBefore?: string` to `ListFilesParams`
in `frontend/app/api/queries/useListFiles.ts`, serialized to
`created_after`/`created_before`. In `frontend/app/knowledge/page.tsx`, convert
`dateRange` using the new time helpers and pass the converted bounds into the
`useListFiles` params.
- Include the converted bounds in `buildFilterPageResetKey` (or as dependencies
of the reset `useEffect`) so the cursor cache clears and `currentPage` resets to
1 when the range changes. Keep the date range out of
`hasActiveFilters`/`isWildcardQuery` so it does not trigger semantic search
mode.
- Update `noRowsOverlayComponent` in `frontend/app/knowledge/page.tsx` (both
grid branches) to show a "no results for the selected date range" message when a
date range is active and `serverTotal === 0`, while keeping the existing "No
knowledge" copy for the true-empty-library case.
Phase 3: Tests
This phase covers the API filter with unit tests and the Knowledge page with Playwright, following existing conventions.
Task 1: Unit-test the query builder
Assert the range clause is constructed correctly.
Add tests in tests/unit/test_query_acl_filtering.py calling FileServiceV2()._build_filter_query(...) directly (no mocks).
Assert a created_after value produces {"range": {"created_time": {"gte": ...}}} in query["bool"]["filter"].
Assert both bounds combine into a single range clause with gte and lte.
Assert that documents without created_time are excluded by the range clause (documented exclusion behavior).
Task 2: Unit-test API parameter forwarding
Confirm the handlers pass the new params to the service.
Add tests in tests/unit/api/test_v1_files.py that mock FileServiceV2 and assert call_kwargs["created_after"] and call_kwargs["created_before"] are forwarded, following test_forwards_all_params_to_service.
Extend TestSignatureParity so the internal and public list_files signatures include the new parameters.
Task 3: Add Playwright coverage
Verify the picker filters the grid and the clear action restores it.
Add page-object helpers to frontend/tests/pages/Knowledge.ts, for example setKnowledgeDateRange(from, to) and clearKnowledgeDateRange(), following the "open control → select → close" pattern used by createKnowledgeFilter.
Add a spec near frontend/tests/core/knowledge_filter.spec.ts that ingests a file, applies a date range, asserts the expected rows appear or disappear (using getDataRows/getSearchResults), clears the range, and asserts the unfiltered grid returns.
Follow the existing idempotent setup/teardown and extended test.setTimeout conventions.
🤖 Prompt for AI agents
Implement automated test coverage for the date-range filter, on both the backend
and the frontend. Goal: verify the query builder, API parameter forwarding, and
the end-to-end toolbar behavior.
- Add tests in `tests/unit/test_query_acl_filtering.py` that call
`FileServiceV2()._build_filter_query(...)` directly (no mocks). Assert that a
`created_after` value produces `{"range": {"created_time": {"gte": ...}}}` in
`query["bool"]["filter"]`, that both bounds combine into a single `range` clause
with `gte` and `lte`, and that documents without `created_time` are excluded by
the range clause.
- Add tests in `tests/unit/api/test_v1_files.py` that mock `FileServiceV2` and
assert `call_kwargs["created_after"]` and `call_kwargs["created_before"]` are
forwarded, following the existing `test_forwards_all_params_to_service` pattern.
Extend `TestSignatureParity` so the internal and public `list_files` signatures
include the new parameters.
- Add page-object helpers to `frontend/tests/pages/Knowledge.ts`, for example
`setKnowledgeDateRange(from, to)` and `clearKnowledgeDateRange()`, following the
"open control → select → close" pattern used by `createKnowledgeFilter`.
- Add a spec near `frontend/tests/core/knowledge_filter.spec.ts` that ingests a
file, applies a date range, asserts the expected rows appear or disappear (using
`getDataRows`/`getSearchResults`), clears the range, and asserts the unfiltered
grid returns. Follow the existing idempotent setup/teardown and extended
`test.setTimeout` conventions.
Research
The Knowledge page (frontend/app/knowledge/page.tsx) owns pagination and cursor state. It toggles between list mode (useListFiles → GET /api/files) and semantic search mode (useGetSearchQuery → /api/search) based on isWildcardQuery. Filter state (source/type/owner/data_sources) lives in KnowledgeFilterContext under parsedFilterData.filters. The backend list path uses FastAPI Query(...) params in src/api/files.py, wrapped by API-key handlers in src/api/v2/files.py, and translated into an OpenSearch bool query by FileServiceV2._build_filter_query. The same query drives count, composite-aggregation pagination, and chunk-count aggregation, so filters stay consistent across totals and pages. The frontend has no date library and no calendar component today.
🚀 Next Steps
🤖 All AI agent prompts combined
Task: 1
Implement the backend date-range filter for the file list endpoints. Goal: add
`created_after`/`created_before` support and translate them into an OpenSearch
`range` clause that joins the existing `bool.filter` list.
- Create `build_date_range_query(field, gte=None, lte=None)` in
`src/utils/opensearch_queries.py`, following the style of the existing filename
builders. Return `{"range": {field: {...}}}`, include `gte` only when
`created_after` is set and `lte` only when `created_before` is set, and return
`None` when both bounds are absent.
- Update `FileServiceV2._build_filter_query` in
`src/services/file_service_v2.py` to accept `created_after` and
`created_before`, and use the new helper to append the `range` clause on
`created_time` to `filter_clauses`.
- Add `created_after` and `created_before` parameters to
`FileServiceV2.list_files` and pass them to `_build_filter_query`. Do not change
`_get_file_count`, `_build_composite_aggregation`, or the chunk-count path,
since they already reuse the same `query`.
- Add `created_after: str | None = Query(None)` and `created_before: str | None
= Query(None)` to `list_files` in `src/api/files.py` and forward them to
`FileServiceV2.list_files`.
- Mirror the same two parameters in the `/v2/files` wrapper in
`src/api/v2/files.py` to keep the internal and public signatures in parity.
- Treat the values as opaque ISO-8601 strings and pass them through unchanged.
Do not add server-side timezone conversion.
===============================================================================
Task: 2
Implement the frontend date-range picker for the Knowledge page toolbar and wire
it into the list data fetch. Goal: let users select a date range that filters
the file grid, without affecting saved Knowledge Filters or semantic search
mode.
- Add exported helpers `toUtcDayStartISOString(date)` and
`toUtcDayEndISOString(date)` to `frontend/lib/time-utils.ts`. Map the start date
to local `00:00:00.000` and the end date to local `23:59:59.999`, then emit
`toISOString()` (UTC). Use native `Date` APIs only; do not add a date library
for this conversion.
- Extend `KnowledgeFilterContext` in
`frontend/contexts/knowledge-filter-context.tsx` with `dateRange: { from?: Date;
to?: Date } | null` and a `setDateRange` setter, initialized to `null`. Do not
add the date range to `ParsedQueryData` or `parsedFilterData.filters`.
- Add `react-day-picker` to `frontend/package.json`. Create
`frontend/components/ui/calendar.tsx` as a shadcn-style wrapper over
`react-day-picker`, styled with `cn(...)` to match existing `components/ui`
conventions.
- Create `frontend/components/knowledge-date-range-filter.tsx` using `Popover` +
`PopoverTrigger` + `PopoverContent`, following the pattern in
`frontend/components/filter-icon-popover.tsx`. Support a from/to range, a clear
control that resets the range to `null`, and optional presets ("Last 7 days",
"Last 30 days", "This month"). Read and write the range through
`useKnowledgeFilter()`.
- Place `KnowledgeDateRangeFilter` in the toolbar: as a `flex-shrink-0` item
near the search input in the non-cloud branch of
`frontend/app/knowledge/page.tsx`, and in the `<form>` row of
`frontend/components/knowledge-search-bar.tsx` (alongside the existing buttons,
before `KnowledgeDropdown`) for the cloud branch.
- Add `createdAfter?: string` and `createdBefore?: string` to `ListFilesParams`
in `frontend/app/api/queries/useListFiles.ts`, serialized to
`created_after`/`created_before`. In `frontend/app/knowledge/page.tsx`, convert
`dateRange` using the new time helpers and pass the converted bounds into the
`useListFiles` params.
- Include the converted bounds in `buildFilterPageResetKey` (or as dependencies
of the reset `useEffect`) so the cursor cache clears and `currentPage` resets to
1 when the range changes. Keep the date range out of
`hasActiveFilters`/`isWildcardQuery` so it does not trigger semantic search
mode.
- Update `noRowsOverlayComponent` in `frontend/app/knowledge/page.tsx` (both
grid branches) to show a "no results for the selected date range" message when a
date range is active and `serverTotal === 0`, while keeping the existing "No
knowledge" copy for the true-empty-library case.
===============================================================================
Task: 3
Implement automated test coverage for the date-range filter, on both the backend
and the frontend. Goal: verify the query builder, API parameter forwarding, and
the end-to-end toolbar behavior.
- Add tests in `tests/unit/test_query_acl_filtering.py` that call
`FileServiceV2()._build_filter_query(...)` directly (no mocks). Assert that a
`created_after` value produces `{"range": {"created_time": {"gte": ...}}}` in
`query["bool"]["filter"]`, that both bounds combine into a single `range` clause
with `gte` and `lte`, and that documents without `created_time` are excluded by
the range clause.
- Add tests in `tests/unit/api/test_v1_files.py` that mock `FileServiceV2` and
assert `call_kwargs["created_after"]` and `call_kwargs["created_before"]` are
forwarded, following the existing `test_forwards_all_params_to_service` pattern.
Extend `TestSignatureParity` so the internal and public `list_files` signatures
include the new parameters.
- Add page-object helpers to `frontend/tests/pages/Knowledge.ts`, for example
`setKnowledgeDateRange(from, to)` and `clearKnowledgeDateRange()`, following the
"open control → select → close" pattern used by `createKnowledgeFilter`.
- Add a spec near `frontend/tests/core/knowledge_filter.spec.ts` that ingests a
file, applies a date range, asserts the expected rows appear or disappear (using
`getDataRows`/`getSearchResults`), clears the range, and asserts the unfiltered
grid returns. Follow the existing idempotent setup/teardown and extended
`test.setTimeout` conventions.
💡 Iterate on the plan with:
`@coderabbitai` <feedback>
Example Feedback
- `@coderabbitai` You can skip phase 3. Add a simple unit test case for phase 2.
- `@coderabbitai` For design choice 1 go ahead with option 3 and replan.
Activity
coderabbitai commentedon Sep 9, 2026
Coding Plan
Summary
created_after/created_beforeto the list endpoints and translate them into a single OpenSearchrangeclause oncreated_timeinsideFileServiceV2._build_filter_query, reusing the existing shared query so totals and pagination stay consistent.Popoverwithreact-day-picker, store the range as toolbar-level context state (not part of a saved filter), and convert local day boundaries to inclusive UTC ISO-8601 bounds.useListFiles, include them in the pagination reset key, and keep them out of the search-mode switch so the filter stays confined to the list path and does not affect chat/agent retrieval.Design Choices
Design Choice 1: Which date field backs the filter?
Options Considered:
created_time. This matches the ticket default. Documents withoutcreated_time(local uploads and legacy chunks) are excluded when a range is active.indexed_time. This includes local uploads because the field is always present, but it represents ingest time, not document creation time.Chosen Option: 1
Rationale: The ticket names
created_timeas the default and states that documents with no timestamp must be excluded when a range is active. This stays in scope and requires no changes to how connectors or uploads write timestamps.Design Choice 2: Does the date filter apply to semantic search mode?
Options Considered:
useListFiles/GET /files). Keep the date range as separate toolbar state, not part ofparsedFilterData, so it never routes throughsearch_service.search_service.py, which also affects chat and agent retrieval.Chosen Option: 1
Rationale: The ticket explicitly excludes chat/retrieval date filtering.
search_service.search()is shared with the agent, so modifying it would leak date filtering into retrieval. Confining the filter to the list path keeps the change in scope.Design Choice 3: How is the range picker UI built, given no date library exists?
Options Considered:
react-day-pickerand build a shadcn-styleCalendaron top of the existing RadixPopover. This gives a standard two-month range calendar and presets.<input type="date">controls inside aPopover. This avoids a new dependency but gives a weaker range UX.Chosen Option: 1
Rationale:
react-day-pickeris the standard companion for the existing RadixPopoverpattern and provides proper range selection and preset support with minimal custom code.💡 User Tips
Regenerate the plan with different choices with
@coderabbitai <feedback>.Implementation Steps
Phase 1: Backend date-range filter
This phase adds the
created_after/created_beforeparameters to the list endpoints and translates them into an OpenSearchrangeclause. The clause joins the existingbool.filterlist, so totals, composite-aggregation pagination, and the chunk-count path all scope to the same filtered set automatically.Task 1: Add a reusable date-range query helper
Provide a single, testable builder for a
rangeclause on a date field.build_date_range_query(field, gte=None, lte=None)insrc/utils/opensearch_queries.py, following the style of the existing filename builders.{"range": {field: {...}}}dict. Includegteonly whencreated_afteris set andlteonly whencreated_beforeis set.Nonewhen both bounds are absent, so callers can skip appending an empty clause.Task 2: Apply the range clause in FileServiceV2
Thread the new bounds through the query builder and the list path.
FileServiceV2._build_filter_queryinsrc/services/file_service_v2.pyto acceptcreated_afterandcreated_before.rangeclause oncreated_timetofilter_clauses.created_afterandcreated_beforeparameters toFileServiceV2.list_filesand pass them to_build_filter_query._get_file_count,_build_composite_aggregation, or the chunk-count path; they already reuse the samequery, so totals and pagination stay correct.Task 3: Add the API query parameters
Expose the new bounds on both the internal and public list handlers.
created_after: str | None = Query(None)andcreated_before: str | None = Query(None)tolist_filesinsrc/api/files.pyand forward them toFileServiceV2.list_files./v2/fileswrapper insrc/api/v2/files.pyso the internal and public signatures stay in parity.🤖 Prompt for AI agents
Phase 2: Frontend date-range picker and data wiring
This phase adds the toolbar control, the local-to-UTC conversion, the data-fetch parameters, the pagination reset, and the filter-aware empty state. The date range is toolbar-level state and is not part of a saved Knowledge Filter.
Task 1: Add local-day-to-UTC conversion helpers
Convert a selected local calendar day range into inclusive UTC ISO-8601 bounds.
frontend/lib/time-utils.ts, for exampletoUtcDayStartISOString(date)andtoUtcDayEndISOString(date).00:00:00.000and the end date to local23:59:59.999, then emittoISOString()(UTC).DateAPIs. Do not add a date library for this conversion.Task 2: Add date-range state to the filter context
Store the selected range separately from saved-filter state.
KnowledgeFilterContextinfrontend/contexts/knowledge-filter-context.tsxwithdateRange: { from?: Date; to?: Date } | nulland asetDateRangesetter.ParsedQueryDataorparsedFilterData.filters. Saving the range as part of a Knowledge Filter is out of scope.dateRangetonull(unfiltered).Task 3: Build the calendar and range picker components
Add the missing UI primitives on top of the existing Radix
Popover.react-day-pickertofrontend/package.json.frontend/components/ui/calendar.tsxas a shadcn-style wrapper overreact-day-picker, styled withcn(...)to match existingcomponents/uiconventions.frontend/components/knowledge-date-range-filter.tsx. UsePopover+PopoverTrigger+PopoverContent, followingfrontend/components/filter-icon-popover.tsx.null, and optional presets ("Last 7 days", "Last 30 days", "This month").useKnowledgeFilter()(dateRange/setDateRange).Task 4: Place the picker in the toolbar
Add the control next to the existing filter controls in both brand layouts.
frontend/app/knowledge/page.tsx, addKnowledgeDateRangeFilteras aflex-shrink-0item in the toolbar row, near the search input.<form>row infrontend/components/knowledge-search-bar.tsx, alongside the existing buttons beforeKnowledgeDropdown.Task 5: Wire the range into the list query and pagination reset
Send the bounds to the list endpoint and reset the cursor when the range changes.
createdAfter?: stringandcreatedBefore?: stringtoListFilesParamsinfrontend/app/api/queries/useListFiles.tsand serialize them tocreated_after/created_before.frontend/app/knowledge/page.tsx, convertdateRangewith the Task 2.1 helpers and passcreatedAfter/createdBeforeinto theuseListFilesparams.buildFilterPageResetKey(or add them as dependencies of the resetuseEffect) so the cursor cache clears andcurrentPageresets to 1 when the range changes.hasActiveFilters/isWildcardQueryso that setting a range does not switch the page into semantic search mode.Task 6: Make the empty state filter-aware
Show clear feedback when a range matches no documents.
noRowsOverlayComponentinfrontend/app/knowledge/page.tsx(both grid branches) to show a "no results for the selected date range" message when a date range is active andserverTotal === 0.🤖 Prompt for AI agents
Phase 3: Tests
This phase covers the API filter with unit tests and the Knowledge page with Playwright, following existing conventions.
Task 1: Unit-test the query builder
Assert the
rangeclause is constructed correctly.tests/unit/test_query_acl_filtering.pycallingFileServiceV2()._build_filter_query(...)directly (no mocks).created_aftervalue produces{"range": {"created_time": {"gte": ...}}}inquery["bool"]["filter"].rangeclause withgteandlte.created_timeare excluded by the range clause (documented exclusion behavior).Task 2: Unit-test API parameter forwarding
Confirm the handlers pass the new params to the service.
tests/unit/api/test_v1_files.pythat mockFileServiceV2and assertcall_kwargs["created_after"]andcall_kwargs["created_before"]are forwarded, followingtest_forwards_all_params_to_service.TestSignatureParityso the internal and publiclist_filessignatures include the new parameters.Task 3: Add Playwright coverage
Verify the picker filters the grid and the clear action restores it.
frontend/tests/pages/Knowledge.ts, for examplesetKnowledgeDateRange(from, to)andclearKnowledgeDateRange(), following the "open control → select → close" pattern used bycreateKnowledgeFilter.frontend/tests/core/knowledge_filter.spec.tsthat ingests a file, applies a date range, asserts the expected rows appear or disappear (usinggetDataRows/getSearchResults), clears the range, and asserts the unfiltered grid returns.test.setTimeoutconventions.🤖 Prompt for AI agents
Research
The Knowledge page (
frontend/app/knowledge/page.tsx) owns pagination and cursor state. It toggles between list mode (useListFiles→GET /api/files) and semantic search mode (useGetSearchQuery→/api/search) based onisWildcardQuery. Filter state (source/type/owner/data_sources) lives inKnowledgeFilterContextunderparsedFilterData.filters. The backend list path uses FastAPIQuery(...)params insrc/api/files.py, wrapped by API-key handlers insrc/api/v2/files.py, and translated into an OpenSearchboolquery byFileServiceV2._build_filter_query. The same query drives count, composite-aggregation pagination, and chunk-count aggregation, so filters stay consistent across totals and pages. The frontend has no date library and no calendar component today.🚀 Next Steps
🤖 All AI agent prompts combined
💡 Iterate on the plan with:
`@coderabbitai` <feedback>