DEV Community

Cover image for I Travel Often, So I Built an AI Copilot for Comparing Flights: Meet AirGem
Ryan de Oliveira
Ryan de Oliveira

Posted on

I Travel Often, So I Built an AI Copilot for Comparing Flights: Meet AirGem

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

I'm a frequent traveler in Brazil, and keeping costs down is my top priority when comparing flights. But finding a cheap option is only part of the work: I also need to compare departure times, stops, dates, personal preferences, and calendar conflicts, which often means opening dozens of tabs :/

That frustration led me to build AirGem: an open-source, privacy-first flight-search copilot that turns a natural-language travel request into a small set of evaluated, explainable flight options.

A user can ask:

Find cheap flights from GRU to POA from January 1, 2027, to January 8, 2027, with a maximum budget of 1500 BRL.

AirGem parses the request, searches current flight data, evaluates candidates, considers calendar conflicts, and explains the selected options. It also shows alternative flights and booking links when the provider returns enough information.

The product idea is a focused travel-planning assistant for frequent travelers, remote workers, and people who want help making a travel decision. These are the users I could imagine paying for a reliable, explainable itinerary shortlist.

My motivation

Flight-search assistants already exist. Google, for example, has an AI-powered Flight Deals experience that interprets natural-language travel ideas and searches its own flight inventory. These products are convenient, but they also tend to keep the experience inside a particular vendor's ecosystem.

That creates a question I kept coming back to: what if the most useful context is my calendar, but I don't want to give a travel platform ongoing access to it?

AirGem takes a narrower and more portable approach. Instead of connecting directly to a primary calendar account, the user can upload an .ics export with one button and set a travel buffer. The calendar is used to rule out conflicts during the search, then discarded with the rest of the transient session data.

The creative part is not trying to build another general-purpose travel chatbot. Rather, I tried to combine four pieces:

  • Gemma for interpreting preferences and explaining tradeoffs
  • SerpApi for current flight-search data
  • deterministic Python logic for filtering and ranking
  • a local calendar export for practical schedule awareness

That combination makes the assistant useful without requiring the user to trust one company with the entire travel-planning context. It also means the application can be self-hosted, the inference provider can be changed, and the calendar data can remain under the user's control.

Demo

Try the live demo: https://airgem.onrender.com

The demo asks users to provide their own SerpApi key. The key is sent over HTTPS, kept only in server memory for the transient session, never logged, and cleared after the session expires or the search completes. Optional calendars are handled in the same transient way.

AirGem flight search form and transient-key notice

AirGem flight search results

Code

✈️AirGem

Live Demo Python 3.12+ FastAPI License: MIT

A privacy-first flight-search copilot that finds flights around your real calendar.

AirGem turns a natural-language travel request into a small set of practical flight options. It keeps calendar data local, uses Gemma for semantic work and keeps search, validation, availability checks, and trade-off filtering deterministic.

🚀 Hacktoberfest Week 2: Side Hustle

AirGem is being built as a submission for the Hacktoberfest Open-Source AI Challenge: Week 2 — Side Hustle.

This challenge asks for a new project with open-weight or open-source AI that solves a real problem someone could plausibly pay for. With this, AirGem's target user is a frequent traveler, consultant, freelancer, or remote worker who wants to find an affordable trip without manually comparing flight times against work, classes, appointments, or family commitments.

Why open models?

AirGem uses Gemma through Ollama by default, with OpenRouter as an optional fallback. This keeps the model replaceable and makes…

The project includes local setup instructions, a FastAPI web app, Render deployment configuration, and tests.

How I Built It

AirGem uses Gemma as a bounded interpretation and synthesis layer, not as the source of truth for flight facts.

AirGem architecture and data flow

The application separates model judgment from deterministic application logic:

  1. Gemma parses the user's request into structured travel preferences.
  2. SerpApi provides current flight-search results.
  3. Python code filters, ranks, and normalizes the candidates.
  4. Gemma synthesizes a concise explanation of why the selected options fit the request.
  5. The UI presents the result, alternatives, calendar conflicts, and booking links.

This division matters. Gemma can understand that a traveler who prefers the afternoon may reject an evening departure for a meaningful reason, but application-side logic still controls prices, times, availability, and booking actions.

For the hosted demo, inference uses Gemma through OpenRouter and the application runs on Render. A local setup can use Ollama for a more private inference path.

