DEV Community

shrey vijayvargiya
shrey vijayvargiya

Posted on

AI Travel Itinerary Application

Building one using Next.js, Vercel AI SDK and Other APIs

Under the Hood

AI travel planner is quite the trending idea since the evolution or launching of chatGPT.

LLM apps can provide tonnes of data related to the travel industry, all models like GPT 4 and other versions can even provide more details, such as latitude and longitude from Google Maps, Google or other websites such as unsplash images.

Year by year, LLM models can easily provide more details data, such as Airbnb, Google Maps, flight details, along with passport and other details, making the process of AI integration more imperative in the field of tourism.

One single endpoint using Vercel AI SDK and Next.js server-side API can provide a simple travel itinerary from a single prompt

export default async function handler(req, res) {
 if (req.method !== "POST") {
  return res.status(405).json({ error: "Method Not Allowed" });
 }

 try {
  const { prompt } = req.body;

  if (!prompt) {
   return res.status(400).json({ error: "Missing prompt parameter" });
  }

  // Set headers for streaming
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");
  res.setHeader("Transfer-Encoding", "chunked");

  const result = await streamText({
   model: googleClient("gemini-1.5-flash"),
   messages: [
    {
     role: "system",
     content: Create a simple itinerary for the following prompt: prompt,
    },
    {
     role: "user",
     content: prompt,
    },
   ],

   temperature: 0.7,
   streamProtocol: true,
  });

  try {
   for await (const chunk of result.textStream) {
    res.write(chunk);
    res.flush();
   }
   res.end();
  } catch (streamError) {
   console.error("Streaming error:", streamError);
   res.write(
    `data: ${JSON.stringify({
     error: "Streaming error",
     details: streamError.message,
    })}\n\n`
   );
   res.end();
  }
 } catch (error) {
  console.error("API Error:", error);
  res.write(
   `data: ${JSON.stringify({
    error: "Internal Server Error",
    details: error.message,
   })}\n\n`
  );
  res.end();
 }
}
Enter fullscreen mode Exit fullscreen mode

The AI response can provide specific details for each location, including images and location coordinates.

For example, “5 days spiritual trip to Banaras” as a prompt to LLM will generate an itinerary containing locations, places, coordinates, with related images.

One can think of fine-tuning the LLM model for a specific use-case, but in general itinerary I believe a simple LLM model might work well.

In the above code, I am using Next.js server-side API to make an HTTP API call to the LLM model using the streamText method provided by Vercel AI SDK.

AI SDK is the kind of wrapper that opens up the LLM model to talk using npm exported modules or classes, and streamText is one of them.

const result = await streamText({
   model: googleClient("gemini-1.5-flash"),
   messages: [
    {
     role: "system",
     content: Create simple itinerary for the following prompt: prompt,
    },
    {
     role: "user",
     content: prompt,
    },
   ],

   temperature: 0.7,
   streamProtocol: true,
  });
Enter fullscreen mode Exit fullscreen mode

streamText method is async in nature and needs a few required parameters, such as model and messages.

model By default is the LLM model is used, and messages are in an array object containing temperature and messages or a prompt with other customisation key values.

One thing to understand, the vercel AI SDK streamText method returns chunks just like streaming or chatgpt-like chunking of AI messages. Similarly, we read the chunk and send each chunk back to the client as the API response.


for await (const chunk of result.textStream) {
    res.write(chunk);
    res.flush();
   }
res.end();
Enter fullscreen mode Exit fullscreen mode

This is important, otherwise the client side will always render the final output, skipping the jumping state.

Vercel AI SDK provides a few hooks as well, such as useChat to handle each LLM response or AI response on the client side accordingly with streaming in chunks, rendering the chunks on the client side, data background processing and much more.

Prompting

This is where most of the techniques differ, and hence the output of the LLM.

Prompting is easy in one sense, but very tricky to validate the AI response.

System prompt is important to train the LLM model for a specific niche idea to prevent hallucinations and generalisation.

System prompt becomes important as this will take care of more differences, for example, a simple travel itinerary for Banaras is different from 5 5-day spiritual itinerary for a Banaras trip with some location details, images and coordinates.

Since we understand that the system places importance, can one also preconceive this importance to be taken care of by the user?

But not every time a user wants to add entire same long prompt for each itinerary in the world, and that's where the system prompt can change the game.

