Spaces:
Running
Running
David de la Iglesia Castro
commited on
Add `surf-spot-finder-no-framework`. (#42)
Browse files* Add `surf-spot-finder-no-framework`.
```
surf-spot-finder-no-framework "Vigo" 2 "2025-04-02T12:00"
```
* Add `no_framework` docs.
- docs/api.md +2 -0
- pyproject.toml +1 -0
- src/surf_spot_finder/no_framework.py +110 -0
docs/api.md
CHANGED
@@ -4,6 +4,8 @@
|
|
4 |
|
5 |
::: surf_spot_finder.config.Config
|
6 |
|
|
|
|
|
7 |
## Tools
|
8 |
|
9 |
::: surf_spot_finder.tools.openmeteo
|
|
|
4 |
|
5 |
::: surf_spot_finder.config.Config
|
6 |
|
7 |
+
::: surf_spot_finder.no_framework
|
8 |
+
|
9 |
## Tools
|
10 |
|
11 |
::: surf_spot_finder.tools.openmeteo
|
pyproject.toml
CHANGED
@@ -54,4 +54,5 @@ dev = [
|
|
54 |
|
55 |
[project.scripts]
|
56 |
surf-spot-finder = "surf_spot_finder.cli:main"
|
|
|
57 |
surf-spot-finder-evaluate = "surf_spot_finder.evaluation.evaluate:main"
|
|
|
54 |
|
55 |
[project.scripts]
|
56 |
surf-spot-finder = "surf_spot_finder.cli:main"
|
57 |
+
surf-spot-finder-no-framework = "surf_spot_finder.no_framework:main"
|
58 |
surf-spot-finder-evaluate = "surf_spot_finder.evaluation.evaluate:main"
|
src/surf_spot_finder/no_framework.py
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
from datetime import datetime
|
3 |
+
|
4 |
+
from fire import Fire
|
5 |
+
from litellm import completion
|
6 |
+
from loguru import logger
|
7 |
+
from pydantic import BaseModel
|
8 |
+
|
9 |
+
from any_agent.tools.web_browsing import search_web, visit_webpage
|
10 |
+
from surf_spot_finder.tools.openmeteo import get_wave_forecast, get_wind_forecast
|
11 |
+
from surf_spot_finder.tools.openstreetmap import (
|
12 |
+
driving_hours_to_meters,
|
13 |
+
get_area_lat_lon,
|
14 |
+
get_surfing_spots,
|
15 |
+
)
|
16 |
+
|
17 |
+
|
18 |
+
spot_info_pattern = r"\[.*?\]\((https:\/\/www\.surf-forecast\.com\/breaks\/[^)/]+)\)"
|
19 |
+
|
20 |
+
|
21 |
+
class SpotScore(BaseModel):
|
22 |
+
score: int
|
23 |
+
reason: str
|
24 |
+
|
25 |
+
|
26 |
+
@logger.catch(reraise=True)
|
27 |
+
def find_surf_spot_no_framework(
|
28 |
+
location: str, max_driving_hours: int, date: datetime, model_id: str
|
29 |
+
) -> list[SpotScore]:
|
30 |
+
"""Find the best surf spot based on the given `location` and `date`.
|
31 |
+
|
32 |
+
Uses the following tools:
|
33 |
+
|
34 |
+
- any_agent.tools.web_browsing
|
35 |
+
- [surf_spot_finder.tools.openmeteo][]
|
36 |
+
- [surf_spot_finder.tools.openstreetmap][]
|
37 |
+
|
38 |
+
To find nearby spots along with the forecast and
|
39 |
+
recommended conditions for the spot.
|
40 |
+
|
41 |
+
Then, uses `litellm` with the provided `model_id` to score
|
42 |
+
each spot based on the available information.
|
43 |
+
|
44 |
+
Args:
|
45 |
+
location: The place of interest.
|
46 |
+
max_driving_hours: Used to limit the surf spots based on
|
47 |
+
the distance to `location`.
|
48 |
+
date: Used to filter the forecast results.
|
49 |
+
model_id: Can be any of the [litellm providers](https://docs.litellm.ai/docs/providers).
|
50 |
+
|
51 |
+
Returns:
|
52 |
+
A list of spot scores and reasons for the value.
|
53 |
+
"""
|
54 |
+
max_driving_meters = driving_hours_to_meters(max_driving_hours)
|
55 |
+
lat, lon = get_area_lat_lon(location)
|
56 |
+
|
57 |
+
logger.info(f"Getting surfing spots around {location}")
|
58 |
+
surf_spots = get_surfing_spots(lat, lon, max_driving_meters)
|
59 |
+
|
60 |
+
if not surf_spots:
|
61 |
+
logger.warning("No surfing spots found around {location}")
|
62 |
+
return None
|
63 |
+
|
64 |
+
spots_scores = []
|
65 |
+
for spot_name, (spot_lat, spot_lon) in surf_spots:
|
66 |
+
logger.info(f"Processing {spot_name}")
|
67 |
+
logger.debug("Getting wave forecast...")
|
68 |
+
wave_forecast = get_wave_forecast(spot_lat, spot_lon, date)
|
69 |
+
logger.debug("Getting wind forecast...")
|
70 |
+
wind_forecast = get_wind_forecast(spot_lat, spot_lon, date)
|
71 |
+
|
72 |
+
logger.debug("Searching web for spot information")
|
73 |
+
search_result = search_web(f"surf-forecast.com spot info {spot_name}")
|
74 |
+
match = re.search(spot_info_pattern, search_result)
|
75 |
+
if match:
|
76 |
+
extracted_url = match.group(1)
|
77 |
+
logger.debug(f"Visiting {extracted_url}")
|
78 |
+
spot_info = visit_webpage(extracted_url)
|
79 |
+
else:
|
80 |
+
logger.debug(f"Couldn't find spot info for {spot_name}")
|
81 |
+
continue
|
82 |
+
|
83 |
+
logger.debug("Scoring conditions with LLM")
|
84 |
+
response = completion(
|
85 |
+
model="openai/gpt-4o-mini",
|
86 |
+
messages=[
|
87 |
+
{
|
88 |
+
"content": "Given the wind and wave forecast along with the spot information, "
|
89 |
+
"rate from 1 to 5 the expected surfing conditions."
|
90 |
+
f"Wind forecast:\n{wind_forecast}\n"
|
91 |
+
f"Wave forecast:\n{wave_forecast}\n"
|
92 |
+
f"Spot Information:\n{spot_info}",
|
93 |
+
"role": "user",
|
94 |
+
}
|
95 |
+
],
|
96 |
+
response_format=SpotScore,
|
97 |
+
)
|
98 |
+
spot_score = SpotScore.model_validate_json(response.choices[0].message.content)
|
99 |
+
logger.debug(spot_score)
|
100 |
+
spots_scores.append(spot_score)
|
101 |
+
|
102 |
+
return spots_scores
|
103 |
+
|
104 |
+
|
105 |
+
def main():
|
106 |
+
Fire(find_surf_spot_no_framework)
|
107 |
+
|
108 |
+
|
109 |
+
if __name__ == "__main__":
|
110 |
+
main()
|