feat(v2): complete n8n workflow template (36 nodes) covering full V2 pipeline + shadow branch
- 14 new Code node drivers implementing the pipeline stages: normalize
candidates, build context SQL, merge context + hash, decide AI need,
prepare AI prompt, validate AI response, AI-disabled fallback, reuse
cached classification, compute score, select top 10, prepare memory/
envio records, build message/digest, compare legacy x V2, debug panel.
- Consolidated the two context queries (followups/tasks) into a single
UNION ALL query tagged by tipo, and added a "Merge - Unir Shadow"
synchronization node before comparing legacy x V2: n8n does not
guarantee relative execution order between sibling branches that only
share an early common ancestor, so cross-branch data was only safe to
read by name once a real Merge node forced both to complete first.
- Reordered score calculation before the memory upsert (score is needed
in the persisted row) and turned both Data Table writes into
side-effect branches that fan out from the main path instead of
gating it, so the Data Table node's own output shape never needs to
carry the full pipeline state forward.
- Fixed a duplicate-identifier bug in the build script: score.js's
require('./cooldown') survived inlining and collided with
cooldown.js's own computeCooldown function in the same Code node
scope. build-workflow.js now strips inter-module requires when
inlining, and a new test syntax-checks every generated jsCode string
plus asserts no leftover require() calls.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
ec596a29a8
commit
a99a25cb1a
@@ -5,8 +5,13 @@ const ROOT = path.join(__dirname, '..');
|
||||
|
||||
function readLogicSource(fileName) {
|
||||
const raw = fs.readFileSync(path.join(ROOT, 'logic', fileName), 'utf8');
|
||||
// Remove a linha final "module.exports = {...}" -- as funcoes ficam soltas no escopo do node.
|
||||
return raw.replace(/module\.exports\s*=\s*\{[\s\S]*?\};?\s*$/m, '').trim();
|
||||
return raw
|
||||
// Remove a linha final "module.exports = {...}" -- as funcoes ficam soltas no escopo do node.
|
||||
.replace(/module\.exports\s*=\s*\{[\s\S]*?\};?\s*$/m, '')
|
||||
// Remove requires de outros modulos de logic/*.js -- quando inlined, a funcao requerida
|
||||
// ja esta no mesmo escopo (senao vira identificador duplicado: function X + const {X} = require(...)).
|
||||
.replace(/^const\s*\{[^}]*\}\s*=\s*require\(['"]\.\/[a-zA-Z0-9_-]+['"]\);?\s*$/gm, '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function inlinedLogic(fileNames) {
|
||||
@@ -29,8 +34,7 @@ function buildDriverCode(driverRelativePath) {
|
||||
|
||||
driver = driver.replace('/* __SQL_CANDIDATOS_V2__ */', sqlAsJsTemplateLiteral('sql/candidatos_v2_glpi.sql'));
|
||||
driver = driver.replace('/* __SQL_LEGADO_V1_VERBATIM__ */', sqlAsJsTemplateLiteral('sql/top_10_chamados_abertos_glpi.sql'));
|
||||
driver = driver.replace('/* __SQL_CONTEXTO_FOLLOWUPS__ */', sqlAsJsTemplateLiteral('sql/contexto_followups_glpi.sql'));
|
||||
driver = driver.replace('/* __SQL_CONTEXTO_TAREFAS__ */', sqlAsJsTemplateLiteral('sql/contexto_tarefas_glpi.sql'));
|
||||
driver = driver.replace('/* __SQL_CONTEXTO__ */', sqlAsJsTemplateLiteral('sql/contexto_glpi.sql'));
|
||||
|
||||
const logicBlock = requiredLogicFiles.length ? `${inlinedLogic(requiredLogicFiles)}\n\n` : '';
|
||||
return `${logicBlock}${driver.trim()}`;
|
||||
|
||||
@@ -26,3 +26,24 @@ test('nenhum node de Code sobra com marcador nao resolvido', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('todo jsCode gerado e sintaticamente valido (pega identificador duplicado, chaves erradas, etc.)', () => {
|
||||
const wf = buildWorkflow();
|
||||
for (const node of wf.nodes) {
|
||||
if (node.type === 'n8n-nodes-base.code') {
|
||||
assert.doesNotThrow(() => {
|
||||
// eslint-disable-next-line no-new-func
|
||||
new Function('$input', '$env', '$', node.parameters.jsCode);
|
||||
}, `node ${node.name} tem jsCode com erro de sintaxe`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('nenhum jsCode gerado contem "require(" sobrando (modulos devem estar totalmente inlined)', () => {
|
||||
const wf = buildWorkflow();
|
||||
for (const node of wf.nodes) {
|
||||
if (node.type === 'n8n-nodes-base.code') {
|
||||
assert.ok(!/\brequire\(/.test(node.parameters.jsCode), `node ${node.name} ainda tem require() nao resolvido`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user