Skip to content

feat: expose aircraft, legroom, amenities, codeshares, and emissions in flight results#95

Open
iclems wants to merge 2 commits intopunitarani:mainfrom
iclems:feat/add-aircraft-amenities-emissions
Open

feat: expose aircraft, legroom, amenities, codeshares, and emissions in flight results#95
iclems wants to merge 2 commits intopunitarani:mainfrom
iclems:feat/add-aircraft-amenities-emissions

Conversation

@iclems
Copy link
Copy Markdown

@iclems iclems commented Mar 30, 2026

Summary

  • Per-leg details: Parse and return aircraft (e.g. "Boeing 777"), legroom (e.g. "31 in"), codeshares (e.g. [{"airline": "DL", "flight_number": "8747"}]), and onboard amenities (wifi, power, in_seat_entertainment) from the raw Google Flights API response.
  • Itinerary-level emissions: Extract and return CO2 emissions data (co2_grams, typical_co2_grams, diff_percent) when available.
  • Backward compatible: All new fields are optional (None by default) and only included in the MCP JSON output when present, so existing consumers see no change.

Files changed

File What
fli/models/google_flights/base.py New Emissions model; added optional fields to FlightLeg and FlightResult
fli/models/google_flights/__init__.py Export Emissions
fli/search/flights.py _parse_leg_details() and _parse_emissions() static methods to extract new data from raw API arrays
fli/mcp/server.py _serialize_emissions() helper; updated leg and result serializers to conditionally include new fields

Mapping from raw Google Flights data

Index Field Example
fl[17] Aircraft type "Boeing 777"
fl[14] Legroom "31 in"
fl[15] Codeshare partners [["DL","8747",null,"Delta"]]
fl[12][1] Wi-Fi true
fl[12][11] Power (USB/AC) 3 (AC+USB)
fl[12][9] Seatback entertainment true
data[0][18][7/8/3] CO2 grams / typical / diff% 329000 / 369000 / -11

Test plan

  • All 125 existing tests pass (1 pre-existing flaky timeout on test_complex_round_trip_search — unrelated network issue)
  • Verify new fields appear in MCP output for a live one-way search (e.g. CDG→JFK)
  • Confirm fields are omitted when Google Flights doesn't return them (e.g. some budget carriers)

Made with Cursor

Greptile Summary

This PR enriches flight search results with per-leg details (aircraft type, legroom, codeshares, Wi-Fi/power/entertainment amenities) and itinerary-level CO₂ emissions data, all surfaced as optional fields so existing consumers are unaffected.

  • New Emissions Pydantic model and optional fields on FlightLeg/FlightResult are clean and backward-compatible.
  • _parse_leg_details() defensively handles missing/malformed API array entries.
  • power and in_seat_entertainment are typed bool | None in the model but only ever assigned True by the parser — False is never produced, making it impossible to distinguish "API reported no power" from "API didn't return the field."
  • For round-trip results, return_flight.emissions is silently discarded; only the outbound leg's CO₂ data appears in the serialised output.
  • One line in _parse_leg_details (~110 chars) exceeds the project's configured 100-character limit and will fail make lint.

Confidence Score: 5/5

  • Safe to merge; all findings are P2 quality/design suggestions with no production breakage.
  • No P0 or P1 issues found. The power/in_seat_entertainment boolean asymmetry and the round-trip emissions omission are design inconsistencies worth addressing but neither breaks existing behaviour, corrupts data, nor fails existing tests. The line-length violation is a style issue. All new fields are optional and the change is fully backward-compatible.
  • fli/search/flights.py (boolean semantics + line length) and fli/mcp/server.py (round-trip emissions handling)

Important Files Changed

Filename Overview
fli/search/flights.py Adds _parse_leg_details() and _parse_emissions() static methods; power/in_seat_entertainment are never set to False (type mismatch vs model), and a line exceeds the 100-char style limit.
fli/mcp/server.py Adds _serialize_emissions() and conditionally includes new fields in leg/result serializers; round-trip return-leg emissions are silently discarded.
fli/models/google_flights/base.py Adds optional fields to FlightLeg and new Emissions model; clean, backward-compatible Pydantic changes.
fli/models/google_flights/init.py Exports the new Emissions model; trivial, correct change.

