You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
11 KiB
Go
275 lines
11 KiB
Go
/*
|
|
* Copyright 2025 CloudWeGo Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"hash/fnv"
|
|
|
|
"github.com/cloudwego/eino/components/tool"
|
|
"github.com/cloudwego/eino/components/tool/utils"
|
|
|
|
tool2 "github.com/cloudwego/eino-examples/adk/common/tool"
|
|
)
|
|
|
|
type WeatherRequest struct {
|
|
City string `json:"city" jsonschema_description:"City name to get weather for"`
|
|
Date string `json:"date" jsonschema_description:"Date in YYYY-MM-DD format"`
|
|
}
|
|
|
|
type WeatherResponse struct {
|
|
City string `json:"city"`
|
|
Temperature int `json:"temperature"`
|
|
Condition string `json:"condition"`
|
|
Date string `json:"date"`
|
|
}
|
|
|
|
type FlightBookingRequest struct {
|
|
From string `json:"from" jsonschema_description:"Departure city"`
|
|
To string `json:"to" jsonschema_description:"Destination city"`
|
|
Date string `json:"date" jsonschema_description:"Departure date in YYYY-MM-DD format"`
|
|
Passengers int `json:"passengers" jsonschema_description:"Number of passengers"`
|
|
PreferredTime string `json:"preferred_time" jsonschema_description:"Preferred departure time (morning/afternoon/evening)"`
|
|
}
|
|
|
|
type FlightBookingResponse struct {
|
|
BookingID string `json:"booking_id"`
|
|
Airline string `json:"airline"`
|
|
FlightNo string `json:"flight_no"`
|
|
From string `json:"from"`
|
|
To string `json:"to"`
|
|
Date string `json:"date"`
|
|
DepartureTime string `json:"departure_time"`
|
|
ArrivalTime string `json:"arrival_time"`
|
|
Price int `json:"price"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type HotelBookingRequest struct {
|
|
City string `json:"city" jsonschema_description:"City to book hotel in"`
|
|
CheckIn string `json:"check_in" jsonschema_description:"Check-in date in YYYY-MM-DD format"`
|
|
CheckOut string `json:"check_out" jsonschema_description:"Check-out date in YYYY-MM-DD format"`
|
|
Guests int `json:"guests" jsonschema_description:"Number of guests"`
|
|
RoomType string `json:"room_type" jsonschema_description:"Room type preference (standard/deluxe/suite)"`
|
|
}
|
|
|
|
type HotelBookingResponse struct {
|
|
BookingID string `json:"booking_id"`
|
|
HotelName string `json:"hotel_name"`
|
|
City string `json:"city"`
|
|
CheckIn string `json:"check_in"`
|
|
CheckOut string `json:"check_out"`
|
|
RoomType string `json:"room_type"`
|
|
PricePerNight int `json:"price_per_night"`
|
|
TotalPrice int `json:"total_price"`
|
|
Amenities []string `json:"amenities"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
type AttractionRequest struct {
|
|
City string `json:"city" jsonschema_description:"City to search attractions in"`
|
|
Category string `json:"category" jsonschema_description:"Category of attractions (museum, park, landmark, etc.)"`
|
|
}
|
|
|
|
type AttractionResponse struct {
|
|
Attractions []Attraction `json:"attractions"`
|
|
}
|
|
|
|
type Attraction struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Rating float64 `json:"rating"`
|
|
OpenHours string `json:"open_hours"`
|
|
TicketPrice int `json:"ticket_price"`
|
|
Category string `json:"category"`
|
|
}
|
|
|
|
func NewWeatherTool(ctx context.Context) (tool.BaseTool, error) {
|
|
return utils.InferTool("get_weather", "Get weather information for a specific city and date",
|
|
func(ctx context.Context, req *WeatherRequest) (*WeatherResponse, error) {
|
|
weathers := map[string]WeatherResponse{
|
|
"Tokyo": {City: "Tokyo", Temperature: 22, Condition: "Partly Cloudy", Date: req.Date},
|
|
"Beijing": {City: "Beijing", Temperature: 18, Condition: "Sunny", Date: req.Date},
|
|
"Paris": {City: "Paris", Temperature: 15, Condition: "Rainy", Date: req.Date},
|
|
"New York": {City: "New York", Temperature: 12, Condition: "Cloudy", Date: req.Date},
|
|
}
|
|
|
|
if weather, exists := weathers[req.City]; exists {
|
|
return &weather, nil
|
|
}
|
|
|
|
conditions := []string{"Sunny", "Cloudy", "Rainy", "Partly Cloudy"}
|
|
hashInput := req.City + req.Date
|
|
return &WeatherResponse{
|
|
City: req.City,
|
|
Temperature: consistentHashing(hashInput+"temp", 10, 30),
|
|
Condition: conditions[consistentHashing(hashInput+"cond", 0, len(conditions)-1)],
|
|
Date: req.Date,
|
|
}, nil
|
|
})
|
|
}
|
|
|
|
func NewFlightBookingTool(ctx context.Context) (tool.BaseTool, error) {
|
|
baseTool, err := utils.InferTool("book_flight", "Book a flight between cities. This requires user review before confirmation.",
|
|
func(ctx context.Context, req *FlightBookingRequest) (*FlightBookingResponse, error) {
|
|
airlines := []string{"Japan Airlines", "ANA", "United Airlines", "Delta", "Air China"}
|
|
hashInput := req.From + req.To + req.Date
|
|
|
|
airlineIdx := consistentHashing(hashInput+"airline", 0, len(airlines)-1)
|
|
depHour := consistentHashing(hashInput+"dephour", 6, 20)
|
|
depMin := consistentHashing(hashInput+"depmin", 0, 59)
|
|
duration := consistentHashing(hashInput+"duration", 2, 14)
|
|
|
|
arrHour := (depHour + duration) % 24
|
|
arrMin := depMin
|
|
|
|
return &FlightBookingResponse{
|
|
BookingID: fmt.Sprintf("FL-%s-%d", req.Date, consistentHashing(hashInput+"id", 10000, 99999)),
|
|
Airline: airlines[airlineIdx],
|
|
FlightNo: fmt.Sprintf("%s%d", airlines[airlineIdx][:2], consistentHashing(hashInput+"flightno", 100, 999)),
|
|
From: req.From,
|
|
To: req.To,
|
|
Date: req.Date,
|
|
DepartureTime: fmt.Sprintf("%02d:%02d", depHour, depMin),
|
|
ArrivalTime: fmt.Sprintf("%02d:%02d", arrHour, arrMin),
|
|
Price: consistentHashing(hashInput+"price", 300, 1500) * req.Passengers,
|
|
Status: "confirmed",
|
|
}, nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &tool2.InvokableReviewEditTool{InvokableTool: baseTool}, nil
|
|
}
|
|
|
|
func NewHotelBookingTool(ctx context.Context) (tool.BaseTool, error) {
|
|
baseTool, err := utils.InferTool("book_hotel", "Book a hotel in a city. This requires user review before confirmation.",
|
|
func(ctx context.Context, req *HotelBookingRequest) (*HotelBookingResponse, error) {
|
|
hotelNames := []string{"Grand Hyatt", "Marriott", "Hilton", "Sheraton", "Ritz-Carlton"}
|
|
amenitiesList := [][]string{
|
|
{"WiFi", "Pool", "Gym", "Spa", "Restaurant"},
|
|
{"WiFi", "Breakfast", "Parking", "Business Center"},
|
|
{"WiFi", "Pool", "Restaurant", "Bar", "Concierge"},
|
|
}
|
|
|
|
hashInput := req.City + req.CheckIn + req.CheckOut
|
|
|
|
hotelIdx := consistentHashing(hashInput+"hotel", 0, len(hotelNames)-1)
|
|
amenitiesIdx := consistentHashing(hashInput+"amenities", 0, len(amenitiesList)-1)
|
|
|
|
pricePerNight := consistentHashing(hashInput+"price", 100, 400)
|
|
switch req.RoomType {
|
|
case "deluxe":
|
|
pricePerNight = int(float64(pricePerNight) * 1.5)
|
|
case "suite":
|
|
pricePerNight = pricePerNight * 2
|
|
}
|
|
|
|
nights := 3
|
|
|
|
return &HotelBookingResponse{
|
|
BookingID: fmt.Sprintf("HT-%s-%d", req.CheckIn, consistentHashing(hashInput+"id", 10000, 99999)),
|
|
HotelName: fmt.Sprintf("%s %s", req.City, hotelNames[hotelIdx]),
|
|
City: req.City,
|
|
CheckIn: req.CheckIn,
|
|
CheckOut: req.CheckOut,
|
|
RoomType: req.RoomType,
|
|
PricePerNight: pricePerNight,
|
|
TotalPrice: pricePerNight * nights,
|
|
Amenities: amenitiesList[amenitiesIdx],
|
|
Status: "confirmed",
|
|
}, nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &tool2.InvokableReviewEditTool{InvokableTool: baseTool}, nil
|
|
}
|
|
|
|
func NewAttractionSearchTool(ctx context.Context) (tool.BaseTool, error) {
|
|
return utils.InferTool("search_attractions", "Search for tourist attractions in a city",
|
|
func(ctx context.Context, req *AttractionRequest) (*AttractionResponse, error) {
|
|
attractionsByCity := map[string][]Attraction{
|
|
"Tokyo": {
|
|
{Name: "Senso-ji Temple", Description: "Ancient Buddhist temple in Asakusa", Rating: 4.6, OpenHours: "6:00-17:00", TicketPrice: 0, Category: "landmark"},
|
|
{Name: "Tokyo Skytree", Description: "Tallest tower in Japan with observation decks", Rating: 4.5, OpenHours: "10:00-21:00", TicketPrice: 2100, Category: "landmark"},
|
|
{Name: "Meiji Shrine", Description: "Shinto shrine dedicated to Emperor Meiji", Rating: 4.7, OpenHours: "5:00-18:00", TicketPrice: 0, Category: "landmark"},
|
|
{Name: "Ueno Park", Description: "Large public park with museums and zoo", Rating: 4.4, OpenHours: "5:00-23:00", TicketPrice: 0, Category: "park"},
|
|
},
|
|
"Beijing": {
|
|
{Name: "Forbidden City", Description: "Ancient imperial palace complex", Rating: 4.8, OpenHours: "8:30-17:00", TicketPrice: 60, Category: "historic site"},
|
|
{Name: "Great Wall", Description: "Historic fortification stretching thousands of miles", Rating: 4.9, OpenHours: "6:00-18:00", TicketPrice: 45, Category: "landmark"},
|
|
{Name: "Temple of Heaven", Description: "Imperial sacrificial altar", Rating: 4.6, OpenHours: "6:00-22:00", TicketPrice: 35, Category: "park"},
|
|
},
|
|
}
|
|
|
|
if attractions, exists := attractionsByCity[req.City]; exists {
|
|
if req.Category != "" {
|
|
var filtered []Attraction
|
|
for _, a := range attractions {
|
|
if a.Category == req.Category {
|
|
filtered = append(filtered, a)
|
|
}
|
|
}
|
|
return &AttractionResponse{Attractions: filtered}, nil
|
|
}
|
|
return &AttractionResponse{Attractions: attractions}, nil
|
|
}
|
|
|
|
return &AttractionResponse{
|
|
Attractions: []Attraction{
|
|
{Name: fmt.Sprintf("%s Central Park", req.City), Description: "Popular city park", Rating: 4.3, OpenHours: "6:00-22:00", TicketPrice: 0, Category: "park"},
|
|
{Name: fmt.Sprintf("%s National Museum", req.City), Description: "Major museum with local history", Rating: 4.5, OpenHours: "9:00-17:00", TicketPrice: 15, Category: "museum"},
|
|
},
|
|
}, nil
|
|
})
|
|
}
|
|
|
|
func GetAllTravelTools(ctx context.Context) ([]tool.BaseTool, error) {
|
|
weatherTool, err := NewWeatherTool(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
flightTool, err := NewFlightBookingTool(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
hotelTool, err := NewHotelBookingTool(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
attractionTool, err := NewAttractionSearchTool(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return []tool.BaseTool{weatherTool, flightTool, hotelTool, attractionTool}, nil
|
|
}
|
|
|
|
func consistentHashing(s string, min, max int) int {
|
|
h := fnv.New32a()
|
|
h.Write([]byte(s))
|
|
hash := h.Sum32()
|
|
return min + int(hash)%(max-min+1)
|
|
}
|