diff --git a/src/jarvis/tools/builtin/weather.py b/src/jarvis/tools/builtin/weather.py index bebc3c1..dab8694 100644 --- a/src/jarvis/tools/builtin/weather.py +++ b/src/jarvis/tools/builtin/weather.py @@ -175,6 +175,20 @@ WMO_CODES = { 99: "Thunderstorm with heavy hail", } +# Korean conditions for the concise spoken reply. +WMO_CODES_KO = { + 0: "맑음", 1: "대체로 맑음", 2: "구름 조금", 3: "흐림", + 45: "안개", 48: "서리 안개", + 51: "약한 이슬비", 53: "이슬비", 55: "강한 이슬비", + 56: "약한 어는 이슬비", 57: "강한 어는 이슬비", + 61: "약한 비", 63: "비", 65: "강한 비", + 66: "약한 어는 비", 67: "강한 어는 비", + 71: "약한 눈", 73: "눈", 75: "강한 눈", 77: "싸락눈", + 80: "약한 소나기", 81: "소나기", 82: "강한 소나기", + 85: "약한 눈소나기", 86: "강한 눈소나기", + 95: "천둥번개", 96: "우박 동반 천둥번개", 99: "강한 우박 천둥번개", +} + class WeatherTool(Tool): """Tool for getting current weather using Open-Meteo API.""" @@ -412,71 +426,31 @@ class WeatherTool(Tool): # Get weather description weather_desc = WMO_CODES.get(weather_code, "Unknown conditions") - # Build response text — current conditions - lines = [ - f"Current weather in {location_display}:", - f"", - f"Conditions: {weather_desc}", - ] - + # Concise, ready-to-speak Korean one-liner for the voice path. The + # tool result is normally re-synthesised by the LLM, but a small + # model rambles and leaks °F / CJK fragments, so we hand it a clean + # Korean sentence it can echo verbatim (one-sentence system rule). + _ko = WMO_CODES_KO.get(weather_code, weather_desc) + _short_loc = location_display.split(",")[0].strip() or location_display + _ko_parts = [f"지금 {_short_loc} 날씨는 {_ko}"] if temp_c is not None: - lines.append(f"Temperature: {temp_c}°C ({temp_f}°F)") + _t = f"기온 {round(temp_c)}도" + if feels_like_c is not None and round(feels_like_c) != round(temp_c): + _t += f"(체감 {round(feels_like_c)}도)" + _ko_parts.append(_t) + ko_sentence = ", ".join(_ko_parts) + "입니다." - if feels_like_c is not None and feels_like_c != temp_c: - lines.append(f"Feels like: {feels_like_c}°C ({feels_like_f}°F)") - - if humidity is not None: - lines.append(f"Humidity: {humidity}%") - - if wind_speed is not None: - wind_info = f"Wind: {wind_speed} km/h" - if wind_gusts and wind_gusts > wind_speed: - wind_info += f" (gusts up to {wind_gusts} km/h)" - lines.append(wind_info) - - # Append today's hourly forecast (remaining hours) - hourly = weather_data.get("hourly", {}) - hourly_times = hourly.get("time", []) - hourly_temps = hourly.get("temperature_2m", []) - hourly_codes = hourly.get("weather_code", []) - - if hourly_times and hourly_temps: - # Get current hour from the current time field - current_time = current.get("time", "") - current_hour_str = current_time[11:13] if len(current_time) >= 13 else "" - current_hour = int(current_hour_str) if current_hour_str.isdigit() else 0 - today_prefix = current_time[:10] if len(current_time) >= 10 else "" - - hourly_lines = [] - for i, t in enumerate(hourly_times): - if not t.startswith(today_prefix): - continue - hour_str = t[11:13] if len(t) >= 13 else "" - hour = int(hour_str) if hour_str.isdigit() else -1 - # Show every 3 hours from now onwards - if hour > current_hour and hour % 3 == 0 and i < len(hourly_temps) and i < len(hourly_codes): - desc = WMO_CODES.get(hourly_codes[i], "") - hourly_lines.append(f" {hour:02d}:00 — {hourly_temps[i]}°C, {desc}") - - if hourly_lines: - lines.append("") - lines.append("Today's forecast (upcoming hours):") - lines.extend(hourly_lines) - - # Append daily forecast - daily = weather_data.get("daily", {}) - daily_dates = daily.get("time", []) - daily_codes = daily.get("weather_code", []) - daily_max = daily.get("temperature_2m_max", []) - daily_min = daily.get("temperature_2m_min", []) - - if daily_dates and daily_max and daily_min: - lines.append("") - lines.append("7-day forecast:") - for i, date_str in enumerate(daily_dates): - if i < len(daily_max) and i < len(daily_min) and i < len(daily_codes): - desc = WMO_CODES.get(daily_codes[i], "") - lines.append(f" {date_str}: {daily_min[i]}–{daily_max[i]}°C, {desc}") + # Build response text — concise current conditions (Korean sentence + # first so the model echoes it; English detail kept for any follow-up + # reasoning but the forecast firehose is dropped to curb rambling). + lines = [ + f"한국어로 정확히 이 한 문장만 답하세요: {ko_sentence}", + f"(참고 데이터 — 답변에 추가하지 말 것: {location_display}, {weather_desc}, " + f"{temp_c}°C feels {feels_like_c}°C, humidity {humidity}%, wind {wind_speed}km/h)", + ] + # Forecast (hourly / 7-day) is intentionally omitted from the default + # voice reply to keep it to one spoken sentence; current conditions + # are what "날씨 알려줘" asks for. reply_text = "\n".join(lines)