const generateItineraryPrompt = (prompt) => {
 return `You are a travel itinerary expert. Generate a detailed travel itinerary based on the following requirements:

 ${prompt}

 Please follow these guidelines:
 1. Analyze the prompt to determine the start and end destinations, number of days, and any specific requirements
 2. For each location mentioned in the itinerary, create:
    a. A specific and optimized Google image search query that will return the best possible images. Follow these rules for image queries:
       - Include the full name of the location
       - Add descriptive terms like "landmark", "tourist spot", "famous", "beautiful", "scenic", "aerial view" where appropriate
       - Include specific features or attractions of the location
       - Use terms that will yield high-quality, professional photos
       - Avoid generic terms that might return irrelevant results
       - Format as: ![Location Name](query: "specific search query")

    b. A Google Maps location query for places that need coordinates. Follow these rules for location queries:
       - Always include the full name of the place
       - Always include the city/area name
       - Always include the country
       - For restaurants: include "restaurant" and street name if available
       - For hotels: include "hotel" and street name if available
       - For attractions: include specific identifiers (e.g., "temple", "museum", "park")
       - For meeting points: include nearby landmarks
       - Format as: [Location Name](location: "specific location query")
       - Use this format for ALL places that need coordinates: restaurants, hotels, attractions, meeting points, etc.
       - Be as specific as possible to ensure accurate coordinates

 3. Format the response in Markdown with the following structure:

 # Travel Itinerary

 ## Overview
 - Brief summary of the trip
 - Total duration
 - Main highlights

 ## Day-by-Day Breakdown

 ### Day 1: [Location Name]
 ![Location Name](query: "location name landmark scenic view")

 #### Morning
 - Activity 1 (Time) at [Place Name](location: "Place Name, Street Name, City, Country")
 - Activity 2 (Time) at [Place Name](location: "Place Name, Street Name, City, Country")

 #### Afternoon
 - Lunch at [Restaurant Name](location: "Restaurant Name, Street Name, City, Country restaurant")
 - Activity 1 (Time) at [Place Name](location: "Place Name, Street Name, City, Country")

 #### Evening
 - Dinner at [Restaurant Name](location: "Restaurant Name, Street Name, City, Country restaurant")
 - Activity 1 (Time) at [Place Name](location: "Place Name, Street Name, City, Country")

 #### Accommodation
 - [Hotel Name](location: "Hotel Name, Street Name, City, Country hotel")
 - Estimated cost

 #### Local Cuisine
 - Restaurant recommendations with location queries
 - Must-try dishes

 #### Transportation
 - How to get there
 - Estimated cost

 [Repeat for each day]

 ## Budget Breakdown
 - Accommodation
 - Transportation
 - Activities
 - Food
 - Miscellaneous

 ## Travel Tips
 - Best time to visit
 - Local customs and etiquette
 - Safety considerations
 - Packing suggestions

 Make sure to:
 1. Include specific details about each location and activity
 2. Provide accurate time estimates
 3. Include practical information like costs and transportation options
 4. Format all content in proper Markdown
 5. For each location:
    - Create an optimized image search query that will return the best possible images
    - Add a location query for places that need coordinates
 6. Use the formats:
    - ![Location Name](query: "specific search query") for images
    - [Location Name](location: "specific location query") for Google Maps coordinates

 Example of good queries:
 - Image query for Eiffel Tower: "Eiffel Tower Paris landmark aerial view sunset"
 - Location query for Eiffel Tower: "Eiffel Tower, Champ de Mars, 75007 Paris, France"

 - Image query for Tokyo Skytree: "Tokyo Skytree Japan modern architecture night view"
 - Location query for Tokyo Skytree: "Tokyo Skytree, 1 Chome-1-2 Oshiage, Sumida City, Tokyo, Japan"

 - Image query for Grand Canyon: "Grand Canyon, Arizona, USA scenic landscape aerial view"
 - Location query for Grand Canyon: "Grand Canyon National Park, Arizona, United States"

 - Image query for Sensō-ji Temple: "Sensō-ji Temple Tokyo Asakusa district famous pagoda"
 - Location query for Sensō-ji Temple: "Sensō-ji Temple, 2 Chome-3-1 Asakusa, Taito City, Tokyo, Japan"

 - Image query for Le Jules Verne: "Le Jules Verne Restaurant Eiffel Tower Paris fine dining"
 - Location query for Le Jules Verne: "Le Jules Verne Restaurant, Eiffel Tower, 75007 Paris, France"

 - Image query for Park Hyatt Tokyo: "Park Hyatt Tokyo hotel luxury rooms city view"
 - Location query for Park Hyatt Tokyo: "Park Hyatt Tokyo, 3-7-1-2 Nishishinjuku, Shinjuku City, Tokyo, Japan"`;
};
Enter fullscreen mode Exit fullscreen mode

In our system prompt (as shown above), I’ve added a few details such as providing the location name, the format of each itinerary the AI should provide, images, along with a placeholder and a few other details such as historical importance and top places to visit.

You can go into more details as per your choice, but the above prompt is good enough for me to generate a detailed itinerary using the LLM model.

Third-Party APIs

One can question how to use a third-party API within the LLM response.

You can either instruct the LLM model to use APIs for fetching images, coordinates, and flight details via the function or the tool calling. This is a totally new concept to allow the LLM model to use APIs or functions to fetch the real-time or updated data further, while responding.

For example, using Google Maps API as the function call, our LLM model will make an API call to the Google Maps HTTP endpoint, fetching each location's coordinates, similar can be done for the unsplash or Pexels APIs to fetch images of the locations.

Another method is to first let the AI model provide the response with itineraries and proxy/dummy data/placeholder data, and post AI response invoke API calls to fetch the locations or images or flight details or even scrape the internet to fetch the real-time data.

Both ways work perfectly well, and it depends on your execution, which will be a solid solution.

In our case, we have only instructed the LLM to add placeholders for the images and coordinates for the locations, and further, we use Google Maps and unsplash APIs to fetch the coordinates and images, respectively.

But in future, LLM can easily use MCP servers to fetch real-time coordinates and images with other details such as Airbnbs, hotels and flight details and more.

I hope this makes sense, see you in the next one

Shrey

Originally published on iHateReading

Top comments (0)