feat: add chatbot memory and conversational replies
This commit is contained in:
@@ -365,7 +365,7 @@
|
||||
"type": "function",
|
||||
"z": "f68df1c4d2e4e1a9",
|
||||
"name": "Chatbot ALS",
|
||||
"func": "\nconst raw = typeof msg.payload === 'string' ? msg.payload : JSON.stringify(msg.payload || {});\nlet body = {};\ntry {\n body = typeof msg.payload === 'object' ? msg.payload : JSON.parse(raw);\n} catch (err) {\n body = {};\n}\nconst prompt = (body.prompt || body.message || msg.prompt || '').trim();\nif (!prompt) {\n msg.statusCode = 400;\n msg.payload = { error: 'Falta el prompt del chatbot.' };\n return msg;\n}\nconst generator = global.get('alsGenerator');\nif (!generator || typeof generator.generateFromPrompt !== 'function') {\n msg.statusCode = 500;\n msg.payload = { error: 'Generador no disponible.' };\n return msg;\n}\nconst info = generator.listLibrary ? generator.listLibrary() : [];\nconst sources = generator.listSources ? generator.listSources() : [];\nfunction shouldGenerate(text) {\n return /(genera(me)?|crea(me)?|haz(me)?|arma(me)?|produce(me)?).*(als|ableton|beat|proyecto)/i.test(text);\n}\nfunction shouldDescribeSources(text) {\n return /(source|sources|stems|loops|samples|archivos|carpeta)/i.test(text);\n}\nfunction describeSources(list) {\n if (!list || !list.length) {\n return 'Todav\u00eda no hay archivos en data/sources/. Copia stems o loops ah\u00ed para que puedan usarse como referencia.';\n }\n const preview = list.slice(0, 10).join(', ');\n return `Encontr\u00e9 ${list.length} archivos en data/sources/. Ejemplos: ${preview}${list.length > 10 ? ' ...' : ''}`;\n}\nfunction buildSummary(text, libraryList, sourceList) {\n if (!libraryList || !libraryList.length) {\n return 'A\u00fan no tengo proyectos cargados. Sube un .als primero y luego dime \"generame un als ...\" para empezar.';\n }\n const recent = libraryList.slice(-1)[0];\n const parts = [\n `Tengo ${libraryList.length} proyectos almacenados. El \u00faltimo fue \"${recent.projectName}\" a ${recent.liveSet?.tempo || 'N/D'} BPM.`,\n sourceList && sourceList.length ? `Hay ${sourceList.length} recursos en sources (ej: ${sourceList.slice(0,3).join(', ')})` : 'A\u00fan no hay archivos en data/sources/.',\n 'Cuando est\u00e9s listo dime algo como \"generame un als afrohouse 2025 con 124 bpm\" y preparo la sesi\u00f3n.'\n ];\n return parts.join(' ');\n}\nif (shouldDescribeSources(prompt)) {\n msg.statusCode = 200;\n msg.payload = { reply: describeSources(sources) };\n return msg;\n}\nif (!shouldGenerate(prompt)) {\n return (async () => {\n try {\n const reply = await (generator.chatReply ? generator.chatReply(prompt, info, sources) : buildSummary(prompt, info, sources));\n msg.statusCode = 200;\n msg.payload = { reply };\n return msg;\n } catch (err) {\n node.error(err.message, msg);\n msg.statusCode = 500;\n msg.payload = { error: err.message };\n return msg;\n }\n })();\n}\nreturn (async () => {\n try {\n const result = await generator.generateFromPrompt(prompt);\n msg.statusCode = 200;\n msg.payload = {\n prompt: prompt,\n projectName: result.plan.projectName,\n templateHash: result.plan.templateHash,\n outputPath: result.outputPath,\n registered: result.registered\n };\n return msg;\n } catch (err) {\n node.error(err.message, msg);\n msg.statusCode = 500;\n msg.payload = { error: err.message };\n return msg;\n }\n})();\n",
|
||||
"func": "\nconst raw = typeof msg.payload === 'string' ? msg.payload : JSON.stringify(msg.payload || {});\nlet body = {};\ntry {\n body = typeof msg.payload === 'object' ? msg.payload : JSON.parse(raw);\n} catch (err) {\n body = {};\n}\nconst prompt = (body.prompt || body.message || msg.prompt || '').trim();\nconst chatHistory = context.get('chatHistory') || [];\nfunction remember(role, text) {\n if (!text) { return; }\n chatHistory.push({ role, text });\n if (chatHistory.length > 40) {\n chatHistory.shift();\n }\n context.set('chatHistory', chatHistory);\n}\nif (!prompt) {\n msg.statusCode = 400;\n msg.payload = { error: 'Falta el prompt del chatbot.' };\n return msg;\n}\nconst generator = global.get('alsGenerator');\nif (!generator || typeof generator.generateFromPrompt !== 'function') {\n msg.statusCode = 500;\n msg.payload = { error: 'Generador no disponible.' };\n return msg;\n}\nremember('user', prompt);\nconst info = generator.listLibrary ? generator.listLibrary() : [];\nconst sources = generator.listSources ? generator.listSources() : [];\nfunction shouldGenerate(text) {\n return /(genera(me)?|crea(me)?|haz(me)?|arma(me)?|produce(me)?).*(als|ableton|beat|proyecto)/i.test(text);\n}\nfunction shouldDescribeSources(text) {\n return /(source|sources|stems|loops|samples|archivos|carpeta)/i.test(text);\n}\nfunction describeSources(list) {\n if (!list || !list.length) {\n return 'Todav\u00eda no hay archivos en data/sources/. Copia stems o loops ah\u00ed para que puedan usarse como referencia.';\n }\n const preview = list.slice(0, 10).join(', ');\n return `Encontr\u00e9 ${list.length} archivos en data/sources/. Ejemplos: ${preview}${list.length > 10 ? ' ...' : ''}`;\n}\nfunction buildSummary(text, libraryList, sourceList) {\n if (!libraryList || !libraryList.length) {\n return 'A\u00fan no tengo proyectos cargados. Sube un .als primero y luego dime \"generame un als ...\" para empezar.';\n }\n const recent = libraryList.slice(-1)[0];\n const parts = [\n `Tengo ${libraryList.length} proyectos almacenados. El \u00faltimo fue \"${recent.projectName}\" a ${recent.liveSet?.tempo || 'N/D'} BPM.`,\n sourceList && sourceList.length ? `Hay ${sourceList.length} recursos en sources (ej: ${sourceList.slice(0,3).join(', ')})` : 'A\u00fan no hay archivos en data/sources/.',\n 'Cuando est\u00e9s listo dime algo como \"generame un als afrohouse 2025 con 124 bpm\" y preparo la sesi\u00f3n.'\n ];\n return parts.join(' ');\n}\nif (shouldDescribeSources(prompt)) {\n const reply = describeSources(sources);\n remember('bot', reply);\n msg.statusCode = 200;\n msg.payload = { reply };\n return msg;\n}\nif (!shouldGenerate(prompt)) {\n return (async () => {\n try {\n const reply = await (generator.chatReply ? generator.chatReply(prompt, info, sources, chatHistory) : buildSummary(prompt, info, sources));\n remember('bot', reply);\n msg.statusCode = 200;\n msg.payload = { reply };\n return msg;\n } catch (err) {\n node.error(err.message, msg);\n msg.statusCode = 500;\n msg.payload = { error: err.message };\n return msg;\n }\n })();\n}\nreturn (async () => {\n try {\n const result = await generator.generateFromPrompt(prompt);\n const summary = `Listo, gener\u00e9 \"${result.plan.projectName}\" usando ${result.plan.templateHash}.`;\n remember('bot', summary);\n msg.statusCode = 200;\n msg.payload = {\n prompt: prompt,\n projectName: result.plan.projectName,\n templateHash: result.plan.templateHash,\n outputPath: result.outputPath,\n registered: result.registered\n };\n return msg;\n } catch (err) {\n node.error(err.message, msg);\n remember('bot', err.message);\n msg.statusCode = 500;\n msg.payload = { error: err.message };\n return msg;\n }\n})();\n",
|
||||
"outputs": 1,
|
||||
"noerr": 0,
|
||||
"initialize": "",
|
||||
|
||||
@@ -351,7 +351,7 @@ async function generateFromPrompt(prompt, options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
async function conversationalReply(prompt, library, sources) {
|
||||
async function conversationalReply(prompt, library, sources, history = []) {
|
||||
const context = {
|
||||
prompt,
|
||||
projects: library.slice(-5).map((entry) => ({
|
||||
@@ -361,22 +361,29 @@ async function conversationalReply(prompt, library, sources) {
|
||||
})),
|
||||
sources: sources.slice(0, 20)
|
||||
};
|
||||
const historySummary = (history || [])
|
||||
.slice(-10)
|
||||
.map((msg) => `${msg.role === 'user' ? 'Usuario' : 'Bot'}: ${msg.text}`)
|
||||
.join('\\n');
|
||||
|
||||
const text = await callAnthropic(
|
||||
'Actúa como un productor musical amigable que ayuda a planear nuevos proyectos de Ableton Live. Responde en español latino, máximo 3 frases, incluye sugerencias creativas y cuándo usar \"generame un als ...\".',
|
||||
`Usuario: ${prompt}\nContexto: ${JSON.stringify(context, null, 2)}`
|
||||
`Historial reciente:\n${historySummary || 'Sin historial'}\nUsuario: ${prompt}\nContexto: ${JSON.stringify(context, null, 2)}`
|
||||
);
|
||||
if (text) {
|
||||
return text.trim();
|
||||
}
|
||||
if (!library.length) {
|
||||
return '¡Hola! Aún no tengo proyectos para tomar como referencia. Sube un .als y cuéntame qué estilo buscas; luego pide "generame un als ..." y prepararé una sesión.';
|
||||
const vibe = prompt ? `Me encanta esa idea de ${prompt}. ` : '';
|
||||
return `${vibe}Aún no tengo proyectos para tomar como referencia. Sube un .als y luego dime "generame un als ..." y prepararé una sesión nueva.`;
|
||||
}
|
||||
const recent = library.slice(-1)[0];
|
||||
const srcText =
|
||||
sources && sources.length
|
||||
? `También veo ${sources.length} archivos en data/sources/ (ej: ${sources.slice(0, 3).join(', ')}). `
|
||||
: '';
|
||||
return `Hola, tengo ${library.length} proyectos guardados y el más reciente es "${recent.projectName}" (${recent.liveSet?.tempo || 'N/D'} BPM). ${srcText}Cuéntame la vibra y cuando digas "generame un als ..." lo construyo.`;
|
||||
const vibe = prompt ? `Esa vibra de "${prompt}" suena genial. ` : '';
|
||||
return `${vibe}Tengo ${library.length} proyectos guardados y el más reciente es "${recent.projectName}" (${recent.liveSet?.tempo || 'N/D'} BPM). ${srcText}Cuéntame más y cuando digas "generame un als ..." lo construyo.`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
Reference in New Issue
Block a user