diff --git a/current-functions.txt b/current-functions.txt index 357e8f7..1ef6124 100644 --- a/current-functions.txt +++ b/current-functions.txt @@ -94,8 +94,16 @@ target: entity_id: media_player.spotify_bhetherman data: - source: "{{ room }}" - - delay: "00:00:02" + source: "{{ room | replace('Living Room', 'd0166d6f83796b0f9059b0adf0f6cb0d81fa6376') | replace('Bedroom', 'a334c8ba989367b52303eaa9dbc424cdd83012b5') }}" + - repeat: + while: + - condition: template + value_template: >- + {{ repeat.index <= 8 and + (state_attr('media_player.spotify_bhetherman', 'supported_features') | int(0) + | bitwise_and(512)) != 512 }} + sequence: + - delay: "00:00:01" - service: media_player.play_media target: entity_id: media_player.spotify_bhetherman @@ -137,8 +145,16 @@ target: entity_id: media_player.spotify_bhetherman data: - source: "{{ room }}" - - delay: "00:00:02" + source: "{{ room | replace('Living Room', 'd0166d6f83796b0f9059b0adf0f6cb0d81fa6376') | replace('Bedroom', 'a334c8ba989367b52303eaa9dbc424cdd83012b5') }}" + - repeat: + while: + - condition: template + value_template: >- + {{ repeat.index <= 8 and + (state_attr('media_player.spotify_bhetherman', 'supported_features') | int(0) + | bitwise_and(512)) != 512 }} + sequence: + - delay: "00:00:01" - service: media_player.play_media target: entity_id: media_player.spotify_bhetherman diff --git a/current-prompt.txt b/current-prompt.txt index 75b0222..9f175ba 100644 --- a/current-prompt.txt +++ b/current-prompt.txt @@ -1,18 +1,6 @@ I want you to act as smart home manager of Home Assistant. I will provide information of smart home along with a question, you will truthfully make correction or answer using information provided in one sentence in everyday language. -Current Time: {{now()}} - -Available Devices: -```csv -entity_id,name,state,aliases -{% for entity in exposed_entities -%} -{{ entity.entity_id }},{{ entity.name }},{{ entity.state }},{{entity.aliases | join('/')}} -{% endfor -%} -``` - -Available Spotify Connect rooms (valid values for the "room" parameter): {{ state_attr('media_player.spotify_bhetherman', 'source_list') | join(', ') }} - The current state of devices is provided in available devices. Use execute_services function only for requested action, not for current states. Do not execute service without user's confirmation. Exception: music playback commands (search_spotify, play_music, queue_music, control_playback) never require confirmation — execute immediately per the Music Playback Rules below. @@ -36,8 +24,8 @@ RULE: if the request has the form "[title] by [artist]" (a specific song/album t How to fill in search_spotify(query, type) — match these examples: - "Play Coldplay" → query="Coldplay", type="artist" - "Play songs by Taylor Swift on the kitchen speaker" → query="Taylor Swift", type="artist" -- "Play some music by Jungle in the living room" → query="Jungle", type="artist" -- "Play back on 74 in the living room" → query="Back on 74", type="track" (this is a song title, not a volume command) +- "Play some music by Jungle" → query="Jungle", type="artist" +- "Play back on 74" → query="Back on 74", type="track" (this is a song title, not a volume command) - "Play the song Yellow" → query="Yellow", type="track" - "Play Yellow by Coldplay" → query="Yellow", type="track" - "Play 1979 by the Smashing Pumpkins" → query="1979", type="track" (a bare number here is a song title, not an album — default to "track" unless the user explicitly says "album") @@ -54,9 +42,22 @@ Volume/playback control examples (use control_playback, NOT search_spotify): - "Skip this song" → action="next_track" Room: -- The "room" parameter for play_music/queue_music must exactly match one of the Available Spotify Connect rooms listed above. +- The "room" parameter for play_music/queue_music must exactly match one of the Available Spotify Connect rooms listed below. - If no room is specified, default room="Living Room" - control_playback never takes a room parameter — it acts on whatever is currently playing After playing an artist (type="artist" search), also call control_playback with action="shuffle_on". -Playlist searches check the user's own library first, then fall back to public Spotify. \ No newline at end of file +Playlist searches check the user's own library first, then fall back to public Spotify. + +Current Date: {{ now().strftime('%B %d, %Y') }} +Current Time: {{ now().strftime('%I:%M %p') }} + +Available Devices: +```csv +entity_id,name,state,aliases +{% for entity in exposed_entities -%} +{{ entity.entity_id }},{{ entity.name }},{{ entity.state }},{{entity.aliases | join('/')}} +{% endfor -%} +``` + +Available Spotify Connect rooms (valid values for the "room" parameter): {{ state_attr('media_player.spotify_bhetherman', 'source_list') | join(', ') | replace('d0166d6f83796b0f9059b0adf0f6cb0d81fa6376', 'Living Room') | replace('a334c8ba989367b52303eaa9dbc424cdd83012b5', 'Bedroom') }} \ No newline at end of file diff --git a/test_prompt.py b/test_prompt.py index e1bfb0e..a05870a 100644 --- a/test_prompt.py +++ b/test_prompt.py @@ -26,14 +26,28 @@ MODEL = os.environ.get("OLLAMA_MODEL") # set this to the exact `ollama list` ta MOCK_SOURCE_LIST = ["Living Room", "Web Player (Firefox)", "Bedroom"] +# Mirrors the real (currently broken) source_list this entity actually reports — +# Spotify Connect device IDs instead of friendly names — to exercise the +# opaque-ID fallback mapping added to current-prompt.txt's Room section. +MOCK_SOURCE_LIST_IDS = [ + "a334c8ba989367b52303eaa9dbc424cdd83012b5", + "d0166d6f83796b0f9059b0adf0f6cb0d81fa6376", +] +ROOM_NAME_TO_ID = { + "Living Room": "a334c8ba989367b52303eaa9dbc424cdd83012b5", + "Bedroom": "d0166d6f83796b0f9059b0adf0f6cb0d81fa6376", +} -def render_prompt(): + +def render_prompt(source_list=None): + if source_list is None: + source_list = MOCK_SOURCE_LIST with open(os.path.join(HERE, "current-prompt.txt")) as f: raw = f.read() def state_attr(entity_id, attr): if entity_id == "media_player.spotify_bhetherman" and attr == "source_list": - return MOCK_SOURCE_LIST + return source_list return None template = Template(raw) @@ -447,6 +461,18 @@ def main(): for label, user_message, expect_step2_name, expect_room, expect_type in TESTS_FLOW: results.append((label, check_flow(label, user_message, expect_step2_name, expect_room, expect_type, system_prompt, tools))) + print(f"\n{'#' * 40}\n# Same flow tests, but source_list reports opaque IDs (regression test for\n" + f"# current-prompt.txt's ID-to-name normalization in the Room section — the\n" + f"# model should still see and use plain names; it never has to know IDs exist)\n{'#' * 40}") + system_prompt_ids = render_prompt(MOCK_SOURCE_LIST_IDS) + for label, user_message, expect_step2_name, expect_room, expect_type in TESTS_FLOW: + if expect_room not in ROOM_NAME_TO_ID: + continue + results.append(( + f"{label} [opaque-ID rooms]", + check_flow(label, user_message, expect_step2_name, expect_room, expect_type, system_prompt_ids, tools), + )) + total_passed = sum(passed for _, (passed, n) in results) total_attempts = sum(n for _, (passed, n) in results) reliable = [label for label, (passed, n) in results if passed == n]