99 lines
3.4 KiB
Swift
99 lines
3.4 KiB
Swift
import Foundation
|
|
|
|
public final class SongProjectGenerationDirector: Sendable {
|
|
private let aiService: any AIService
|
|
|
|
public init(aiService: any AIService) {
|
|
self.aiService = aiService
|
|
}
|
|
|
|
public func generateCompleteProject(
|
|
fromIdea idea: String,
|
|
seedProject: SongProject? = nil,
|
|
discussionMode: ConversationMode = .auto,
|
|
conversation: [AIConversationMessage] = [],
|
|
localeIdentifier: String? = nil,
|
|
generatedAt: Date = Date()
|
|
) async throws -> SongProjectGenerationResult {
|
|
let trimmedIdea = idea.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard !trimmedIdea.isEmpty else {
|
|
throw SongProjectGenerationDirectorError.emptyIdea
|
|
}
|
|
|
|
let request = SongProjectGenerationRequest(
|
|
context: AIRequestContext(
|
|
userInstruction: trimmedIdea,
|
|
conversation: conversation,
|
|
localeIdentifier: localeIdentifier
|
|
),
|
|
seedProject: seedProject,
|
|
discussionMode: discussionMode
|
|
)
|
|
let result = try await aiService.generateSongProject(from: request)
|
|
let project = normalizeGeneratedProject(
|
|
result.project,
|
|
idea: trimmedIdea,
|
|
seedProject: seedProject,
|
|
discussionMode: discussionMode,
|
|
generatedAt: generatedAt
|
|
)
|
|
|
|
return SongProjectGenerationResult(
|
|
project: project,
|
|
notes: result.notes,
|
|
followUpQuestions: result.followUpQuestions
|
|
)
|
|
}
|
|
|
|
private func normalizeGeneratedProject(
|
|
_ generatedProject: SongProject,
|
|
idea: String,
|
|
seedProject: SongProject?,
|
|
discussionMode: ConversationMode,
|
|
generatedAt: Date
|
|
) -> SongProject {
|
|
let normalizedIdea = generatedProject.idea.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
|
? idea
|
|
: generatedProject.idea
|
|
|
|
guard let seedProject else {
|
|
var project = generatedProject
|
|
project.idea = normalizedIdea
|
|
project.conversationMode = discussionMode
|
|
project.updatedAt = generatedAt
|
|
return project
|
|
}
|
|
|
|
return SongProject(
|
|
id: seedProject.id,
|
|
title: generatedProject.title,
|
|
idea: normalizedIdea,
|
|
duration: generatedProject.duration,
|
|
conversationMode: discussionMode,
|
|
languages: generatedProject.languages,
|
|
dialects: generatedProject.dialects,
|
|
arabicPronunciationSettings: generatedProject.arabicPronunciationSettings,
|
|
genres: generatedProject.genres,
|
|
moods: generatedProject.moods,
|
|
emotionalArc: generatedProject.emotionalArc,
|
|
bpm: generatedProject.bpm,
|
|
key: generatedProject.key,
|
|
scale: generatedProject.scale,
|
|
maqam: generatedProject.maqam,
|
|
sections: generatedProject.sections,
|
|
instruments: generatedProject.instruments,
|
|
vocalists: generatedProject.vocalists,
|
|
lyrics: generatedProject.lyrics,
|
|
productionDirections: generatedProject.productionDirections,
|
|
sunoOutput: generatedProject.sunoOutput,
|
|
versions: seedProject.versions,
|
|
createdAt: seedProject.createdAt,
|
|
updatedAt: generatedAt
|
|
)
|
|
}
|
|
}
|
|
|
|
public enum SongProjectGenerationDirectorError: Error, Equatable, Sendable {
|
|
case emptyIdea
|
|
}
|