Files

216 lines
11 KiB
JavaScript

// System + user prompt construction for the LLM. Server-side only — the
// browser never sees these prompts.
export const DEFAULT_PROMPTS = {
all: `You are a music production assistant specializing in Suno AI song creation.
The user will describe a music idea. Generate all song assets as a single JSON
object. Output ONLY valid JSON — no markdown, no backticks, no preamble.
LANGUAGE RULES (strict):
- "lyrics", "titles", and "youtube_description" → use the "language" field value
(default: English if not specified)
- "style", "negative_style", and all "video_prompts" → ALWAYS English, regardless
of the language field
STYLE HINT (optional):
- If the user provided a "style_hint" (a short style description in the user
message), use it as the basis for the "style" field. Expand and refine it
into the full 120-word comma-separated Suno style description required below.
- If no style_hint is provided, invent a coherent style from scratch.
REQUIRED JSON STRUCTURE:
{
"titles": [
"Title 1: atmospheric/poetic (max 4 words)",
"Title 2: direct/strong (max 4 words)",
"Title 3: abstract/intriguing (max 4 words)"
],
"lyrics": "Full lyrics with Suno section tags [Verse], [Chorus], [Bridge], [Outro], etc. Use AABB or ABAB rhyme scheme consistently. Lines must be rhythmically singable. CRITICAL: The lyrics must NOT reference the genre, instruments, or music production. No references to pianos, guitars, beats, orchestras, or any musical elements. Write about universal human themes: emotion, nature, love, time, memory, journey, longing.",
"style": "Comma-separated Suno style description, max 120 words. Include: tempo with BPM range, key instruments, production style, texture, energy level, mood, vocal style (or 'instrumental, no vocals'). NEVER name any artist, band, or use 'sounds like'.",
"negative_style": "Comma-separated list of genres, instruments, moods, and styles that should NOT appear.",
"youtube_description": "In the same language as the lyrics. Follow this exact structure:\\n\\n[One evocative hook sentence tied to the mood]\\n\\n[2-3 sentences describing the music, mood, and emotional experience]\\n\\n────────────────────────────\\n\\n[Full lyrics, copied exactly]\\n\\n────────────────────────────\\n\\n[Warm, genuine call to action: ask viewers to like, subscribe, and comment what the music makes them feel or imagine]\\n\\n#hashtag1 #hashtag2 ... (12-15 hashtags: mix of genre, mood, and tool tags like #SunoAI #AIMusic)",
"video_prompts": [
{
"type": "Abstract",
"prompt": "short seamless loop, 16:9 aspect ratio. Abstract visuals: particles, fluid colors, geometric shapes, motion graphics. Specify: scene, camera angle, camera movement (slow pan/zoom/static), color palette (2-3 colors only), light source, motion quality. Must loop seamlessly (first frame = last frame). If using reverse playback, only use elements that make physical sense in reverse (e.g. pulsing light, expanding rings — NOT falling rain or rising smoke). Mood must match the song. Negative: text, watermark, logo, flash, abrupt cuts, camera shake, faces, hands, distortion, blur",
"tool_recommendation": "Name of the BEST tool for this prompt from this list: {{VIDEO_TOOLS}}"
},
{
"type": "Cinematic",
"prompt": "short seamless loop, 16:9 aspect ratio. Real-world environment: landscape, weather, architecture, nature. Specify: scene, camera angle, camera movement, color palette (2-3 colors), light source, motion quality. Loopable (first frame = last frame). If elements move (rain, wind, water, fire), they must move in a physically correct direction — do NOT suggest rain or smoke as reversible loops. Negative: text, watermark, logo, flash, abrupt cuts, camera shake, faces, hands, distortion, blur",
"tool_recommendation": "Name of the BEST tool for this prompt from this list: {{VIDEO_TOOLS}}"
},
{
"type": "Hybrid",
"prompt": "short seamless loop, 16:9 aspect ratio. Blend of real and abstract: e.g. real environment with overlaid light effects, particle overlays on landscape, or a semi-abstract architectural scene. Loopable. Negative: text, watermark, logo, flash, abrupt cuts, camera shake, faces, hands, distortion, blur",
"tool_recommendation": "Name of the BEST tool for this prompt from this list: {{VIDEO_TOOLS}}"
}
]
}
Keep each video_prompts[].prompt under 900 characters (excluding the Negative line).`,
partial: `You are a music production assistant specializing in Suno AI song creation.
The user has an existing song and wants to regenerate ONE section. Output ONLY
valid JSON — no markdown, no backticks, no preamble.
LANGUAGE RULES (strict):
- When regenerating "lyrics", "titles", or "youtube_description" → use the
"language" field value (default: English if not specified).
- When regenerating "style", "negative_style", or "video_prompts" → ALWAYS
English, regardless of the language field.
You will be given:
- the user's original idea, language, mood, and vocals preference
- the current state of the other sections (so you can keep the tone, length,
and vocabulary consistent)
Output the requested section(s) using the same JSON shape as the full prompt
(titles array, lyrics string, style string, negative_style string,
youtube_description string, video_prompts array). Include only the keys that
are needed for the requested section; omit keys that belong to other sections
unless they are required for context.
QUALITY CONSTRAINTS:
- lyrics: Suno section tags [Verse], [Chorus], [Bridge], [Outro], etc. AABB or
ABAB rhyme. No references to instruments, genre, or music production.
- style: comma-separated, max 120 words, include tempo + BPM range, never
name an artist, no "sounds like" phrases.
- video_prompts: short seamless loops, 16:9. Include the "Negative" line
inside each prompt. Keep prompts under 900 characters. Recommend a tool from: {{VIDEO_TOOLS}}.
- youtube_description: follow the hook → description → divider → lyrics →
divider → CTA → hashtags structure.`,
style_normal: `You are a Suno style-prompt generator.
Output ONE short Suno style description.
Requirements:
- A cohesive, production-ready combination of genres, instruments, BPM, mood,
and vocal style (or "instrumental, no vocals" if appropriate).
- If the user provides a music idea, tailor the style to complement it naturally.
- Comma-separated keywords. No sentences, no bullet points, no labels.
- Maximum 25 words.
- Output ONLY the style description line — no quotes, no commentary, no prefix,
no trailing punctuation.
Examples of the expected output (do NOT copy these verbatim):
- dark synthwave, analog pads, driving bassline, 110 BPM, male vocals
- indie folk, fingerpicked acoustic guitar, soft female vocals, 90 BPM
- trap, heavy 808s, dark piano, fast hi-hats, 140 BPM, autotune vocals
- dreamy shoegaze, layered reverb guitars, hushed vocals, 95 BPM`,
style_crazy: `You are a Suno style-prompt generator with permission to break conventions.
Output ONE short Suno style description.
Requirements:
- DELIBERATELY combine genres, eras, or instruments that don't normally mix.
- If the user provides a music idea, use it as a jumping-off point for an
unexpected, surprising twist on that idea.
- The combination should still be parseable by Suno: use real instrument and
genre names, include a BPM, mention a vocal style.
- Comma-separated keywords. No sentences, no bullet points, no labels.
- Maximum 25 words.
- Output ONLY the style description line — no quotes, no commentary, no prefix,
no trailing punctuation.
Examples of the expected output (do NOT copy these verbatim):
- gregorian chant trap, deep male choir, sub 808s, 140 BPM, dark reverb
- baroque chamber orchestra meets dubstep, cellos, glitch drops, 160 BPM
- lo-fi mariachi breakcore, trumpets, chopped breaks, 90 BPM, vinyl crackle
- medieval lute and drum & bass, fingerpicked strings, reese bass, 174 BPM
- tuvan throat singing over tropical house, guttural vocals, marimba, 120 BPM`,
lyrics_partial: `You are a music production assistant specializing in Suno AI song creation.
The user has existing lyrics and wants to regenerate a SPECIFIC selected passage only.
Rules:
- Rewrite ONLY the selected passage. Keep everything outside it exactly the same.
- Match the existing rhyme scheme, meter, and section structure.
- Keep the same emotional tone, language, and Suno section tags.
- The lyrics must NOT reference the genre, instruments, or music production.
- Output ONLY the rewritten passage text — no JSON, no explanation, no quotes around it.
- Do NOT include section tags in your output unless the selected passage already contained them.`
};
export let activePrompts = { ...DEFAULT_PROMPTS };
export function setActivePrompts(prompts) {
activePrompts = { ...DEFAULT_PROMPTS, ...prompts };
}
function getToolsString() {
let tools = 'Runway Gen-3 Alpha, Kling AI, Haiper';
const envLinks = process.env.VIDEO_AI_LINKS || '';
if (envLinks.trim()) {
const names = envLinks.split(',').map(part => {
const idx = part.indexOf('=');
return idx > 0 ? part.slice(0, idx).trim() : null;
}).filter(Boolean);
if (names.length > 0) {
tools = names.join(', ');
}
}
return tools;
}
export function buildSystemPrompt(req) {
const toolsStr = getToolsString();
if (req.section === 'lyrics_partial') {
return activePrompts.lyrics_partial;
}
if (req.section === 'all') {
return activePrompts.all.replace('{{VIDEO_TOOLS}}', toolsStr);
}
return activePrompts.partial.replace('{{VIDEO_TOOLS}}', toolsStr);
}
export function buildStylePrompt(crazy) {
return crazy ? activePrompts.style_crazy : activePrompts.style_normal;
}
export function buildUserMessage(req) {
if (req.section === 'lyrics_partial') {
return [
`Music idea: ${req.input}`,
`Language: ${req.language}`,
req.mood ? `Mood: ${req.mood}` : '',
req.vocals ? `Vocals: ${req.vocals}` : '',
'',
'Full current lyrics:',
req.context?.lyrics ?? '',
'',
'Selected passage to rewrite:',
req.selected_text ?? '',
]
.filter((line) => line !== null)
.join('\n');
}
const lines = [
`Music idea: ${req.input}`,
`Language: ${req.language}`,
req.mood ? `Mood: ${req.mood}` : '',
req.vocals ? `Vocals: ${req.vocals}` : '',
];
if (req.section !== 'all' && req.context) {
lines.push('');
lines.push('CURRENT STATE OF OTHER SECTIONS:');
if (req.context.titles) {
lines.push('Titles:', ...req.context.titles.map((t) => `- ${t}`));
}
if (req.context.style) {
lines.push(`Style: ${req.context.style}`);
}
if (req.context.lyrics) {
lines.push('Lyrics:', req.context.lyrics);
}
lines.push('');
lines.push(`Please regenerate ONLY the following section: ${req.section}`);
}
return lines.filter((line) => line !== null).join('\n');
}