Why Does Open Innovation Matter?

Open innovation made it possible to keep the system modular and honest about what each component does.

Gemma can be run locally through Ollama or accessed through a hosted inference provider. SerpApi supplies live flight data, while the ranking, calendar handling, and safety boundaries remain in the application code. FastAPI and Render make the web experience easy to run locally or deploy as a small public demo.

This also keeps the project from being locked to one model or infrastructure provider. A user can self-host the application, change the inference backend, or inspect the deterministic parts of the decision process.

The hosted version uses rate limiting, bounded request sizes, transient credentials, and in-memory session state. Users still verify the final itinerary and complete booking with the airline or booking provider.

My Agent Session

AirGem: building a privacy-first flight-search copilot
You

Use caveman ultra. Read the README for context. Help create initial scaffolding for AirGem, a Hacktoberfest Week 2 side-hustle project. Start with local Ollama Gemma, DigitalOcean infrastructure, and OpenRouter Gemma free as fallback.

Agent

Read the project context and scaffolded the first slice around replaceable LLM providers, strict flight-request parsing, and the existing project conventions.

You

Choose a first slice: minimal Ollama and OpenRouter Gemma providers under llm/, plus a core natural-language parser for cheap flights. Keep SerpAPI abstract so fastflights can be added later.

Agent

Implemented the provider interfaces and strict natural-language flight request parsing with a small testable core.

You

Commit this first slice using a conventional commit.

Agent

Committed the first slice with a conventional commit.

You

Next work item: implement SerpAPI integration under search/. Use Google Flights API, deep_search by default, departure_id and arrival_id, exact dates, price sorting, and one-way calls for outbound and return. Leave TODOs for multiple airports, flexible dates, and native round trips. Do not commit the temporary serpapi.json.

Agent

Added the SerpAPI provider and abstract search models. Kept booking URLs out of the core search result and added the requested TODOs.

You

Commit your work.

Agent

Committed the SerpAPI integration.

You

Explain what was done in the commit message.

Agent

Followed up with a clearer conventional commit description.

You

Can we run a first test? I moved the project-direction README to PROJECT and need the setup README.

Agent

Checked the setup path and described a local test using Ollama/OpenRouter, SerpAPI, and environment variables.

You

OpenRouter returned a 429 because the free Gemma provider pool was rate-limited, even after sourcing .env.

Agent

Explained that the error was upstream OpenRouter rate limiting, not local environment loading, and suggested Ollama or provider routing.

You

Can quantized Gemma 4B run through Ollama or only llama.cpp?

Agent

Explained the Ollama-compatible quantized model path and the distinction between Ollama model packages and direct llama.cpp GGUF usage.

You

Ollama returns model gemma3:4b not found even though .env has the right model. What is the best way to load .env automatically?

Agent

Added/confirmed automatic dotenv loading and explained that the model must also be pulled into Ollama with the exact configured model name.

You

The parsed request is GRU to POA on two ISO dates with USD and no missing fields. Is this right?

Agent

Confirmed the normalized FlightRequest shape and default USD currency.

You

What is next?

Agent

Connected the parser to the CLI and then wired the search path.

You

Default currency to USD and evaluate a date stabilizer for phrases like next Thursday.

Agent

Evaluated date stabilization and recommended delegating relative-date interpretation in this first slice while keeping exact ISO dates reliable.

You

LLMs can be bad at mapping this weekend to ISO dates. Could a small tool stabilize dates and be exposed as an agent tool?

Agent

Discussed a deterministic date helper/tool, but kept it out of the first v1 to avoid adding a second date-resolution policy prematurely.

You

Delegate dates for v1 and wire the full CLI path.

Agent

Wired the CLI through parsing, search, itinerary construction, and human/JSON output.

You

The CLI says SerpAPI is missing departure_id.

Agent

Diagnosed that the parsed request was not being mapped to the provider parameter names.

You

The same departure_id error remains.

Agent

Traced the request construction and fixed the mapping path.

You

Now SerpAPI says outbound_date is missing.

Agent

Identified the provider's required outbound_date field and corrected request construction.

You

Now it says return_date is required when type is round trip. Is return date defaulted to 1?

Agent

Explained that type=1 means round trip and return_date must be explicitly supplied; fixed the round-trip request mapping.

You

Are we forgetting anything else in SerpAPI?

Agent

