feat: temperature unit preference and slot timezone display for briefing
- Add °C/°F toggle in briefing settings; persisted in briefing_config.temp_unit - briefing_pipeline reads temp_unit and converts Open-Meteo Celsius values before passing them to the LLM (both full compilation and slot injection) - Scheduled Slots section now shows each UTC slot time converted to the user's browser local time, plus a line confirming which timezone the browser is using Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -192,16 +192,26 @@ def _internal_user_prompt(data: dict, slot: str) -> str:
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _external_user_prompt(data: dict, slot: str) -> str:
|
||||
def _format_temp(value: float, unit: str) -> str:
|
||||
"""Convert Celsius to the requested unit and format as an integer string."""
|
||||
if unit == "F":
|
||||
return f"{value * 9 / 5 + 32:.0f}"
|
||||
return f"{value:.0f}"
|
||||
|
||||
|
||||
def _external_user_prompt(data: dict, slot: str, temp_unit: str = "C") -> str:
|
||||
unit_sym = f"°{temp_unit}"
|
||||
lines = [f"Briefing slot: {slot}", ""]
|
||||
if data["weather"]:
|
||||
lines.append("WEATHER:")
|
||||
for loc in data["weather"]:
|
||||
lines.append(f" {loc['location_label']}:")
|
||||
for day in loc["days"][:3]:
|
||||
t_min = _format_temp(day["temp_min"], temp_unit)
|
||||
t_max = _format_temp(day["temp_max"], temp_unit)
|
||||
lines.append(
|
||||
f" {day['date']}: {day['description']}, "
|
||||
f"{day['temp_min']}–{day['temp_max']}°C, {day['precip_mm']}mm rain"
|
||||
f"{t_min}–{t_max}{unit_sym}, {day['precip_mm']}mm rain"
|
||||
)
|
||||
if loc["changes_since_last_fetch"]:
|
||||
lines.append(" FORECAST CHANGES:")
|
||||
@@ -218,6 +228,18 @@ def _external_user_prompt(data: dict, slot: str) -> str:
|
||||
|
||||
# ── Main entry point ───────────────────────────────────────────────────────────
|
||||
|
||||
async def _get_temp_unit(user_id: int) -> str:
|
||||
"""Read the user's preferred temperature unit from briefing_config ('C' or 'F')."""
|
||||
import json
|
||||
raw = await get_setting(user_id, "briefing_config", "{}")
|
||||
try:
|
||||
config = json.loads(raw) if isinstance(raw, str) else (raw or {})
|
||||
unit = config.get("temp_unit", "C")
|
||||
return unit if unit in ("C", "F") else "C"
|
||||
except Exception:
|
||||
return "C"
|
||||
|
||||
|
||||
async def run_compilation(user_id: int, slot: str, model: str | None = None) -> str:
|
||||
"""
|
||||
Run the full two-lane briefing pipeline for a user and slot.
|
||||
@@ -227,7 +249,10 @@ async def run_compilation(user_id: int, slot: str, model: str | None = None) ->
|
||||
model = await get_setting(user_id, "default_model", Config.OLLAMA_MODEL)
|
||||
|
||||
from fabledassistant.services.briefing_profile import get_profile_body
|
||||
profile_body = await get_profile_body(user_id)
|
||||
profile_body, temp_unit = await asyncio.gather(
|
||||
get_profile_body(user_id),
|
||||
_get_temp_unit(user_id),
|
||||
)
|
||||
|
||||
# Parallel gather
|
||||
internal_data, external_data = await asyncio.gather(
|
||||
@@ -244,7 +269,7 @@ async def run_compilation(user_id: int, slot: str, model: str | None = None) ->
|
||||
),
|
||||
_llm_synthesise(
|
||||
_external_system_prompt(),
|
||||
_external_user_prompt(external_data, slot),
|
||||
_external_user_prompt(external_data, slot, temp_unit),
|
||||
model,
|
||||
),
|
||||
)
|
||||
@@ -268,9 +293,10 @@ async def run_slot_injection(user_id: int, slot: str, model: str | None = None)
|
||||
if model is None:
|
||||
model = await get_setting(user_id, "default_model", Config.OLLAMA_MODEL)
|
||||
|
||||
internal_data, external_data = await asyncio.gather(
|
||||
internal_data, external_data, temp_unit = await asyncio.gather(
|
||||
_gather_internal(user_id),
|
||||
_gather_external(user_id),
|
||||
_get_temp_unit(user_id),
|
||||
)
|
||||
|
||||
system = (
|
||||
@@ -281,6 +307,6 @@ async def run_slot_injection(user_id: int, slot: str, model: str | None = None)
|
||||
f"Slot: {slot}\n\n"
|
||||
+ _internal_user_prompt(internal_data, slot)
|
||||
+ "\n\n"
|
||||
+ _external_user_prompt(external_data, slot)
|
||||
+ _external_user_prompt(external_data, slot, temp_unit)
|
||||
)
|
||||
return await _llm_synthesise(system, user_prompt, model)
|
||||
|
||||
Reference in New Issue
Block a user