├── .env.example ├── travel-planner ├── .eslintrc.json ├── app │ ├── favicon.ico │ ├── fonts │ │ ├── GeistVF.woff │ │ └── GeistMonoVF.woff │ ├── components │ │ ├── .ResultsPage.tsx.swp │ │ ├── LoadingPage.tsx │ │ ├── ResultsPage.tsx │ │ └── TravelForm.tsx │ ├── layout.tsx │ ├── globals.css │ └── page.tsx ├── next.config.mjs ├── postcss.config.mjs ├── lib │ └── utils.ts ├── components.json ├── tsconfig.json ├── components │ └── ui │ │ ├── label.tsx │ │ ├── input.tsx │ │ ├── button.tsx │ │ └── card.tsx ├── tailwind.config.ts ├── package.json └── README.md ├── main.py ├── .gitignore ├── app.py ├── config ├── agents.yaml └── tasks.yaml ├── my_crew.py └── README.md /.env.example: -------------------------------------------------------------------------------- 1 | SERPER_API_KEY= 2 | OPENAI_API_KEY= 3 | OPENAI_MODEL=gpt-4o -------------------------------------------------------------------------------- /travel-planner/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next/typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /travel-planner/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tylerprogramming/travel-planner/HEAD/travel-planner/app/favicon.ico -------------------------------------------------------------------------------- /travel-planner/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /travel-planner/app/fonts/GeistVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tylerprogramming/travel-planner/HEAD/travel-planner/app/fonts/GeistVF.woff -------------------------------------------------------------------------------- /travel-planner/app/fonts/GeistMonoVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tylerprogramming/travel-planner/HEAD/travel-planner/app/fonts/GeistMonoVF.woff -------------------------------------------------------------------------------- /travel-planner/app/components/.ResultsPage.tsx.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tylerprogramming/travel-planner/HEAD/travel-planner/app/components/.ResultsPage.tsx.swp -------------------------------------------------------------------------------- /travel-planner/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /travel-planner/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /travel-planner/app/components/LoadingPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export function LoadingPage() { 4 | return ( 5 |
6 |
7 |

Planning your perfect trip...

8 |
9 | ) 10 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from my_crew import SurpriseTravelCrew 2 | 3 | def run_crew(): 4 | inputs = { 5 | 'origin': 'New York City, JFK', 6 | 'destination': 'Rome, FCO', 7 | 'age': 30, 8 | 'hotel_location': 'Rome', 9 | 'flight_information': 'JFK123, leaving 10:00AM October 10th, 2024 - coming back 10:00PM October 15th, 2024', 10 | 'trip_duration': '5 days' 11 | } 12 | 13 | result = SurpriseTravelCrew().crew().kickoff(inputs=inputs) 14 | print(result) 15 | 16 | run_crew() -------------------------------------------------------------------------------- /travel-planner/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } -------------------------------------------------------------------------------- /travel-planner/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv-surprise-trip/ 2 | .env 3 | __pycache__/ 4 | 5 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 6 | 7 | .venv-surprise-trip/ 8 | .env 9 | __pycache__/ 10 | 11 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 12 | 13 | # dependencies 14 | node_modules 15 | .pnp 16 | .pnp.js 17 | .yarn/install-state.gz 18 | 19 | # testing 20 | /coverage 21 | 22 | # next.js 23 | .next/ 24 | out/ 25 | 26 | # production 27 | build 28 | 29 | # misc 30 | .DS_Store 31 | *.pem 32 | 33 | # debug 34 | npm-debug.log* 35 | yarn-debug.log* 36 | yarn-error.log* 37 | 38 | # local env files 39 | .env*.local 40 | 41 | # vercel 42 | .vercel 43 | 44 | # typescript 45 | *.tsbuildinfo 46 | next-env.d.ts 47 | 48 | # Node modules 49 | node_modules/ 50 | **/node_modules/ -------------------------------------------------------------------------------- /travel-planner/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /travel-planner/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import localFont from "next/font/local"; 3 | import "./globals.css"; 4 | 5 | const geistSans = localFont({ 6 | src: "./fonts/GeistVF.woff", 7 | variable: "--font-geist-sans", 8 | weight: "100 900", 9 | }); 10 | const geistMono = localFont({ 11 | src: "./fonts/GeistMonoVF.woff", 12 | variable: "--font-geist-mono", 13 | weight: "100 900", 14 | }); 15 | 16 | export const metadata: Metadata = { 17 | title: "Create Next App", 18 | description: "Generated by create next app", 19 | }; 20 | 21 | export default function RootLayout({ 22 | children, 23 | }: Readonly<{ 24 | children: React.ReactNode; 25 | }>) { 26 | return ( 27 | 28 | 31 | {children} 32 | 33 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /travel-planner/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | const config: Config = { 4 | content: [ 5 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 13 | 'gradient-conic': 14 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 15 | }, 16 | colors: { 17 | 'primary': '#3B82F6', 18 | 'secondary': '#10B981', 19 | 'accent': '#F59E0B', 20 | }, 21 | fontFamily: { 22 | 'sans': ['Inter', 'sans-serif'], 23 | }, 24 | borderRadius: { 25 | lg: 'var(--radius)', 26 | md: 'calc(var(--radius) - 2px)', 27 | sm: 'calc(var(--radius) - 4px)' 28 | } 29 | } 30 | }, 31 | plugins: [], 32 | }; 33 | export default config; 34 | -------------------------------------------------------------------------------- /travel-planner/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ) 21 | } 22 | ) 23 | Input.displayName = "Input" 24 | 25 | export { Input } 26 | -------------------------------------------------------------------------------- /travel-planner/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "travel-planner", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-icons": "^1.3.0", 13 | "@radix-ui/react-label": "^2.1.0", 14 | "@radix-ui/react-slot": "^1.1.0", 15 | "class-variance-authority": "^0.7.0", 16 | "clsx": "^2.1.1", 17 | "lucide-react": "^0.445.0", 18 | "next": "14.2.13", 19 | "react": "^18", 20 | "react-dom": "^18", 21 | "tailwind-merge": "^2.5.2", 22 | "tailwindcss-animate": "^1.0.7" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "^20", 26 | "@types/react": "^18", 27 | "@types/react-dom": "^18", 28 | "eslint": "^8", 29 | "eslint-config-next": "14.2.13", 30 | "postcss": "^8", 31 | "tailwindcss": "^3.4.1", 32 | "typescript": "^5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /travel-planner/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --foreground-rgb: 0, 0, 0; 7 | --background-start-rgb: 214, 219, 220; 8 | --background-end-rgb: 255, 255, 255; 9 | } 10 | 11 | @media (prefers-color-scheme: dark) { 12 | :root { 13 | --foreground-rgb: 255, 255, 255; 14 | --background-start-rgb: 0, 0, 0; 15 | --background-end-rgb: 0, 0, 0; 16 | } 17 | } 18 | 19 | body { 20 | color: rgb(var(--foreground-rgb)); 21 | background: linear-gradient( 22 | to bottom, 23 | transparent, 24 | rgb(var(--background-end-rgb)) 25 | ) 26 | rgb(var(--background-start-rgb)); 27 | } 28 | 29 | @layer components { 30 | .btn-primary { 31 | @apply bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-600 transition-colors; 32 | } 33 | 34 | .card { 35 | @apply bg-white rounded-lg shadow-md p-6; 36 | } 37 | 38 | .input { 39 | @apply border border-gray-300 rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, jsonify 2 | from flask_cors import CORS 3 | import time 4 | from my_crew import SurpriseTravelCrew 5 | 6 | app = Flask(__name__) 7 | CORS(app) 8 | 9 | @app.route('/plan-trip', methods=['POST']) 10 | def plan_trip(): 11 | data = request.json 12 | origin = data.get('origin') 13 | destination = data.get('destination') 14 | age = data.get('age') 15 | hotel_location = data.get('hotel_location') 16 | flight_information = data.get('flight_information') 17 | trip_duration = data.get('trip_duration') 18 | 19 | inputs = {} 20 | inputs['origin'] = origin 21 | inputs['destination'] = destination 22 | inputs['age'] = age 23 | inputs['hotel_location'] = hotel_location 24 | inputs['flight_information'] = flight_information 25 | inputs['trip_duration'] = trip_duration 26 | 27 | planner_crew = SurpriseTravelCrew().crew().kickoff(inputs=inputs) 28 | crew_output = planner_crew.to_dict() 29 | 30 | return jsonify(crew_output) 31 | 32 | if __name__ == '__main__': 33 | app.run(debug=True, port=3001) -------------------------------------------------------------------------------- /config/agents.yaml: -------------------------------------------------------------------------------- 1 | personalized_activity_planner: 2 | role: > 3 | Activity Planner 4 | goal: > 5 | Research and find cool things to do at the destination, including activities and events that match the traveler's interests and age group. 6 | backstory: > 7 | You are skilled at creating personalized itineraries that cater to the specific preferences and demographics of the traveler. 8 | 9 | restaurant_scout: 10 | role: > 11 | Restaurant Scout 12 | goal: > 13 | Find highly-rated restaurants and dining experiences at the destination, and recommend scenic locations and fun activities. 14 | backstory: > 15 | As a food lover, you know the best spots in town for a delightful culinary experience. You also have a knack for finding picturesque and entertaining locations. 16 | 17 | itinerary_compiler: 18 | role: > 19 | Itinerary Compiler 20 | goal: > 21 | Compile all researched information into a comprehensive day-by-day itinerary, ensuring the integration of flights and hotel information. 22 | backstory: > 23 | With an eye for detail, you organize all the information into a coherent and enjoyable travel plan. -------------------------------------------------------------------------------- /travel-planner/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. 37 | -------------------------------------------------------------------------------- /travel-planner/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | import { Slot } from "@radix-ui/react-slot" 3 | import { cva, type VariantProps } from "class-variance-authority" 4 | 5 | import { cn } from "@/lib/utils" 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | } 35 | ) 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button" 46 | return ( 47 | 52 | ) 53 | } 54 | ) 55 | Button.displayName = "Button" 56 | 57 | export { Button, buttonVariants } 58 | -------------------------------------------------------------------------------- /travel-planner/components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )) 18 | Card.displayName = "Card" 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )) 30 | CardHeader.displayName = "CardHeader" 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

