mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-11 09:24:44 +02:00
feat: move Photon geocoder config to config.yaml — public komoot default, local override in deployment config
This commit is contained in:
parent
f65d1d2f59
commit
72611cc148
3 changed files with 56 additions and 13 deletions
|
|
@ -25,7 +25,7 @@ import urllib.request
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
from meshai.adapter_config import adapter_config
|
# Geocoder config is set via init_geocoder_config()
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -248,13 +248,31 @@ def _is_uninformative_road(road: Optional[str]) -> bool:
|
||||||
# 2026-06-04). It's the same Echo6-local Photon instance that backs Central's
|
# 2026-06-04). It's the same Echo6-local Photon instance that backs Central's
|
||||||
# NaviBackend reverse-geocoder. Photon takes osm_tag=place (KEY only, not
|
# NaviBackend reverse-geocoder. Photon takes osm_tag=place (KEY only, not
|
||||||
# key:value with comma-list -- that returns 0 features -- per probe).
|
# key:value with comma-list -- that returns 0 features -- per probe).
|
||||||
# v0.6-3b: photon endpoint settings live in adapter_config.geocoder.
|
# v0.6-3b: photon geocoder config - initialized via init_geocoder_config()
|
||||||
# Module-level names retained as backward-compat aliases so existing
|
# Defaults to public Komoot Photon; deployments override in config.yaml.
|
||||||
# test imports / monkeypatches still resolve.
|
|
||||||
PHOTON_BASE_URL = "http://100.64.0.24:2322"
|
class _GeocoderSettings:
|
||||||
PHOTON_TIMEOUT_S = 2.0
|
url: str = "https://photon.komoot.io"
|
||||||
PHOTON_RADIUS_KM = 80 # ≈ 50 miles
|
timeout_seconds: float = 2.0
|
||||||
PHOTON_LIMIT = 10
|
radius_km: float = 80.0
|
||||||
|
limit: int = 10
|
||||||
|
|
||||||
|
_geocoder = _GeocoderSettings()
|
||||||
|
|
||||||
|
|
||||||
|
def init_geocoder_config(url: str = None, timeout: float = None,
|
||||||
|
radius: float = None, limit: int = None) -> None:
|
||||||
|
"""Initialize geocoder settings from config.yaml values."""
|
||||||
|
if url is not None:
|
||||||
|
_geocoder.url = url
|
||||||
|
if timeout is not None:
|
||||||
|
_geocoder.timeout_seconds = timeout
|
||||||
|
if radius is not None:
|
||||||
|
_geocoder.radius_km = radius
|
||||||
|
if limit is not None:
|
||||||
|
_geocoder.limit = limit
|
||||||
|
|
||||||
|
|
||||||
# OSM place classes we accept as "town". Suburb included for metro coverage;
|
# OSM place classes we accept as "town". Suburb included for metro coverage;
|
||||||
# locality is rare but valid for tiny rural places.
|
# locality is rare but valid for tiny rural places.
|
||||||
_TOWN_OSM_VALUES = frozenset({"city", "town", "village"})
|
_TOWN_OSM_VALUES = frozenset({"city", "town", "village"})
|
||||||
|
|
@ -282,13 +300,13 @@ def _photon_reverse_places(lat: float, lon: float) -> list[dict]:
|
||||||
qs = urllib.parse.urlencode({
|
qs = urllib.parse.urlencode({
|
||||||
"lat": f"{lat:.6f}",
|
"lat": f"{lat:.6f}",
|
||||||
"lon": f"{lon:.6f}",
|
"lon": f"{lon:.6f}",
|
||||||
"radius": PHOTON_RADIUS_KM,
|
"radius": _geocoder.radius_km,
|
||||||
"osm_tag": "place",
|
"osm_tag": "place",
|
||||||
"limit": PHOTON_LIMIT,
|
"limit": _geocoder.limit,
|
||||||
})
|
})
|
||||||
url = f"{PHOTON_BASE_URL}/reverse?{qs}"
|
url = f"{_geocoder.url}/reverse?{qs}"
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(url, timeout=PHOTON_TIMEOUT_S) as resp:
|
with urllib.request.urlopen(url, timeout=_geocoder.timeout_seconds) as resp:
|
||||||
body = resp.read()
|
body = resp.read()
|
||||||
d = json.loads(body)
|
d = json.loads(body)
|
||||||
except (urllib.error.URLError, urllib.error.HTTPError, TimeoutError,
|
except (urllib.error.URLError, urllib.error.HTTPError, TimeoutError,
|
||||||
|
|
@ -308,7 +326,7 @@ def nearest_town(lat: float, lon: float, max_distance_mi: float = 50.0) -> Optio
|
||||||
event is N of the town. Returns None if no town within range or if
|
event is N of the town. Returns None if no town within range or if
|
||||||
Photon is unreachable.
|
Photon is unreachable.
|
||||||
|
|
||||||
Calls Photon /reverse?osm_tag=place at PHOTON_BASE_URL. Results are
|
Calls Photon /reverse?osm_tag=place at _geocoder.url. Results are
|
||||||
H3-cell-cached (resolution 7 ≈ 5 km cells) so the second event near
|
H3-cell-cached (resolution 7 ≈ 5 km cells) so the second event near
|
||||||
the same town is free.
|
the same town is free.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -468,6 +468,16 @@ class CentralConsumerConfig:
|
||||||
region: str = "us.id"
|
region: str = "us.id"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GeocoderConfig:
|
||||||
|
"""Photon reverse geocoder settings."""
|
||||||
|
|
||||||
|
url: str = "https://photon.komoot.io"
|
||||||
|
timeout_seconds: float = 2.0
|
||||||
|
radius_km: float = 80.0
|
||||||
|
limit: int = 10
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class EnvironmentalConfig:
|
class EnvironmentalConfig:
|
||||||
"""Environmental feeds settings."""
|
"""Environmental feeds settings."""
|
||||||
|
|
@ -486,6 +496,7 @@ class EnvironmentalConfig:
|
||||||
wzdx: WZDxConfig = field(default_factory=WZDxConfig)
|
wzdx: WZDxConfig = field(default_factory=WZDxConfig)
|
||||||
firms: FIRMSConfig = field(default_factory=FIRMSConfig)
|
firms: FIRMSConfig = field(default_factory=FIRMSConfig)
|
||||||
central: CentralConsumerConfig = field(default_factory=CentralConsumerConfig)
|
central: CentralConsumerConfig = field(default_factory=CentralConsumerConfig)
|
||||||
|
geocoder: GeocoderConfig = field(default_factory=GeocoderConfig)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ from .commands.status import set_start_time
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .config_loader import load_config, get_config_dir_from_path
|
from .config_loader import load_config, get_config_dir_from_path
|
||||||
from .connector import MeshConnector, MeshMessage
|
from .connector import MeshConnector, MeshMessage
|
||||||
|
from .central_normalizer import init_geocoder_config
|
||||||
from .context import MeshContext
|
from .context import MeshContext
|
||||||
from .history import ConversationHistory
|
from .history import ConversationHistory
|
||||||
from .memory import ConversationSummary
|
from .memory import ConversationSummary
|
||||||
|
|
@ -245,6 +246,19 @@ class MeshAI:
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("persistence init_db failed at startup")
|
logger.exception("persistence init_db failed at startup")
|
||||||
|
|
||||||
|
# v0.6-3b: Initialize geocoder config from config.yaml
|
||||||
|
try:
|
||||||
|
gc = self.config.environmental.geocoder
|
||||||
|
init_geocoder_config(
|
||||||
|
url=gc.url,
|
||||||
|
timeout=gc.timeout_seconds,
|
||||||
|
radius=gc.radius_km,
|
||||||
|
limit=gc.limit
|
||||||
|
)
|
||||||
|
logger.info("Geocoder configured: %s", gc.url)
|
||||||
|
except Exception:
|
||||||
|
logger.exception("geocoder init failed - using defaults")
|
||||||
|
|
||||||
# Conversation history
|
# Conversation history
|
||||||
self.history = ConversationHistory(self.config.history)
|
self.history = ConversationHistory(self.config.history)
|
||||||
await self.history.initialize()
|
await self.history.initialize()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue