Add album_artist, year, rating, file_path column to data layer across all providers#306
Conversation
Capture the original album artist before _select_best_artist() overwrites it with the track-level artist. This preserves the album-level artist (e.g. for compilation albums) in a new `album_artist` column in the score table, propagated through all media server modules (Jellyfin, Emby, Navidrome, Lyrion, MPD), the analysis pipeline, similarity search, song alchemy, path manager, and API responses. Also fix a pre-existing bug in Emby's standalone track path where _select_best_artist() return tuple was not unpacked. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Start collecting additional information could be useful as a basis of future functionality. Did you have something in mind? About mediaserver to test, just one correction, you need to integrate only Jellyfin, Emby, Navidrome, Lyrion. IMPORTANT: Navidrome is the configuration for all the Music Server that support the Open Subsonic standard. Ok maybe cover all is not always possible (someone have some small difference) but I usually tested this kind of change on Navidrome AND Lightweight Music Server For MPD I tried add integration but I was never able to complete basically because I wasn't able to directly download song from MPD for the analysis (maybe if you have any idea integrate MPD or other Music Server in future could be nice). So you can skip it because is incomplete. |
|
My goal is to make a Curated Playlist Builder, this would be great metadata to have in there. I was thinking, when you have local filepath you can:
Regarding Album, there's two paths I think:
The expand album is very much in line for what I want with the second part of my Playlist system - the Expand playlist. You basically expand from N tracks, taking not just the last song as data-point but the group. This would be the same for playlist and album expansion. Similar album is a different approach, matches groups with groups. I wonder how much that would get used. It'd say it's fun from an exploration perspective if you have a large library, but not the best way to stay in a certain mood. |
|
Local file path need to be think carefully:
On this last point it could be nice the idea of a .m3u output for music server that support it. So you don't have to use API at all (and you don't have to match a file path with the ID of the music server). It maybe could be impelmented as a "local-path" music server. The result should be you analyze locally, your output is local file, and then you can import where you want. |
Two Docker Compose files for end-to-end testing of the album_artist column across all providers (Jellyfin, Emby, Navidrome, Lyrion — MPD excluded): - Providers stack with shared test_music mount - Per-provider NVIDIA AudioMuse instances with isolated Redis/Postgres - Bash validation script that queries each Postgres for album_artist data - Step-by-step test guide covering provider setup, API keys, and checklist https://claude.ai/code/session_01AU49aWqCYybatiX1yhK6UD
Build the image from the repo Dockerfile with the nvidia/cuda base instead of pulling from the registry. The flask-jellyfin service owns the build; all other services reuse audiomuse-ai:test-nvidia with pull_policy: never. https://claude.ai/code/session_01AU49aWqCYybatiX1yhK6UD
Replaces named Docker volumes with host bind mounts so provider
config and data persist in testing/providers/{jellyfin,emby,
navidrome,lyrion}/. Added testing/.gitignore to exclude the
providers/ directory and .env.test from version control.
https://claude.ai/code/session_01AU49aWqCYybatiX1yhK6UD
|
Successfully tested. I'm going to look into the other metadata fields before pushing the PR.
|
|
I've added and tested the following fields:
*Navidrome by defaults reports an "Internal path", this is artist/album/track but not your real file path. If you want to match files across services, it's advised to turn on Report Real Path:
You can also change this as a default setting by setting ND_DEFAULTREPORTREALPATH=true |
…emain consistent between version (and not having to re-enable settings)
…dyhd/AudioMuse-AI into feature/album-artist-support
|
Thanks for you effort, to recap you added this 4 field all in the score table:
And for now you use only album artist as as fallback when album is not present in the difference "song search" form? All the other field are only for future implementation right? In your test, on all the mediaserver, did you try:
I'll do my test offcourse, but editing the mediaserver part we need extra attention. |
|
@rendyhd This is great. Having the ids along with the ratings is huge.... |
In your test, on all the mediaserver, did you try:
During my test just now I did find out that album name isn't populated for Navidrome and Lyrion - I haven't seen anything that could cause that regression. I can test tomorrow with the current release |
…r Lyrion and Navidrome
|
Check after last commit, album name is complete now too (rating is just a single song, so 0.3% is correct):
|
|
Finished AB testing the changes to instant playlist, I'll test the navidrome change next if it has breaking impact on file path. Not in the AB report is ratings test, I did that in a separate change, added more strict guidelines around that |
…uite - Refine MCP tool decision tree ordering (album/decade prioritized) - Improve system prompt for better tool selection strategy - Add unit tests for ai_mcp_client, app_chat, mcp_server, playlist_ordering - Gitignore testing_suite directory Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add rules 11-12 to system prompt: rating is a hard filter, combine all user-specified filters in every search_database call - Track detected min_rating from tool calls and post-filter collected songs against the database to remove any that leaked through - Cap agentic loop to 2 iterations when rating filter is active to prevent AI from broadening to unrelated genres - Add genre confidence threshold (0.55) to search_database so weak mood_vector matches (e.g. rock:0.52 on ambient tracks) are excluded - Instruct AI in iteration context to never drop original filters Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
I looked into the change: The "c" parameter is the Subsonic API client identifier, and Navidrome uses it to create and track players, if we make this change then the player name will change with every version bump (e.g., AudioMuse-AI/v0.9.1) This change would mean that this Path metadata would break every version bump. Can we check with the Navidrome developer for advise on this? |
|
Let's keep it easy, if you think that keeping only the application name without the version is better. for me is ok. The only change at this point is to correct name that is Thanks. |
Merge branch 'feature/ai-instant-playlist-upgrade' into feature/album-artist-support
Overhaul the AI instant playlist pipeline with a unified prompt architecture,
stricter filtering, smarter tool selection, playlist ordering, and 2,770 lines
of new unit tests across 4 test files.
=== Prompt & Tool Layer (ai_mcp_client.py) ===
- Add _build_system_prompt() as single canonical prompt for all 4 AI providers
(Gemini, OpenAI, Mistral, Ollama), replacing per-provider prompt duplication
- Inject real library context (genre/mood/year/rating stats) into system prompt
via get_library_context() so the AI knows what's actually in the library
- Add energy normalization: AI sees 0-1 range, converted to raw 0.01-0.15
in execute_mcp_tool using config.ENERGY_MIN/MAX
- Add prioritized tool decision tree (song_similarity > album/decade >
text_search > artist_similarity > song_alchemy > ai_brainstorm > search_database)
- Correct tool descriptions (artist_similarity returns artist's own songs + similar)
- Expand search_database parameters: scale, year_min/year_max, min_rating, album
- Add system prompt rules 11-12: rating is a hard filter, all user-specified
filters must appear in every search_database call
=== Backend Tools (tasks/mcp_server.py) ===
- Add get_library_context() with module-level cache for library stats
(total songs, unique artists, top genres, year range, rating coverage, scales)
- search_database: regex genre matching with (^|,)\s*genre: pattern to prevent
substring false positives (e.g. "rock" no longer matches "indie rock")
- search_database: relevance-scored ranking by genre confidence sum instead of
RANDOM() when genre filter is active
- search_database: genre confidence threshold (0.55) to exclude weak matches
- search_database: new filters for scale, year_min/year_max, min_rating, album
- text_search (CLAP): add genre keyword post-filter that checks mood_vector
overlap, removing off-genre CLAP results (with 40% safety floor)
- text_search: fix column name energy_normalized -> energy
- song_alchemy: add genre overlap post-filter comparing seed track genres
to result genres, keeping only songs with relevant genre overlap (40% floor)
- ai_brainstorm: replace per-song SQL queries with batched lookup; strict
2-stage matching (exact case-insensitive then normalized fuzzy) requiring
BOTH title AND artist to match
- Add album column to all SELECT statements and result dicts
=== Agentic Loop (app_chat.py) ===
- Switch from top-level config imports to runtime config.X module attribute
access so DB-applied settings (via apply_settings_to_config) take effect
- Add pre-execution validation: reject song_similarity with empty title/artist,
reject search_database with no filters
- Add rich iteration feedback: artist diversity stats, genres covered per round
- Enforce artist diversity cap via MAX_SONGS_PER_ARTIST_PLAYLIST (default 5)
with overflow backfill when pool is too small
- Post-collection rating enforcement: track detected min_rating from tool calls
and filter all collected songs against the database to remove any that leaked
- Cap agentic loop to 2 iterations when rating filter is active to prevent
the AI from broadening searches to unrelated genres
- Add iteration context instructing AI to never drop original filters
- Integrate playlist_ordering for smooth BPM/energy/key transitions
=== New Files ===
- tasks/playlist_ordering.py (189 lines): Greedy nearest-neighbor track
ordering using composite distance metric (tempo, energy, Circle of Fifths
key distance) for smooth playlist transitions
- tests/conftest.py (115 lines): Shared test fixtures with importlib bypass
to avoid tasks/__init__.py -> pydub -> audioop import chain
- tests/unit/test_mcp_server.py (1,322 lines): MCP server function tests
- tests/unit/test_ai_mcp_client.py (1,016 lines): AI client and prompt tests
- tests/unit/test_app_chat.py (302 lines): Chat endpoint tests
- tests/unit/test_playlist_ordering.py (130 lines): Ordering algorithm tests
=== Config ===
- MAX_SONGS_PER_ARTIST_PLAYLIST (default 5): artist diversity cap
- PLAYLIST_ENERGY_ARC (default false): energy arc ordering option
- .gitignore: exclude testing_suite/ directory
=== Fix ===
- Update Navidrome client identifier from "AudioMuse" to "AudioMuse-AI" for
consistency across server versions
|
I've merged the current version, however, there are 3 more potential upgrades I identified in testing. I could look at that later this week.
Also, it might be good to do a performance scorecard on a selection of models. I've done extensive testing using Haiku, Gemini Flash, and Sonnet (total of 1200+ requests to openrouter) while optimizing and improving performance. Here I saw a clear difference in using a model like Sonnet vs Haiku/Flash. If we want I can create a test script that runs every model 3x and creates a fair expectation for users. This would be done last though. What I've merged: Overhaul the AI instant playlist pipeline with a unified prompt architecture, === Prompt & Tool Layer (ai_mcp_client.py) ===
=== Backend Tools (tasks/mcp_server.py) ===
=== Agentic Loop (app_chat.py) ===
=== New Files ===
=== Config ===
=== Fix ===
|
|
Look there is an unit test that still fail pointing to your some your local directory. Please have a look and fix. I suggest to postpone any additional feature to future PR. In the weekend, if I have time, I could make a release to add the Authentication layer to AudioMuse-AI (user+password for the human user, api token for the plugin), if meanwhile this PR is ready and bug free it could be added to the release. Thanks. |
Use conftest._import_module helper with relative path instead of absolute C:/Users/rendy/... path so tests work on any machine. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The genre matching in mcp_server was refactored from ~* regex operator to SUBSTRING(mood_vector FROM ...) with CAST for confidence thresholds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
So I did test against an already exist AudioMuse-AI deployment (on INTEL CPU) that work on top of jellyfin where:
All the pushed fix are already against this PR branch, so you can directly pull and start your test. For the test I used I finish my run of test. When you run yours please share here the confirmation that they are completed and no bug is there to have the PR merged. Because here there is change in the mediaserver files, it's important that you run the test against all the mediaserver to avoid regression. Thanks. |
…elaxation - Add explicit decision tree step for specific year queries (year_min=year_max) - Add Ollama examples for year and decade+genre queries - Add Ollama-specific reminder to not invent extra filters - Replace fixed per-artist backfill with progressive cap relaxation (5→6→7...) so playlists reach target count instead of stopping short Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ompt quality - Increase gunicorn timeout from 120s to 300s to prevent worker kills on thinking model (Qwen 3.5) double-inference requests - Strip <think> tags from thinking model retry responses to fix JSON parsing - Add song_alchemy examples to Ollama prompt (Iron Maiden+Metallica, Daft Punk+Gorillaz) - Add "COMMON MISTAKES" section to prevent hallucinated extra filters - Add rule 14 (ACCEPT SMALL PLAYLISTS) to stop models padding with irrelevant songs - Guard text_search against metadata-only queries (e.g. "2026 songs") - Strip empty/default hallucinated args (tempo_min=0, min_rating=0) from tool calls - Cap tool calls per iteration to 10 to prevent pathological looping - Fix executed_query summary to include year_min/year_max and min_rating - Add Ollama env vars to local docker-compose Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ontrol
- Add scale (minor/major) to AI decision tree, examples, and common mistakes
so models use search_database(scale=) instead of genres/text_search
- Parallel brainstorm + catalog search for "top songs of [artist]" prompts
- Case-insensitive artist matching in ai_brainstorm (LOWER/LOWER)
- Tighten song_alchemy genre-coherence: top-3 seed genres, 0.2 threshold
(was top-5 at 0.1) to reduce off-genre alchemy results
- Year-only early stop: halt after 2 iterations to prevent irrelevant padding
- Fix iteration feedback wording to keep on-genre instead of diversifying
into unrelated genres
- Add scale to executed_query summary for visibility
- Fix test config: must_have_filter year_min -> year= to match actual format
- Handle Ollama thinking model {"tool","arguments"} JSON format (Qwen 3.5)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
I've done more optimization for ollama/small models.
|
|
I did some other test and now the intelligence of the AI seems working good. I also did a test with this function to check for hidden regression:
And I didn't find any regression. So I'm happy to say that this is ready to be merged. Please after the next release be on the windows in case users raise any issue related to this (I don't think I will release for this week, already too many release :) ) I think you are very strong with this AI related functionality and will be very valuable if in your next PR you would like to keep working on this topic. |
Capture the original album artist before _select_best_artist() overwrites it with the track-level artist. This preserves the album-level artist (e.g. for compilation albums) in a new
album_artistcolumn in the score table, propagated through all media server modules (Jellyfin, Emby, Navidrome, Lyrion), the analysis pipeline, similarity search, song alchemy, path manager, and API responses.Also fix a pre-existing bug in Emby's standalone track path where _select_best_artist() return tuple was not unpacked.
Also adds:
Year
Track Rating (for Navidrome and Lyrion)
File Path (note on Navidrome)*
Updated chat (instant playlist) with info in year and rating
*Navidrome by defaults reports an "Internal path", this is artist/album/track but not your real file path. If you want to match files across services, it's advised to turn on Report Real Path:
You can also change this as a default setting by setting ND_DEFAULTREPORTREALPATH=true