41 | )) 42 | CardTitle.displayName = "CardTitle" 43 | 44 | const CardDescription = React.forwardRef< 45 | HTMLParagraphElement, 46 | React.HTMLAttributes 47 | >(({ className, ...props }, ref) => ( 48 |

53 | )) 54 | CardDescription.displayName = "CardDescription" 55 | 56 | const CardContent = React.forwardRef< 57 | HTMLDivElement, 58 | React.HTMLAttributes 59 | >(({ className, ...props }, ref) => ( 60 |

61 | )) 62 | CardContent.displayName = "CardContent" 63 | 64 | const CardFooter = React.forwardRef< 65 | HTMLDivElement, 66 | React.HTMLAttributes 67 | >(({ className, ...props }, ref) => ( 68 |
73 | )) 74 | CardFooter.displayName = "CardFooter" 75 | 76 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } 77 | -------------------------------------------------------------------------------- /travel-planner/app/components/ResultsPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" 3 | 4 | type ItineraryData = { 5 | days: Array<{ 6 | date: string 7 | activities: Array<{ 8 | name: string 9 | description: string 10 | location: string 11 | date: string 12 | cuisine: string 13 | why_its_suitable: string 14 | rating: string 15 | reviews: string 16 | }> 17 | restaurants: string[] 18 | flight: string | null 19 | }> 20 | name: string 21 | hotel: string 22 | } 23 | 24 | type ResultsPageProps = { 25 | itinerary: ItineraryData 26 | } 27 | 28 | export function ResultsPage({ itinerary }: ResultsPageProps) { 29 | return ( 30 |
31 |

{itinerary.name}

32 |

Hotel: {itinerary.hotel}

33 | {itinerary.days.map((day, index) => ( 34 | 35 | 36 | Day {index + 1}: {new Date(day.date).toLocaleDateString()} 37 | {day.flight && {day.flight}} 38 | 39 | 40 |

Activities:

41 | {day.activities.map((activity, actIndex) => ( 42 |
43 |

{activity.name}

44 |

{activity.description}

45 |

Location: {activity.location}

46 |

Cuisine: {activity.cuisine}

47 |

Why it's suitable: {activity.why_its_suitable}

48 |

Rating: {activity.rating} ({activity.reviews})

49 |
50 | ))} 51 | {day.restaurants.length > 0 && ( 52 | <> 53 |

Restaurants:

54 |
    55 | {day.restaurants.map((restaurant, resIndex) => ( 56 |
  • {restaurant}
  • 57 | ))} 58 |
59 | 60 | )} 61 |
62 |
63 | ))} 64 |
65 | ) 66 | } -------------------------------------------------------------------------------- /config/tasks.yaml: -------------------------------------------------------------------------------- 1 | personalized_activity_planning_task: 2 | description: > 3 | Research and find cool things to do at {destination}. 4 | Focus on activities and events that match the traveler's interests and age group. 5 | Utilize internet search tools and recommendation engines to gather the information. 6 | 7 | 8 | Traveler's information: 9 | 10 | 11 | - origin: {origin} 12 | 13 | - destination: {destination} 14 | 15 | - age of the traveler: {age} 16 | 17 | - hotel location: {hotel_location} 18 | 19 | - flight information: {flight_information} 20 | 21 | - how long is the trip: {trip_duration} 22 | expected_output: > 23 | A list of recommended activities and events for each day of the trip. 24 | Each entry should include the activity name, location, a brief description, and why it's suitable for the traveler. 25 | And potential reviews and ratings of the activities. 26 | 27 | restaurant_scouting_task: 28 | description: > 29 | Find highly-rated restaurants and dining experiences at {destination}. 30 | Recommend scenic locations and fun activities that align with the traveler's preferences. 31 | Use internet search tools, restaurant review sites, and travel guides. 32 | Make sure to find a variety of options to suit different tastes and budgets, and ratings for them. 33 | 34 | Traveler's information: 35 | 36 | 37 | - origin: {origin} 38 | 39 | - destination: {destination} 40 | 41 | - age of the traveler: {age} 42 | 43 | - hotel localtion: {hotel_location} 44 | 45 | - flight infromation: {flight_information} 46 | 47 | - how long is the trip: {trip_duration} 48 | expected_output: > 49 | A list of recommended restaurants, scenic locations, and fun activities for each day of the trip. 50 | Each entry should include the name, location (address), type of cuisine or activity, and a brief description and ratings. 51 | 52 | itinerary_compilation_task: 53 | description: > 54 | Compile all researched information into a comprehensive day-by-day itinerary for the trip to {destination}. 55 | Ensure the itinerary integrates flights, hotel information, and all planned activities and dining experiences. 56 | Use text formatting and document creation tools to organize the information. 57 | expected_output: > 58 | A detailed itinerary document, the itinerary should include a day-by-day 59 | plan with flights, hotel details, activities, restaurants, and scenic locations. -------------------------------------------------------------------------------- /travel-planner/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import React, { useState } from 'react' 4 | import { TravelForm } from './components/TravelForm' 5 | import { LoadingPage } from './components/LoadingPage' 6 | import { ResultsPage } from './components/ResultsPage' 7 | import Image from 'next/image' 8 | 9 | type FormData = { 10 | origin: string 11 | destination: string 12 | age: number 13 | hotel_location: string 14 | flight_information: string 15 | trip_duration: string 16 | } 17 | 18 | type ItineraryData = { 19 | days: Array<{ 20 | date: string 21 | activities: Array<{ 22 | name: string 23 | description: string 24 | location: string 25 | date: string 26 | cuisine: string 27 | why_its_suitable: string 28 | rating: string 29 | reviews: string 30 | }> 31 | restaurants: string[] 32 | flight: string | null 33 | }> 34 | name: string 35 | hotel: string 36 | } 37 | 38 | export default function TravelPlanner() { 39 | const [stage, setStage] = useState<'form' | 'loading' | 'results'>('form') 40 | const [formData, setFormData] = useState(null) 41 | const [itineraryData, setItineraryData] = useState(null) 42 | const [error, setError] = useState(null) 43 | 44 | const handleFormSubmit = async (data: FormData) => { 45 | setFormData(data) 46 | setStage('loading') 47 | setError(null) 48 | 49 | try { 50 | const response = await fetch('http://localhost:3001/plan-trip', { 51 | method: 'POST', 52 | headers: { 53 | 'Content-Type': 'application/json', 54 | }, 55 | body: JSON.stringify(data), 56 | }) 57 | 58 | if (!response.ok) { 59 | throw new Error('Failed to fetch itinerary') 60 | } 61 | 62 | const itinerary: ItineraryData = await response.json() 63 | setItineraryData(itinerary) 64 | setStage('results') 65 | } catch (error) { 66 | console.error('Error:', error) 67 | setError('An error occurred while planning your trip. Please try again.') 68 | setStage('form') 69 | } 70 | } 71 | 72 | return ( 73 |
74 |
75 |