Sequence Diagram

sequenceDiagram
    participant C as Caller (MCP / CLI)
    participant S as SearchFlights
    participant A as Google Flights API

    C->>S: search(filters)
    S->>A: POST GetShoppingResults
    A-->>S: raw JSON (data[])
    loop for each flight in data[2/3]
        S->>S: _parse_flights_data(data)
        S->>S: _parse_leg_details(fl) → aircraft, legroom, codeshares, wifi, power, ent.
        S->>S: _parse_emissions(data) → Emissions(co2_grams, typical, diff%)
        S-->>S: FlightResult(legs=[FlightLeg(...)], emissions=...)
    end
    S-->>C: list[FlightResult]
    C->>C: _serialize_flight_result(flight)
    Note over C: round-trip: only outbound.emissions serialised
    C-->>C: dict with optional aircraft/legroom/codeshares/amenities/emissions
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: fli/search/flights.py
Line: 171-177

Comment:
**`power` and `in_seat_entertainment` only ever set to `True`**

Both fields are typed `bool | None` in the model, but the parsing logic never assigns `False` — only `True` or nothing (defaulting to `None`). This means consumers cannot distinguish "the API explicitly reported no power" from "the API didn't return power data at all", and any code doing `if leg.power is False:` will never be reached.

`wifi` is handled correctly (assigned `bool(amenities[1])`, which can be `False`), but `power` and `in_seat_entertainment` are only set when their condition is truthy. Either:
- Set them explicitly to `False` when the amenities block is present but the feature is absent, mirroring `wifi`'s behaviour; or
- Keep `None` as the "no data" sentinel and document that `False` is unused.

```suggestion
                amenities = fl[12]
                details["wifi"] = bool(amenities[1]) if len(amenities) > 1 and amenities[1] is not None else None
                if len(amenities) > 11 and amenities[11] is not None:
                    details["power"] = amenities[11] >= 2
                if len(amenities) > 9 and amenities[9] is not None:
                    details["in_seat_entertainment"] = bool(amenities[9])
```

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

---

This is a comment left during a code review.
Path: fli/mcp/server.py
Line: 334-336

Comment:
**Return-leg emissions silently dropped for round trips**

For round-trip results, only `outbound.emissions` is serialised. The `return_flight.emissions` (a separate itinerary with its own `data[0][18]` block from the second API call) is never surfaced, so consumers always see the outbound leg's CO₂ data only.

This may be intentional (e.g. show only the first leg's data), but it's worth being explicit. If the intent is to represent the total trip, consider including both:

```python
outbound_emissions = _serialize_emissions(outbound.emissions)
if outbound_emissions:
    result["outbound_emissions"] = outbound_emissions
return_emissions = _serialize_emissions(return_flight.emissions)
if return_emissions:
    result["return_emissions"] = return_emissions
```

Or, if only one is desired, add a comment explaining why.

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

---

This is a comment left during a code review.
Path: fli/search/flights.py
Line: 170

Comment:
**Line exceeds 100-character limit**

Per the project's Ruff configuration (100-char line length), this line is ~110 characters and will fail `make lint`.

```suggestion
                details["wifi"] = (
                    bool(amenities[1]) if len(amenities) > 1 and amenities[1] is not None else None
                )
```

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

Reviews (1): Last reviewed commit: "feat: expose aircraft, legroom, amenitie..." | Re-trigger Greptile

Greptile also left 3 inline comments on this PR.

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Context used:

  • Context used - CLAUDE.md (source)

…in flight results

Parse additional per-leg details from the Google Flights API response
(aircraft type, seat legroom, codeshare partners, Wi-Fi/power/IFE
availability) and itinerary-level CO2 emissions data. All new fields
are optional and only included in the MCP output when present, so
existing consumers are unaffected.

Made-with: Cursor
- Set power/in_seat_entertainment to explicit False when amenities data
  is present but feature is absent (mirrors wifi behavior)
- Break long wifi assignment line to stay within 100-char limit
- Include both outbound_emissions and return_emissions for round trips
  instead of silently dropping the return leg's CO2 data

Made-with: Cursor
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