Skip to content

feat: add --return-time / return_departure_window for separate return leg time filter#196

Open
RobertoReale wants to merge 6 commits into
punitarani:mainfrom
RobertoReale:feature/return-departure-window
Open

feat: add --return-time / return_departure_window for separate return leg time filter#196
RobertoReale wants to merge 6 commits into
punitarani:mainfrom
RobertoReale:feature/return-departure-window

Conversation

@RobertoReale

@RobertoReale RobertoReale commented Jun 6, 2026

Copy link
Copy Markdown

Motivation

--time / departure_window applies the same time window to both legs of a round trip. This is fine when you want symmetric constraints (e.g. always fly during the day), but breaks the common case where you want a morning outbound and an evening return -- or vice versa. Without this flag you have to post-filter results manually.

What this adds

--return-time / -T on fli flights and fli dates, and return_departure_window on the MCP tools search_flights, search_dates, and get_booking_options. When provided on a round-trip search, the return leg uses its own time window independently of the outbound window.

fli flights BGY NAP 2026-07-22 --round --return 2026-07-27 --time 6-16 --return-time 16-23
fli dates BGY NAP --round --min-duration 5 --max-duration 5 --time 6-16 --return-time 16-23

Design

The core change is a three-state sentinel in builders.py:

Value Meaning
False (default) Inherit the outbound window -- same behaviour as before
None No filter on the return leg
TimeRestrictions(...) Explicit independent window for the return leg

This is a backwards-compatible default: existing callers that do not pass --return-time get exactly the same behaviour as before.

Validation

  • --return-time without --round raises an explicit error (consistent with --min-duration / --max-duration)
  • --min-duration requires --max-duration (unbounded iteration is prevented)
  • --min-duration must not exceed --max-duration

Files changed

File Change
fli/core/builders.py return_time_restrictions sentinel parameter on both builder functions
fli/cli/commands/flights.py --return-time / -T option, threaded through _search_flights_core
fli/cli/commands/dates.py --return-time / -T option, validation guard for one-way searches
fli/mcp/server.py return_departure_window on FlightSearchParams and DateSearchParams
README.md --return-time and return_departure_window added to all reference tables
tests/core/test_builders.py 6 unit tests covering all three sentinel states
tests/cli/test_flights.py Separate return window applied correctly; one-way silently ignores it (flights)
tests/cli/test_dates.py --return-time accepted on round-trip dates search
tests/mcp/test_mcp_server_unit.py Iteration count, round-trip requirement, trip_duration conflict

Test plan

  • uv run pytest -vv --ignore=tests/search -- all 448 unit tests pass
  • uv run ruff check . -- clean
  • fli flights BGY NAP 2026-07-22 --round --return 2026-07-27 --time 6-16 --return-time 16-23 -- returns only flights with morning outbound and evening return
  • fli flights BGY NAP 2026-07-22 --time 6-16 --return-time 16-23 (no --round) -- exits with clear error
  • fli dates BGY NAP --round --min-duration 5 --max-duration 5 --return-time 16-23 -- applies return window to all iterations
  • fli dates BGY NAP --min-duration 5 --max-duration 5 --return-time 16-23 (no --round) -- exits with clear error

Add explicit error when --min-duration exceeds --max-duration instead of
silently returning empty results. Also add min=1 constraint on both CLI
options to prevent zero/negative durations from reaching the API.
trip_type was only assigned inside the for loop body, so Pylance flagged
it as possibly unbound. Since its value depends only on is_round_trip
(constant per call), initialize it once before the loop.
… leg time filter

Adds a new optional parameter to both CLI and MCP that lets callers specify
an independent departure time window for the return leg of a round-trip search,
decoupled from --time / departure_window which controls the outbound leg.

- builders.py: return_time_restrictions kwarg on build_flight_segments and
  build_date_search_segments; False (default) = inherit outbound, None = no
  filter, TimeRestrictions = explicit override
- CLI (flights + dates): --return-time / -T option
- MCP (search_flights, search_dates, get_booking_options): return_departure_window field
Comment thread fli/cli/commands/dates.py
Comment on lines +303 to +304
if (min_duration is not None or max_duration is not None) and not is_round_trip:
raise ValueError("--min-duration and --max-duration require --round")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 --return-time silently ignored on one-way date search

--min-duration and --max-duration without --round immediately raise a ValueError with a clear error message, but --return-time without --round is accepted without complaint — the restriction is parsed into return_time_restrictions and then passed to build_date_search_segments, which simply never appends a return segment for one-way trips, silently discarding the filter. A user who forgets --round while using --return-time will receive results without the requested return window applied and no indication that anything is wrong.

Prompt To Fix With AI
This is a comment left during a code review.
Path: fli/cli/commands/dates.py
Line: 303-304

Comment:
**`--return-time` silently ignored on one-way date search**

`--min-duration` and `--max-duration` without `--round` immediately raise a `ValueError` with a clear error message, but `--return-time` without `--round` is accepted without complaint — the restriction is parsed into `return_time_restrictions` and then passed to `build_date_search_segments`, which simply never appends a return segment for one-way trips, silently discarding the filter. A user who forgets `--round` while using `--return-time` will receive results without the requested return window applied and no indication that anything is wrong.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Cursor Fix in Claude Code Fix in Codex

Comment thread fli/cli/commands/dates.py
Comment on lines +386 to +400
if min_duration is not None or max_duration is not None:
actual_min = min_duration if min_duration is not None else 1
if max_duration is not None:
actual_max = max_duration
else:
from_dt = datetime.strptime(start_date, "%Y-%m-%d")
to_dt = datetime.strptime(end_date, "%Y-%m-%d")
actual_max = max(actual_min, (to_dt - from_dt).days)
if actual_min > actual_max:
raise ValueError(
f"--min-duration ({actual_min}) must not exceed --max-duration ({actual_max})"
)
durations_to_search = list(range(actual_min, actual_max + 1))
else:
durations_to_search = [trip_duration] if trip_type == TripType.ROUND_TRIP else [None]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unbounded iteration count when --max-duration is omitted

When only --min-duration is supplied, actual_max is derived from the entire date-range span: (to_dt - from_dt).days. With a default 60-day window and --min-duration 1, this produces 60 iterations each separated by time.sleep(1) — roughly a 60-second stall with no progress feedback. The same pattern exists in _execute_date_search in server.py. Consider capping the derived maximum (e.g., at 30 days) or requiring --max-duration whenever --min-duration is used.

Prompt To Fix With AI
This is a comment left during a code review.
Path: fli/cli/commands/dates.py
Line: 386-400

Comment:
**Unbounded iteration count when `--max-duration` is omitted**

When only `--min-duration` is supplied, `actual_max` is derived from the entire date-range span: `(to_dt - from_dt).days`. With a default 60-day window and `--min-duration 1`, this produces 60 iterations each separated by `time.sleep(1)` — roughly a 60-second stall with no progress feedback. The same pattern exists in `_execute_date_search` in `server.py`. Consider capping the derived maximum (e.g., at 30 days) or requiring `--max-duration` whenever `--min-duration` is used.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Cursor Fix in Claude Code Fix in Codex

- --return-time without --round now raises an explicit error, consistent
  with --min-duration behaviour
- --min-duration without --max-duration now raises an error instead of
  silently deriving an unbounded iteration count from the date range
- Add tests for both new validation paths
@RobertoReale

Copy link
Copy Markdown
Author

All feedback from the Greptile review has been addressed in subsequent commits. Happy to adjust anything if needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant