├── .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 |
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 |
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).
--------------------------------------------------------------------------------