/
/
/
1"""Constants for the AI Radio plugin."""
2
3from __future__ import annotations
4
5from typing import Any
6
7CONF_AI_ENGINE = "ai_engine"
8CONF_TTS_ENGINE = "tts_engine"
9CONF_TIMEZONE = "timezone"
10CONF_WEATHER_CITY = "weather_city"
11CONF_WEATHER_COUNTRY = "weather_country"
12
13# providers load concurrently, so the plugin supplying the engines may still be
14# loading when AI Radio initializes: wait this long for it before giving up
15ENGINE_DISCOVERY_TIMEOUT = 30
16
17# grace period for an engine that disappears while AI Radio is loaded. Generous enough
18# to sit out a Home Assistant restart, so a running show is not torn down for it
19ENGINE_RECHECK_GRACE = 300
20
21# how long to wait before reloading after an engine stayed missing, matching the
22# cadence the load path uses for its own retries
23ENGINE_RETRY_DELAY = 120
24
25TRANSLATION_OWNER = "provider.ai_radio"
26
27DEFAULT_LLM_INSTRUCTIONS = (
28 "Host personality: warm, sharp, music-literate, and slightly premium "
29 "without sounding formal. Program instructions: write for spoken delivery, "
30 "keep segments concise, avoid bullet-point phrasing, avoid clichés, "
31 "mention concrete details when available, and maintain a believable "
32 "radio flow between sections."
33)
34# appended to every AI query on top of the station's own instructions: how a name has to be
35# spelled to survive the TTS engine is a pipeline concern, not a per-station style choice
36TTS_PRONUNCIATION_INSTRUCTIONS = (
37 "The output is sent directly to a text-to-speech engine. "
38 "Always write names exactly as they should be spoken aloud. Replace stylized spellings, "
39 "acronyms, abbreviations, and unusual artist or band names with their natural spoken "
40 "equivalents. Never include the original spelling, pronunciation explanation, phonetic "
41 "notation, or both versions. Output only the spoken version. Examples: INXS â In Excess; "
42 "Mi-Sex â My Sex; P!nk â Pink; blink-182 â Blink One Eighty-Two. If a name could be "
43 "mispronounced by the TTS engine, rewrite it into the clearest natural spoken form "
44 "without explaining the change."
45)
46DEFAULT_WEATHER_PROVIDER = "open_meteo"
47DEFAULT_WEATHER_TIMEOUT_SECONDS = 20
48DEFAULT_MAX_CONCURRENT_RUNS = 1
49MAX_FINISHED_SESSIONS = 20
50
51# a show whose playback never starts within this window is declared failed
52SHOW_START_TIMEOUT_SECONDS = 300
53
54# last-resort guards so a wedged engine fails the clip instead of hanging the session.
55# Kept above the deadlines the engines apply themselves (120s in the OpenAI-compatible
56# providers), so their own, more specific error is the one that surfaces.
57AI_QUERY_TIMEOUT_SECONDS = 180
58TTS_QUERY_TIMEOUT_SECONDS = 180
59
60# ffprobe reports no status code, so its message is all we have to spot a failed render
61TTS_SERVER_ERROR_MARKERS = ("Server returned 5XX", "HTTP error 5")
62
63SUPPORTED_FEATURES: set[Any] = set()
64EMPTY_SECTION_ID = "EMPTY_SECTION"
65VALID_WEB_SEARCH_MODES = {"disabled", "allow", "force"}
66WEB_SEARCH_MODE_RANK = {"disabled": 0, "allow": 1, "force": 2}
67
68# QueueItem.extra_attributes keys carrying a clip's pending render state. Scalars only â
69# extra_attributes is serialized to clients and persisted with the queue.
70ATTR_SESSION_ID = "ai_radio_session_id"
71ATTR_STATION_ID = "ai_radio_station_id"
72ATTR_PROMPT = "ai_radio_prompt"
73ATTR_MAX_CHARS = "ai_radio_max_chars"
74ATTR_WEB_SEARCH_MODE = "ai_radio_web_search_mode"
75ATTR_RENDERED_TEXT = "ai_radio_rendered_text"
76
77# placeholders resolved at render time rather than at plan time, so the aired script
78# reflects the moment it plays
79DEFERRED_PLACEHOLDERS = frozenset({"<timestamp>", "<weather_hourly>", "<weather_daily>"})
80
81# HA drops a tts_proxy token 60s after its last use at the lowest configurable time_memory
82CLIP_STREAMDETAILS_EXPIRATION = 60
83