85 lines
2.8 KiB
Swift
85 lines
2.8 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
|
|
}
|
|
|
|
let update = SongProjectAIUpdateMerger().apply(
|
|
snapshot: SongProjectSnapshot(project: generatedProject),
|
|
changedScopes: SongProjectUpdateScope.allCases,
|
|
to: seedProject,
|
|
updatedAt: generatedAt
|
|
)
|
|
var project = update.project
|
|
project.title = generatedProject.title
|
|
project.idea = normalizedIdea
|
|
project.conversationMode = discussionMode
|
|
project.moods = generatedProject.moods
|
|
return project
|
|
}
|
|
}
|
|
|
|
public enum SongProjectGenerationDirectorError: Error, Equatable, Sendable {
|
|
case emptyIdea
|
|
}
|