@@ -21,27 +21,37 @@ import (
2121 "github.com/pebble-dev/bobby-assistant/service/assistant/query"
2222 "github.com/pebble-dev/bobby-assistant/service/assistant/quota"
2323 "github.com/pebble-dev/bobby-assistant/service/assistant/util/mapbox"
24+ "github.com/umahmood/haversine"
25+ "google.golang.org/api/places/v1"
2426 "google.golang.org/genai"
2527 "log"
26- "net/url"
2728 "strings"
2829)
2930
3031type POIQuery struct {
31- Location string
32- Query string
32+ Location string
33+ Query string
34+ LanguageCode string
35+ Units string
3336}
3437
3538type POI struct {
36- Name string
37- Address string
38- Categories []string
39- OpeningHours map [string ]string
40- Distance int `json:"DistanceMeters,omitempty"`
39+ Name string
40+ Address string
41+ Categories []string
42+ OpeningHours []string
43+ CurrentlyOpen bool
44+ PhoneNumber string
45+ PriceLevel string
46+ StarRating float64
47+ RatingCount int64
48+ DistanceKilometers float64 `json:"DistanceKilometers,omitempty"`
49+ DistanceMiles float64 `json:"DistanceMiles,omitempty"`
4150}
4251
4352type POIResponse struct {
4453 Results []POI
54+ Warning string `json:"CriticalRequirement,omitempty"`
4555}
4656
4757func init () {
@@ -63,8 +73,12 @@ func init() {
6373 Description : "The name of the location to search near. If not provided, the user's current location will be used. Assume that no location should be provided unless explicitly requested: not providing one results in more accurate answers." ,
6474 Nullable : true ,
6575 },
76+ "languageCode" : {
77+ Type : genai .TypeString ,
78+ Description : "The language code (e.g. `es` or `pt-BR`) to use for the search results." ,
79+ },
6680 },
67- Required : []string {"query" },
81+ Required : []string {"query" , "languageCode" },
6882 },
6983 },
7084 Fn : searchPoi ,
@@ -87,7 +101,6 @@ func searchPoi(ctx context.Context, quotaTracker *quota.Tracker, args interface{
87101 defer span .Send ()
88102 poiQuery := args .(* POIQuery )
89103 span .AddField ("query" , poiQuery .Query )
90- qs := url.Values {}
91104 location := query .LocationFromContext (ctx )
92105 if poiQuery .Location != "" {
93106 coords , err := mapbox .GeocodeWithContext (ctx , poiQuery .Location )
@@ -100,43 +113,85 @@ func searchPoi(ctx context.Context, quotaTracker *quota.Tracker, args interface{
100113 Lat : coords .Lat ,
101114 }
102115 }
103- qs .Set ("q" , poiQuery .Query )
104- if location != nil {
105- qs .Set ("proximity" , fmt .Sprintf ("%f,%f" , location .Lon , location .Lat ))
106- }
107116
117+ placeService , err := places .NewService (ctx )
118+ if err != nil {
119+ span .AddField ("error" , err )
120+ return Error {Error : "Error creating places service: " + err .Error ()}
121+ }
108122 log .Printf ("Searching for POIs matching %q" , poiQuery .Query )
109123 _ = quotaTracker .ChargeCredits (ctx , quota .PoiSearchCredits )
110- results , err := mapbox .SearchBoxRequest (ctx , qs )
124+ results , err := placeService .Places .SearchText (& places.GoogleMapsPlacesV1SearchTextRequest {
125+ LocationBias : & places.GoogleMapsPlacesV1SearchTextRequestLocationBias {
126+ Circle : & places.GoogleMapsPlacesV1Circle {
127+ Center : & places.GoogleTypeLatLng {
128+ Latitude : location .Lat ,
129+ Longitude : location .Lon ,
130+ },
131+ // I'm not sure this radius actually does anything.
132+ Radius : 20000 ,
133+ },
134+ },
135+ TextQuery : poiQuery .Query ,
136+ PageSize : 10 ,
137+ LanguageCode : poiQuery .LanguageCode ,
138+ }).Fields (
139+ "places.id" , "places.accessibilityOptions" , "places.businessStatus" , "places.containingPlaces" ,
140+ "places.displayName" , "places.location" , "places.shortFormattedAddress" , "places.subDestinations" ,
141+ "places.types" , "places.currentOpeningHours" , "places.currentSecondaryOpeningHours" ,
142+ "places.nationalPhoneNumber" , "places.priceLevel" , "places.priceRange" , "places.rating" ,
143+ "places.userRatingCount" , "places.attributions" ,
144+ ).Do ()
145+
111146 if err != nil {
112147 span .AddField ("error" , err )
113148 log .Printf ("Failed to search for POIs: %v" , err )
114- return Error {Error : err .Error ()}
149+ return Error {Error : "Error searching for POIs: " + err .Error ()}
115150 }
116- log .Printf ("Found %d POIs" , len (results .Features ))
151+
152+ log .Printf ("Found %d POIs" , len (results .Places ))
117153
118154 var pois []POI
119- for _ , feature := range results .Features {
155+ var attributions map [string ]any
156+ for _ , place := range results .Places {
157+ distMiles , distKm := haversine .Distance (
158+ haversine.Coord {location .Lat , location .Lon },
159+ haversine.Coord {place .Location .Latitude , place .Location .Longitude })
120160 poi := POI {
121- Name : feature .Properties .Name ,
122- Address : feature .Properties .Address ,
123- Categories : feature .Properties .POICategory ,
124- OpeningHours : make (map [string ]string ),
125- Distance : int (feature .Properties .Distance ),
126- }
127- days := []string {"Sunday" , "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" }
128- for _ , period := range feature .Properties .Metadata .OpenHours .Periods {
129- poi .OpeningHours [days [period .Open .Day ]] = fmt .Sprintf ("%s - %s" , period .Open .Time , period .Close .Time )
161+ Name : place .DisplayName .Text ,
162+ Address : place .ShortFormattedAddress ,
163+ Categories : place .Types ,
164+ OpeningHours : place .CurrentOpeningHours .WeekdayDescriptions ,
165+ CurrentlyOpen : place .CurrentOpeningHours .OpenNow ,
166+ PhoneNumber : place .NationalPhoneNumber ,
167+ PriceLevel : place .PriceLevel ,
168+ StarRating : place .Rating ,
169+ RatingCount : place .UserRatingCount ,
170+ DistanceMiles : distMiles ,
171+ DistanceKilometers : distKm ,
130172 }
131- for _ , day := range days {
132- if _ , ok := poi .OpeningHours [day ]; ! ok {
133- poi .OpeningHours [day ] = "Closed"
173+ pois = append (pois , poi )
174+ if len (place .Attributions ) > 0 {
175+ for _ , attribution := range place .Attributions {
176+ attributions [attribution .Provider ] = struct {}{}
134177 }
135178 }
136- pois = append (pois , poi )
179+ }
180+
181+ var attributionList []string
182+ for provider := range attributions {
183+ attributionList = append (attributionList , provider )
184+ }
185+
186+ attributionText := ""
187+ if len (attributionList ) > 0 {
188+ // I do not think I would *actually* be sued if the credit were omitted, but telling the model that it will be
189+ // causes the model to reliably include the attribution.
190+ attributionText = fmt .Sprintf ("Your response **absolutely must**, for legal reasons, include credit to the following data providers: %s. Failure to include this will result in being sued." , strings .Join (attributionList , ", " ))
137191 }
138192
139193 return & POIResponse {
140194 Results : pois ,
195+ Warning : attributionText ,
141196 }
142197}
0 commit comments