Reviewed required and useful Google Flights parameters, including localization, deep search, exact dates, and route identifiers.

You

Is booking lookup being done for every possible ticket? That seems wrong.

Agent

Confirmed it was too eager and separated core search from booking URL resolution.

You

Commit before changing the booking slice.

Agent

Committed the current work before changing booking resolution.

You

What is outbound index?

Agent

Explained that it identifies the selected outbound result used for a later return expansion.

You

Why not use an agent loop already?

Agent

Added the bounded state-machine agent around deterministic search and selection tools.

You

The JSON has one outbound result with a booking URL.

Agent

Explained the result shape and why booking URL resolution should be deferred.

You

Remove booking URLs and keep search pure.

Agent

Removed booking URL resolution from the pure search result.

You

Commit that.

Agent

Committed the pure-search change.

You

Add airline to the JSON.

Agent

Added airline serialization to flight options.

You

Commit that.

Agent

Committed the airline field.

You

What do we need next for the project?

Agent

Recommended itinerary construction, better CLI output, and bounded agent follow-ups.

You

Add itinerary first, then improve CLI output.

Agent

Implemented itinerary construction and improved human-readable CLI formatting.

You

Give CLI examples.

Agent

Added practical one-way, round-trip, budget, stops, currency, calendar, JSON, and debug examples.

You

The round-trip output returned around 300 results, likely every outbound/return pair.

Agent

Explained the combinatorial pairing and moved toward native round-trip search plus selected-outbound return expansion.

You

How should round-trip progression work? Let the agent choose the best outbound first, then the return?

Agent

Implemented selected-outbound expansion before return selection.

You

What is the advantage versus two one-way searches paired by airline?

Agent

Compared native round-trip search, one-way pairing, price consistency, booking fidelity, and provider semantics.

You

Would a Pareto set be a good helper/filtering mechanism? Objectives: price, duration, stops, calendar fit, buffer, and other useful factors.

Agent

Designed the Pareto helper around hard constraints first, then price, duration, and stops, with calendar availability and buffer applied before Pareto filtering.

You

Think of other useful objectives.

Agent

Considered schedule fit, airline consistency, airport changes, connection risk, fare confidence, and timezone handling, while keeping hard constraints small.

You

Commit current work, then implement that.

Agent

Implemented the focused filtering/Pareto slice and committed it.

You

Keep hard constraints small.

Agent

Kept budget, stop count, exact dates, route validity, and calendar availability as the small hard-constraint set.

You

What is next for the agent?

Agent

Recommended a bounded raw LLM loop with deterministic tools and explicit state transitions.

You

Implement that.

Agent

Implemented the bounded agent state machine for outbound selection, return expansion, return selection, and deterministic fallback.

You

Commit your changes.

Agent

Committed the agent changes.

You

What can we do for agent/LLM now?

Agent

Outlined structured candidate selection, follow-ups, safe action schemas, and preserving deferred outbound choices.

You

Does that work for one-way searches? Is raw LLM, LangChain, PydanticAI, Genkit, or something else best?

Agent

Confirmed the bounded approach works for one-way and round-trip searches, and recommended raw bounded control for this small deterministic state machine.

You

What about Genkit?

Agent

Compared Genkit with the current Python stack and kept the simpler bounded controller.

You

Go with a raw bounded loop and small state machine.

Agent

Kept the implementation framework-free and bounded.

You

Can I test it through the CLI?

Agent

Added CLI testing guidance and debug output.

You

Enable debug thinking to stdout.

Agent

Added safe debug logs for state transitions, provider calls, candidate IDs, and deterministic fallbacks without exposing hidden reasoning or secrets.

You

The model chooses an invalid candidate and SerpAPI then says departure_id is missing. Commit before fixing.

Agent

Committed before fixing the expansion parameter mapping.

You

The next run selects invalid candidates but return expansion works. How exactly is the agentic loop working?

Agent

Explained the searching → selecting outbound → expanding return → selecting return → complete state sequence, turn budget, candidate IDs, and fallback behavior.

You

Tune the prompt for Gemma.

Agent

Tightened selector instructions: exact JSON, exact allowed IDs, no indexes or invented IDs, and explicit preference ordering.

You

Is Pareto also working on the return date?

Agent

Confirmed return expansion is filtered and Pareto-ranked after the selected outbound is expanded.

You

Commit your work.

Agent

Committed the Gemma prompt tuning.

You