Travel Planner

76 |
77 |
78 | {stage === 'form' && } 79 | {stage === 'loading' && } 80 | {stage === 'results' && itineraryData && } 81 |
82 |
83 | ) 84 | } -------------------------------------------------------------------------------- /travel-planner/app/components/TravelForm.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { Button } from "@/components/ui/button" 3 | import { Input } from "@/components/ui/input" 4 | import { Label } from "@/components/ui/label" 5 | 6 | type FormData = { 7 | origin: string 8 | destination: string 9 | age: number 10 | hotel_location: string 11 | flight_information: string 12 | trip_duration: string 13 | } 14 | 15 | type TravelFormProps = { 16 | onSubmit: (data: FormData) => void 17 | } 18 | 19 | export function TravelForm({ onSubmit }: TravelFormProps) { 20 | const [formData, setFormData] = useState({ 21 | origin: '', 22 | destination: '', 23 | age: 0, 24 | hotel_location: '', 25 | flight_information: '', 26 | trip_duration: '' 27 | }) 28 | 29 | const handleChange = (e: React.ChangeEvent) => { 30 | const { name, value } = e.target 31 | setFormData(prev => ({ ...prev, [name]: name === 'age' ? parseInt(value) || 0 : value })) 32 | } 33 | 34 | const handleSubmit = (e: React.FormEvent) => { 35 | e.preventDefault() 36 | onSubmit(formData) 37 | } 38 | 39 | return ( 40 |
41 |
42 | 43 | 51 |
52 |
53 | 54 | 62 |
63 |
64 | 65 | 73 |
74 |
75 | 76 | 84 |
85 |
86 | 87 | 95 |
96 |
97 | 98 | 106 |
107 | 108 |
109 | ) 110 | } -------------------------------------------------------------------------------- /my_crew.py: -------------------------------------------------------------------------------- 1 | from crewai import Agent, Crew, Process, Task 2 | from crewai.project import CrewBase, agent, crew, task 3 | from crewai_tools import SerperDevTool, ScrapeWebsiteTool 4 | from pydantic import BaseModel, Field 5 | from typing import List, Optional 6 | 7 | class Activity(BaseModel): 8 | name: str = Field(..., description="The name of the activity") 9 | description: str = Field(..., description="A description of the activity") 10 | location: str = Field(..., description="The location of the activity") 11 | date: str = Field(..., description="The date of the activity") 12 | cuisine: str = Field(..., description="The cuisine of the activity") 13 | why_its_suitable: str = Field(..., description="Why it's suitable for the user") 14 | rating: str = Field(..., description="The rating of the activity") 15 | reviews: str = Field(..., description="The reviews of the activity") 16 | 17 | class DayPlan(BaseModel): 18 | date: str = Field(..., description="The date of the day plan") 19 | activities: List[Activity] = Field(..., description="The activities of the day plan") 20 | restaurants: List[str] = Field(..., description="The restaurants of the day plan") 21 | flight: Optional[str] = Field(..., description="The flight information") 22 | 23 | class Itinerary(BaseModel): 24 | days: List[DayPlan] = Field(..., description="The day plans of the trip") 25 | name: str = Field(..., description="The name of the itinerary") 26 | hotel: str = Field(..., description="The hotel of the itinerary") 27 | 28 | @CrewBase 29 | class SurpriseTravelCrew(): 30 | """Surprise Travel Crew""" 31 | 32 | agents_config = 'config/agents.yaml' 33 | tasks_config = 'config/tasks.yaml' 34 | 35 | @agent 36 | def personalized_activity_planner(self) -> Agent: 37 | return Agent( 38 | config=self.agents_config['personalized_activity_planner'], 39 | tools=[SerperDevTool(), ScrapeWebsiteTool()], 40 | verbose=True, 41 | allow_delegation=False, 42 | ) 43 | 44 | @agent 45 | def restaurant_scout(self) -> Agent: 46 | return Agent( 47 | config=self.agents_config['restaurant_scout'], 48 | tools=[SerperDevTool(), ScrapeWebsiteTool()], 49 | verbose=True, 50 | allow_delegation=False, 51 | ) 52 | 53 | @agent 54 | def itinerary_compiler(self) -> Agent: 55 | return Agent( 56 | config=self.agents_config['itinerary_compiler'], 57 | tools=[SerperDevTool()], 58 | verbose=True, 59 | allow_delegation=False, 60 | ) 61 | 62 | @task 63 | def personalized_activity_planning_task(self) -> Task: 64 | return Task( 65 | config=self.tasks_config['personalized_activity_planning_task'], 66 | agent=self.personalized_activity_planner(), 67 | ) 68 | 69 | @task 70 | def restaurant_scouting_task(self) -> Task: 71 | return Task( 72 | config=self.tasks_config['restaurant_scouting_task'], 73 | agent=self.restaurant_scout(), 74 | ) 75 | 76 | @task 77 | def itinerary_compilation_task(self) -> Task: 78 | return Task( 79 | config=self.tasks_config['itinerary_compilation_task'], 80 | agent=self.itinerary_compiler(), 81 | output_json=Itinerary 82 | ) 83 | 84 | @crew 85 | def crew(self) -> Crew: 86 | """Creates a SurpriseTravel Crew""" 87 | return Crew( 88 | agents=self.agents, 89 | tasks=self.tasks, 90 | process=Process.sequential, 91 | verbose=True, 92 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Surprise Trip Planner 2 | 3 | This project is a surprise trip planner that uses AI to help create personalized travel itineraries. It consists of a Python Flask backend and a Next.js frontend. 4 | 5 | ## Prerequisites 6 | 7 | - Python 3.8 or higher 8 | - Node.js 14 or higher 9 | - npm or yarn 10 | 11 | ## Installation 12 | 13 | ### Backend Setup 14 | 15 | 1. Clone the repository: 16 | ```bash 17 | git clone 18 | cd 19 | ``` 20 | 21 | 2. Create a virtual environment: 22 | ```bash 23 | python -m venv .venv-surprise-trip 24 | ``` 25 | 26 | 3. Activate the virtual environment: 27 | - On Windows: 28 | ```bash 29 | .venv-surprise-trip\Scripts\activate 30 | ``` 31 | - On macOS and Linux: 32 | ```bash 33 | source .venv-surprise-trip/bin/activate 34 | ``` 35 | 36 | 4. Install the required Python packages: 37 | ```bash 38 | pip install -r requirements.txt 39 | ``` 40 | 41 | 5. Copy the `.env.example` file to `.env` and fill in the necessary environment variables: 42 | ```bash 43 | cp .env.example .env 44 | ``` 45 | Open the `.env` file in a text editor and fill in any required values. 46 | 47 | ### Frontend Setup 48 | 49 | 1. Navigate to the frontend directory: 50 | ```bash 51 | cd travel-planner 52 | ``` 53 | 54 | 2. Install the required npm packages: 55 | ```bash 56 | npm install 57 | ``` 58 | or if you're using yarn: 59 | ```bash 60 | yarn install 61 | ``` 62 | 63 | ## Running the Application 64 | 65 | To run the full application, you'll need to start both the backend Flask server and the frontend Next.js development server. 66 | 67 | ### Backend (Flask App) 68 | 69 | 1. Ensure you're in the root directory of the project and your virtual environment is activated. 70 | 71 | 2. Set the Flask environment variables: 72 | - On Windows: 73 | ```bash 74 | set FLASK_APP=app.py 75 | set FLASK_ENV=development 76 | ``` 77 | - On macOS and Linux: 78 | ```bash 79 | export FLASK_APP=app.py 80 | export FLASK_ENV=development 81 | ``` 82 | 83 | 3. Start the Flask development server: 84 | ```bash 85 | flask run --port=3001 86 | ``` 87 | 88 | This will start the Flask server on `http://localhost:3001`. 89 | 90 | ### Frontend (Next.js) 91 | 92 | 1. Open a new terminal window (keep the Flask server running in the previous terminal). 93 | 94 | 2. Navigate to the `travel-planner` directory: 95 | ```bash 96 | cd travel-planner 97 | ``` 98 | 99 | 3. Start the Next.js development server: 100 | ```bash 101 | npm run dev 102 | ``` 103 | or if you're using yarn: 104 | ```bash 105 | yarn dev 106 | ``` 107 | 108 | This will start the Next.js server, typically on `http://localhost:3000`. 109 | 110 | ## Accessing the Application 111 | 112 | - The Flask backend API will be available at `http://localhost:3001` 113 | - The Next.js frontend will be available at `http://localhost:3000` 114 | 115 | You can now interact with the frontend in your web browser, which will communicate with the Flask backend to process requests. 116 | 117 | ## Development Workflow 118 | 119 | 1. Make changes to the Flask backend in `app.py` and other Python files. 120 | 2. The Flask development server will automatically reload when you save changes. 121 | 3. Make changes to the Next.js frontend in the `travel-planner` directory. 122 | 4. The Next.js development server will automatically reload when you save changes. 123 | 124 | Remember to keep both the Flask and Next.js servers running while developing. 125 | 126 | ## Project Structure 127 | 128 | - `app.py`: Main entry point for the Flask backend application 129 | - `main.py`: Contains core logic for the surprise trip planner 130 | - `my_crew.py`: Defines the AI crew used for planning 131 | - `config/`: Contains configuration files for agents and tasks 132 | - `travel-planner/`: Next.js frontend application 133 | 134 | ## Troubleshooting 135 | 136 | - If you encounter any package-related issues, ensure that your virtual environment is activated and all dependencies are installed correctly. 137 | - Check that the ports 3001 and 3000 are not being used by other applications. 138 | - Ensure that your `.env` file is properly configured with any necessary API keys or settings. 139 | 140 | ## Contributing 141 | 142 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests. 143 | 144 | ## License 145 | 146 | This project is licensed under the [MIT License](LICENSE). --------------------------------------------------------------------------------