From 4fa477bdb0505a73ff9903926846d8e016a08ea0 Mon Sep 17 00:00:00 2001 From: diyaa Date: Mon, 14 Sep 2026 22:04:35 +0200 Subject: [PATCH] Add section performance directives --- .../PromptCompiler/PromptCompiling.swift | 17 ++++++---- .../SongProjectPromptCompilerTests.swift | 31 +++++++++++++++++++ docs/TASKS.md | 2 +- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/Sources/MusicAssistantCore/Services/PromptCompiler/PromptCompiling.swift b/Sources/MusicAssistantCore/Services/PromptCompiler/PromptCompiling.swift index 1a805a2..a4dc16d 100644 --- a/Sources/MusicAssistantCore/Services/PromptCompiler/PromptCompiling.swift +++ b/Sources/MusicAssistantCore/Services/PromptCompiler/PromptCompiling.swift @@ -30,15 +30,20 @@ public struct SongProjectPromptCompiler: PromptCompiling { } return project.orderedSections - .compactMap { section in - let lyrics = project.lyrics.sectionTexts[section.id] ?? section.lyrics - let trimmedLyrics = lyrics.trimmingCharacters(in: .whitespacesAndNewlines) - guard !trimmedLyrics.isEmpty else { return nil } - return "[\(section.title)]\n\(trimmedLyrics)" - } + .compactMap { sectionLyricsBlock(for: $0, project: project) } .joined(separator: "\n\n") } + private func sectionLyricsBlock(for section: SongSection, project: SongProject) -> String? { + let lyrics = project.lyrics.sectionTexts[section.id] ?? section.lyrics + let trimmedLyrics = lyrics.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmedLyrics.isEmpty else { return nil } + + let performanceDirective = normalized(section.vocalDirection) + let header = performanceDirective.map { "[\(section.title) - \($0)]" } ?? "[\(section.title)]" + return "\(header)\n\(trimmedLyrics)" + } + private func stylePrompt(for project: SongProject) -> String { let context = SongGenerationContext(project: project) var components: [String] = [] diff --git a/Tests/MusicAssistantCoreTests/SongProjectPromptCompilerTests.swift b/Tests/MusicAssistantCoreTests/SongProjectPromptCompilerTests.swift index d30fa2d..a6389da 100644 --- a/Tests/MusicAssistantCoreTests/SongProjectPromptCompilerTests.swift +++ b/Tests/MusicAssistantCoreTests/SongProjectPromptCompilerTests.swift @@ -62,4 +62,35 @@ final class SongProjectPromptCompilerTests: XCTestCase { XCTAssertEqual(output.lyricsText, "[Verse]\nVerse line\n\n[Chorus]\nHook line") } + + func testCompilerIncludesSectionPerformanceDirectivesInFallbackLyrics() throws { + let project = SongProject( + title: "Directed Sections", + idea: "Compile performance directions", + sections: [ + SongSection( + id: "intro", + type: .intro, + title: "Intro", + lyrics: "First line", + vocalDirection: "whispered lead" + ), + SongSection( + id: "chorus", + type: .chorus, + title: "Chorus", + order: 1, + lyrics: "Original hook" + ) + ], + lyrics: Lyrics(sectionTexts: ["chorus": "Approved hook"]) + ) + + let output = try SongProjectPromptCompiler().compile(project: project) + + XCTAssertEqual( + output.lyricsText, + "[Intro - whispered lead]\nFirst line\n\n[Chorus]\nApproved hook" + ) + } } diff --git a/docs/TASKS.md b/docs/TASKS.md index a9a6023..91cabe8 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -98,7 +98,7 @@ requirement is missing and blocks implementation, record it in - [x] Create deterministic compiler from approved SongProject → Suno output. -- [ ] Generate lyrics text with section/performance directives where +- [x] Generate lyrics text with section/performance directives where appropriate. - [ ] Generate style prompt from genre blend, instrumentation, vocals, tempo, harmony, emotion and production instructions.