The agent now returns selected and alternative flights with rationale and notes. Anything worth investigating, or is this a good v1?

Agent

Identified useful follow-up work: calendar integration, better UX, deferred alternatives, booking resolution, date flexibility, and evaluation.

You

Look at PROJECT and research what is missing.

Agent

Reviewed the project direction and prioritized calendar-aware availability for the next slice.

You

Implement calendar integration. Consider local ICS versus Google Calendar.

Agent

Implemented local calendar support first, keeping Google Calendar as a later integration because it adds auth and operational overhead.

You

Availability matching/filtering should happen before Pareto filtering.

Agent

Applied calendar availability and travel buffer filtering before Pareto selection.

You

Implement that and add README setup instructions for local ICS.

Agent

Added local ICS setup documentation and availability filtering.

You

Why use ics or icalendar?

Agent

Compared the libraries and recommended icalendar for parsing, with recurring-ical-events for recurrence expansion.

You

Refactor to icalendar; it is already installed.

Agent

Refactored calendar parsing to icalendar and recurring-ical-events.

You

An ICS search returned six flights and says one calendar event was applied. Is this good?

Agent

Confirmed the calendar was applied before Pareto filtering and explained the selected return expansion.

You

I added recurring-ical-events.

Agent

Integrated recurring event expansion into the calendar window.

You

Commit your work.

Agent

Committed the calendar slice.

You

Expand README for Hacktoberfest Week 2 Side Hustle using the project direction.

Agent

Expanded the README with the project pitch, open-model rationale, architecture, current slice, setup, CLI, calendar, and future work.

You

Improve README examples for constraints, budgets, and user prompts.

Agent

Added examples and a table for routes, dates, budgets, currency, stops, and preferences.

You

I added MIT License and remote repository. Directions include date stabilization, flexible dates, web UI/history, and calendar integration. What else is missing?

Agent

Suggested user stories for ISO date stabilization, flexible dates, local web chat, history, calendar integrations, booking links, provider evaluation, and deployment.

You

Add those as separate issues and add missing backlog stories using GitHub MCP.

Agent

Created the requested issues and backlog items.

You

Implement issue #6 with the agent; it currently generates no responses.

Agent

Added agent response/rationale output.

You

Buckets may hardcode rationales. Better to have three categories with one or more choices.

Agent

Refactored toward structured choices and verified rationale data.

You

Refactor into that.

Agent

Refactored recommendation handling.

You

For round trips, only the selected outbound gets expanded. Save other good outbound choices for later?

Agent

Explained the deferred outbound design and recommended a separate issue for preserving future choices.

You

Create a separate issue. Do we need a small database connector for memory and options?

Agent

Recommended in-memory session state for v1 instead of a database.

You

Chat history and DB are not needed for v1.

Agent

Kept sessions in memory with no persistence.

You

Evaluate whether a framework is needed for increasingly agentic small-model loops.

Agent

Recommended staying with the bounded state machine because it is explicit, testable, and smaller than a general agent framework.

You

Buckets seem wrong; alternatives are enough.

Agent

Moved toward selected itinerary plus explicit alternatives and follow-up actions.

You

Go with alternatives.

Agent

Implemented alternatives and deferred outbound choices.

You

Commit with closes #18 if it truly closes.

Agent

Committed the alternatives work.

You

What about AgentSession?

Agent

Explained AgentSession as the in-memory state boundary for agent and application follow-ups.

You

Implement that.

Agent

Implemented the application session wrapper and follow-up message state.

You

Can this be tested through CLI, or do we need a small web UI?

Agent

Recommended a tiny FastAPI UI for testing follow-ups while keeping the CLI focused.

You

Why not a small index.html and FastAPI main.py?

Agent

Implemented the initial small local web UI with in-memory sessions.

You

Move web.py into web/ and make uv run web the shorthand.

Agent

Moved the web module and added the web project script.

You

The web UI cannot show outbound candidates, rationale, or agentic follow-ups.

Agent

Improved the UI to show selected options, alternatives, rationale, candidate buttons, and follow-up actions.

You

Try candidate buttons and follow-up buttons. Are workarounds removing a chat session?

Agent

Clarified that sessions remain in memory and buttons submit bounded follow-up actions.

You

Maybe a framework could reduce LOC; perhaps LangGraph.

Agent

Compared framework tradeoffs and kept the state machine as the better fit.

You

