@@ -4,12 +4,17 @@ title: Genius playlist creator
44status : In Progress
55assignee : []
66created_date : ' 2026-02-18 05:58'
7- updated_date : ' 2026-03-31 21:18 '
7+ updated_date : ' 2026-03-31 21:27 '
88labels :
99 - feature
1010 - playlists
1111 - recommendation
12+ - agent
13+ - ollama
14+ - lastfm
1215dependencies : []
16+ references :
17+ - docs/genius.md
1318priority : high
1419ordinal : 750
1520---
@@ -39,7 +44,7 @@ Design document: `~/.claude/plans/steady-juggling-scroll.md`
3944## Implementation Plan
4045
4146<!-- SECTION:PLAN:BEGIN -->
42- ## Phase 1: Dependencies (DONE)
47+ ## Phase 1: Dependencies (DONE β commit fc5846b )
4348
4449Added to ` crates/mt-tauri/Cargo.toml ` :
4550
@@ -49,7 +54,7 @@ Added to `crates/mt-tauri/Cargo.toml`:
4954
5055Verified: ` cargo check --features agent ` and ` cargo check ` (without) both compile.
5156
52- ## Phase 2: Types + Prompt + Module Scaffold (DONE)
57+ ## Phase 2: Types + Prompt + Module Scaffold (DONE β commit 2f121f7 )
5358
5459Created ` crates/mt-tauri/src/agent/ ` module with 3 files:
5560
@@ -73,7 +78,8 @@ Created `crates/mt-tauri/src/agent/` module with 3 files:
7378### ` mod.rs `
7479- Module exports (` pub mod types; pub mod prompt; ` )
7580- ` parse_agent_response() ` β robust LLM output parser (handles brackets, preamble, mixed valid/invalid IDs)
76- - Placeholder ` agent_generate_playlist ` and ` agent_check_status ` functions
81+ - ` parse_model_names() ` β extracts model names from Ollama /api/tags JSON
82+ - ` has_default_model() ` β checks if default model is in available list (base name match)
7783- 8 unit tests for parser edge cases
7884
7985### ` lib.rs ` wiring
@@ -82,67 +88,107 @@ Created `crates/mt-tauri/src/agent/` module with 3 files:
8288- Graceful "not enabled" JSON response when feature is off
8389- Commands registered unconditionally in ` generate_handler! `
8490
85- Verified: ` cargo check --features agent ` , ` cargo check ` , and 17/17 agent tests pass.
91+ ## Phase 3: Tools (DONE β commit d9a37c9)
8692
87- ## Phase 3: Tools ( ` tools.rs ` )
93+ Implemented 8 tools with Rig's ` Tool ` trait in ` tools.rs ` , each receiving ` Arc<AgentContext> ` :
8894
89- Implement 8 tools with Rig's ` Tool ` trait, each receiving ` Arc<AgentContext> ` :
90-
91- 1 . ** GetRecentlyPlayed** β ` db::favorites::get_recently_played `
92- 2 . ** GetTopArtists** β ` db::stats::get_top_artists `
93- 3 . ** SearchLibrary** β ` db::library::get_all_tracks `
94- 4 . ** GetSimilarTracks** β Last.fm ` track.getSimilar ` + library cross-ref
95+ 1 . ** GetRecentlyPlayed** β ` db::favorites::get_recently_played ` with configurable limit/date range
96+ 2 . ** GetTopArtists** β ` db::stats::get_top_artists ` with configurable limit
97+ 3 . ** SearchLibrary** β ` db::library::get_all_tracks ` with keyword/artist/genre filters + limit
98+ 4 . ** GetSimilarTracks** β Last.fm ` track.getSimilar ` + library cross-ref via ` find_track_by_artist_title `
95995 . ** GetSimilarArtists** β Last.fm ` artist.getSimilar ` + library cross-ref
961006 . ** GetTrackTags** β Last.fm ` track.getTopTags `
971017 . ** GetTopArtistsByTag** β Last.fm ` tag.getTopArtists ` + library cross-ref
981028 . ** GetTopTracksByCountry** β Last.fm ` geo.getTopTracks ` + library cross-ref
99103
100- TDD: Unit test each tool's ` call() ` with test DB; mock Last.fm responses for similarity tools.
104+ Also added 5 Last.fm discovery methods to ` lastfm/client.rs ` and response types to ` lastfm/types.rs ` .
105+ 12 tool tests + all tools have ` name() ` , ` description() ` , ` definition() ` verified.
106+
107+ ## Phase 4: Agent Loop + Tauri Commands (DONE β commit d9a37c9)
101108
102- ## Phase 4: Agent Loop + Tauri Commands
109+ Implemented in ` mod.rs ` :
110+ - ` check_ollama() ` β GET to ` {OLLAMA_BASE_URL}/api/tags ` , returns raw JSON string
111+ - ` build_agent(ctx) ` β constructs Rig agent with Ollama client, all 8 tools, system prompt, multi_turn(5)
112+ - ` agent_generate_playlist(db, prompt) ` β full flow: health check -> build agent -> run prompt -> parse response -> create playlist via ` db::playlists::create_playlist ` + ` add_tracks_to_playlist ` -> return AgentResponse
113+ - ` agent_check_status(db) ` β checks Ollama availability + model presence -> returns AgentStatusResponse
103114
104- - ` build_agent(ctx) ` β construct Rig agent with all 8 tools + system prompt
105- - Wire real implementation into ` agent_generate_playlist ` : health check -> build agent -> run (max 5 turns) -> parse track IDs -> create playlist
106- - Wire real implementation into ` agent_check_status ` : check Ollama + model availability
115+ Key patterns: ` LastFmClient::new() ` constructed fresh (not Tauri state), ` Arc<AgentContext> ` shared across tools, graceful NoOllama/NoModel status returns.
107116
108- ## Phase 5: Onboarding + Setup (` setup.rs ` )
117+ ## Phase 5: Onboarding + Setup (DONE β commit f1cf52c )
109118
110- Tauri commands:
119+ Created ` setup.rs ` with 4 public functions:
120+ - ` check_ollama_status() ` β wraps ` check_ollama() ` + ` parse_model_names() ` , returns ` OllamaStatus { available, models } `
121+ - ` pull_model(app, model) ` β POST streaming to ` {OLLAMA_BASE_URL}/api/pull ` , emits ` agent://pull-progress ` Tauri events with ` PullProgress { status, completed, total } ` , returns ` PullModelResult { success, error } `
122+ - ` get_onboarding_state(app) ` β reads ` OnboardingState ` from ` agent.json ` store (key: ` agent_onboarding ` ), defaults to ` { complete: false, model: None } `
123+ - ` set_onboarding_complete(app, model) ` β writes ` OnboardingState { complete: true, model: Some(model) } ` to store
111124
112- - ` agent_check_ollama() ` β health check Ollama, list models
113- - ` agent_pull_model(model) ` β stream model download with progress events
114- - ` agent_get_onboarding_state() ` / ` agent_set_onboarding_complete() ` β persist via tauri-plugin-store
125+ New types in ` types.rs ` : ` OllamaStatus ` , ` PullProgress ` , ` OnboardingState ` , ` PullModelResult `
126+ 4 new Tauri command pairs (cfg/not-cfg) in ` lib.rs ` : ` agent_check_ollama ` , ` agent_pull_model ` , ` agent_get_onboarding_state ` , ` agent_set_onboarding_complete `
127+ 7 unit tests in setup.rs + 6 type tests in types.rs (13 total for Phase 5).
115128
116- ## Phase 6: Evals (` evals.rs ` )
129+ ## Phase 6: Evals (DONE β commit 27f9c2e, cfg-gate fix fd8593a )
117130
118- Heuristic evals (no LLM judge) :
131+ 13 heuristic eval tests in ` evals.rs ` using wiremock mock Ollama server :
119132
120- - Tool selection evals (correct tools per prompt class)
121- - Output format evals (parseable response, no hallucinated IDs)
122- - Degradation evals (graceful fallback when Last.fm tools fail)
133+ - ** Tool execution evals (3) ** : ` eval_tool_execution_search_library ` , ` eval_tool_execution_get_recently_played ` , ` eval_tool_execution_get_top_artists ` β verify mock Ollama tool-call requests trigger correct tool execution and return valid data
134+ - ** Output format evals (6) ** : ` eval_output_format_valid_response ` , ` eval_output_format_with_preamble ` , ` eval_output_format_bracketed_ids ` , ` eval_output_format_no_valid_ids ` , ` eval_output_format_missing_playlist_line ` , ` eval_output_format_hallucinated_ids_filtered ` β verify ` parse_agent_response() ` handles all LLM output variations
135+ - ** Degradation evals (5) ** : ` eval_degradation_ollama_unreachable ` , ` eval_degradation_ollama_healthy ` , ` eval_degradation_empty_tool_result ` , ` eval_degradation_invalid_tags_response ` , ` eval_degradation_no_models_available ` β verify graceful fallback behavior
123136
124- Gated behind ` --features agent ` ; CI uses mock HTTP server.
137+ Refactored ` build_agent() ` and ` check_ollama() ` to accept ` base_url: &str ` parameter for test injection.
138+ Added ` wiremock = "0.6" ` to dev-dependencies.
139+ 753/753 tests pass.
125140
126- ## Phase 7: Frontend β Genius Sidebar Category + Prompt UI
141+ Cfg-gate fix (fd8593a): After merging main (which had reverted the lastfm discovery methods), restored the methods and gated all discovery types, methods, and tests behind ` #[cfg(feature = "agent")] ` so default builds have zero dead_code warnings.
127142
128- - Genius sidebar category with wayfarer glasses icon
143+ ## Phase 7: Frontend β Genius Sidebar Category + Prompt UI β NOT STARTED
144+
145+ - Genius sidebar category with wayfarer glasses icon (in sidebar nav alongside Library, Playlists, etc.)
129146- Natural language prompt input + Generate button
130- - Loading state while agent runs
147+ - Loading state while agent runs (listen for ` agent://pull-progress ` events during model download)
131148- Onboarding wizard (3 steps): Ollama check -> model download -> ready
149+ - Wire to Tauri commands: ` agent_check_ollama ` , ` agent_pull_model ` , ` agent_get_onboarding_state ` , ` agent_set_onboarding_complete ` , ` agent_generate_playlist ` , ` agent_check_status `
150+ - Alpine.js store for agent state, basecoat/Tailwind for UI components
151+
152+ Must satisfy AC #1 (prompt input), AC #2 (LLM interpretation), AC #3 (8 tools visible), AC #4 (local tracks only), AC #5 (graceful degradation UX).
132153<!-- SECTION:PLAN:END -->
133154
134155## Implementation Notes
135156
136157<!-- SECTION:NOTES:BEGIN -->
137158## Current State (2026-03-31)
138159
139- - Phase 1 (deps) complete
140- - Phase 2 (types + prompt + module scaffold) complete β 3 files, 17 tests
141- - Phase 3 (tools) complete β 8 Tool trait implementations, 12 tests
142- - Phase 4 (agent loop + Tauri commands) complete β real implementations, 20 tests in mod.rs
160+ - Phase 1 (deps) complete β commit fc5846b
161+ - Phase 2 (types + prompt + module scaffold) complete β commit 2f121f7
162+ - Phase 3 (tools) complete β commit d9a37c9
163+ - Phase 4 (agent loop + Tauri commands) complete β commit d9a37c9
164+ - Phase 5 (onboarding + setup) complete β commit f1cf52c
165+ - Phase 6 (evals) complete β commit 27f9c2e, cfg-gate fix fd8593a
166+ - Merged into main and pushed (fast-forward merge fd8593a)
167+
168+ 753/753 tests pass with ` cargo nextest run --workspace --features agent ` .
169+ Both ` cargo check --features agent ` and ` cargo check ` compile cleanly (zero warnings).
170+
171+ ## Phase 5 Summary
172+
173+ Added ` setup.rs ` with 4 public functions + 13 new tests:
174+ - ` check_ollama_status() ` β health check returning OllamaStatus (available/unavailable + model list)
175+ - ` pull_model(app, model) ` β POST streaming to Ollama, emits ` agent://pull-progress ` Tauri events
176+ - ` get_onboarding_state(app) ` β reads from ` agent.json ` store
177+ - ` set_onboarding_complete(app, model) ` β writes to ` agent.json ` store
178+
179+ New types in ` types.rs ` : OllamaStatus, PullProgress, OnboardingState, PullModelResult
143180
144- Both ` cargo check --features agent ` and ` cargo check ` pass.
145- 726/726 tests pass with ` cargo nextest run --workspace --features agent ` .
181+ 4 new Tauri command pairs (cfg/not-cfg) wired in lib.rs:
182+ - ` agent_check_ollama ` , ` agent_pull_model ` , ` agent_get_onboarding_state ` , ` agent_set_onboarding_complete `
183+
184+ ## Phase 6 Summary
185+
186+ 13 eval tests in ` evals.rs ` using wiremock mock Ollama server.
187+ Categories: tool execution (3), output format (6), degradation (5).
188+ Refactored ` build_agent() ` and ` check_ollama() ` to accept ` base_url: &str ` for test injection.
189+ Added ` wiremock = "0.6" ` to dev-dependencies.
190+
191+ Cfg-gate fix: After merging main (which had reverted lastfm discovery methods), restored them and gated all discovery types, methods, and tests behind ` #[cfg(feature = "agent")] ` .
146192
147193## Key Design Decisions
148194
@@ -153,6 +199,8 @@ Both `cargo check --features agent` and `cargo check` pass.
153199- Thin wrapper Tauri commands in lib.rs with cfg pairs (agent/not-agent) to keep generate_handler! unconditional
154200- Agent module functions are plain async fns (not #[ tauri::command] ) β lib.rs wrappers handle Tauri integration
155201- LastFmClient constructed fresh inside agent_generate_playlist (not managed as Tauri state)
202+ - Onboarding state persisted via tauri-plugin-store (` agent.json ` with key ` agent_onboarding ` )
203+ - Model pull uses streaming POST to Ollama with Tauri event emission for progress
156204
157205## Rig API Notes (v0.27 + experimental)
158206
@@ -165,33 +213,19 @@ Both `cargo check --features agent` and `cargo check` pass.
165213## Files Created/Modified
166214
167215- ` crates/mt-tauri/src/agent/mod.rs ` β module root, parse_agent_response, parse_model_names, has_default_model, check_ollama, build_agent, agent_generate_playlist, agent_check_status
168- - ` crates/mt-tauri/src/agent/types.rs ` β AgentResponse, TrackSummary, AgentContext, AgentError, ParsedPlaylist, AgentStatusResponse
216+ - ` crates/mt-tauri/src/agent/types.rs ` β AgentResponse, TrackSummary, AgentContext, AgentError, ParsedPlaylist, AgentStatusResponse, OllamaStatus, PullProgress, OnboardingState, PullModelResult
169217- ` crates/mt-tauri/src/agent/prompt.rs ` β SYSTEM_PROMPT, DEFAULT_MODEL, OLLAMA_BASE_URL, MAX_AGENT_TURNS
170218- ` crates/mt-tauri/src/agent/tools.rs ` β 8 Tool implementations
171- - ` crates/mt-tauri/src/lastfm/client.rs ` β 5 discovery methods (get_similar_tracks, get_similar_artists, get_track_top_tags, get_top_artists_by_tag, get_top_tracks_by_country)
172- - ` crates/mt-tauri/src/lastfm/types.rs ` β response types for discovery methods
173- - ` crates/mt-tauri/Cargo.toml ` β rig-core + schemars deps
174- - ` crates/mt-tauri/src/lib.rs ` β agent module declaration + wrapper Tauri commands
175-
176- ## Next: Phase 5 (Onboarding + Setup)
177-
178- ## Phase 6 Complete (2026-03-31)
179-
180- - Phase 6 (evals) complete β 13 heuristic eval tests in ` evals.rs ` using wiremock mock Ollama server
181- - Categories: tool execution (3), output format (6), degradation (5)
182- - Refactored ` build_agent() ` and ` check_ollama() ` to accept ` base_url: &str ` for test injection
183- - Added ` wiremock = "0.6" ` to dev-dependencies
184- - 753/753 tests pass with ` cargo nextest run --workspace --features agent `
185- - All 14 agent eval tests pass (13 new + 1 existing build_agent test)
186-
187- ## Files Created/Modified (Phase 6)
219+ - ` crates/mt-tauri/src/agent/setup.rs ` β check_ollama_status, pull_model, get/set_onboarding_state, parse_pull_progress_line
220+ - ` crates/mt-tauri/src/agent/evals.rs ` β 13 eval tests with wiremock
221+ - ` crates/mt-tauri/src/lastfm/client.rs ` β 5 discovery methods (cfg-gated behind agent feature)
222+ - ` crates/mt-tauri/src/lastfm/types.rs ` β response types for discovery methods (cfg-gated behind agent feature)
223+ - ` crates/mt-tauri/Cargo.toml ` β rig-core + schemars deps, wiremock dev-dependency
224+ - ` crates/mt-tauri/src/lib.rs ` β agent module declaration + 6 wrapper Tauri commands (cfg pairs)
188225
189- - ` crates/mt-tauri/src/agent/evals.rs ` β NEW: 13 eval tests with wiremock
190- - ` crates/mt-tauri/src/agent/mod.rs ` β refactored build_agent/check_ollama signatures, added ` mod evals `
191- - ` crates/mt-tauri/src/agent/setup.rs ` β updated check_ollama_status caller
192- - ` crates/mt-tauri/Cargo.toml ` β added wiremock dev-dependency
226+ ## Remaining
193227
194- ## Next: Phase 7 ( Frontend β Genius Sidebar Category + Prompt UI)
228+ - Phase 7: Frontend β Genius sidebar category, prompt UI, onboarding wizard UI
195229<!-- SECTION:NOTES:END -->
196230
197231## Definition of Done
0 commit comments