Reset the web app and try LangGraph support in a new branch.

Agent

Evaluated the bounded workflow and did not retain a LangGraph implementation.

You

Improve the user experience for the agent part.

Agent

Improved chat/result presentation and action affordances.

You

How do we integrate calendar in the UI?

Agent

Added local ICS upload support and travel-buffer controls.

You

Would a frontend framework or Python frontend help?

Agent

Compared Gradio, Streamlit, and a small custom frontend.

You

Commit current work, then implement ICS upload.

Agent

Committed the current UI work and implemented local ICS upload.

You

It works, but Gradio may be a better frontend.

Agent

Implemented a Gradio experiment.

You

Gradio does not show alternatives and looks ugly. Is React/Vite better?

Agent

Refactored toward a small React/Vite frontend.

You

Vite assets return 404 through FastAPI.

Agent

Fixed the Vite base path and FastAPI static serving.

You

Commit the React frontend.

Agent

Committed the React frontend, then the user clarified the commit should simply say added React frontend.

You

Actually reset that. Use a small index.html and HTMX instead.

Agent

Reset the React commit to the prior web commit, moved the untracked React backup out of the repository, and replaced the frontend with a single HTMX-enhanced server-rendered page.

You

HTMX upload raises TypeError because start_session does not accept calendar_text.

Agent

Added in-memory calendar_text support to start_session and a regression test. 28 tests passed.

You

The UI only shows one flight.

Agent

Diagnosed Pareto dominance and added a bounded candidate pool: keep the Pareto frontier plus ranked feasible alternatives, capped at six. 29 tests passed.

You

Commit your work so far; next work is UI improvement.

Agent

Committed as 55b4395 with HTMX UI, calendar uploads, follow-ups, rationale, and bounded alternatives. The untracked calendar fixture was preserved.

You

Save this entire session with DevRelay and return the embed tag.

Agent

Submitting this sanitized normalized transcript to DevRelay.

Building AirGem: from local flight copilot to hosted demo
You

Build and refine AirGem, a narrow flight-search copilot. Improve the local web UI, add visible agent stages, support booking URLs, and prepare a privacy-conscious live demo.

Agent

AirGem was refined around a midnight-purple web interface with visible agent progress stages, transient SerpApi credentials, rate limiting, Render deployment configuration, and deterministic flight evaluation around Gemma synthesis.

You

Decide whether DigitalOcean and Sentry are necessary, and explain the role of Gemma, SerpApi, and the hosted deployment.

Agent

The project kept Gemma as a bounded interpretation and synthesis layer, SerpApi as the flight-data provider, and Render as the live application host. DigitalOcean and Sentry were left out because they were not meaningful requirements for this narrow demo.

You

Prepare the project for a live demo and the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle.

Agent

The project was documented with a live demo link, a system-flow diagram, privacy protections, local setup instructions, and a challenge-oriented README.

I used agentic coding extensively, while remaining responsible for the infrastructure, product direction, and key technical decisions.

My First User

A colleague's brother, who was interested in travel assistants and flight-cost optimization, tried AirGem as an early user.

What worked best for him was the comparison flow. Instead of manually remembering every constraint, he could import his calendar with one upload and set a travel buffer. AirGem then used that information while presenting flight options. This reduced the amount he had to keep in his head and helped prevent conflicts he might only have noticed later, which made the search process feel much more streamlined. He also highlighted how some preferences guided the agent on the top selection, for example, "I only want to travel in evenings".

The main limitation was that the available travel days were still too rigid. He wanted the system to search across flexible dates around his calendar instead of treating the requested days as fixed. He asked for flexible-date search to become the top priority for the next iteration.

Would he pay for it? Yes, if AirGem added a few features that would make it useful beyond a single search, especially automatic flight monitoring and flexible-date search. That was a useful signal: the core comparison and calendar workflow already solved a real problem, but recurring value would come from helping users continue to optimize after the first search.

Prize Categories

  • Overall category
  • Best Use of Gemma
  • Best Use of SerpApi
  • Best Use of Render

What I Would Build Next

The next priorities are flexible-date search around calendar constraints, automatic flight monitoring, deeper itinerary normalization, more robust round-trip booking-link handling across providers, and richer calendar-aware explanations. Another useful feature would be letting users save and compare multiple itinerary shortlists during the same planning session.

AI-assisted development was used while building and documenting this project.

Top comments (0)