@atomoz/workflows-nodes 0.1.22 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +243 -95
- package/dist/index.js +243 -95
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -993,39 +993,50 @@ var IaAgentNode = {
|
|
|
993
993
|
typeable: false,
|
|
994
994
|
handle: {
|
|
995
995
|
type: "output",
|
|
996
|
-
label: "
|
|
996
|
+
label: "Response",
|
|
997
997
|
name: "response",
|
|
998
998
|
fieldType: "string"
|
|
999
999
|
}
|
|
1000
1000
|
},
|
|
1001
1001
|
{
|
|
1002
|
-
id: "
|
|
1003
|
-
label: "
|
|
1004
|
-
type: "
|
|
1002
|
+
id: "blocked",
|
|
1003
|
+
label: "Is Blocked",
|
|
1004
|
+
type: "boolean",
|
|
1005
1005
|
required: false,
|
|
1006
1006
|
typeable: false,
|
|
1007
1007
|
handle: {
|
|
1008
|
-
type: "
|
|
1009
|
-
label: "
|
|
1010
|
-
name: "
|
|
1011
|
-
fieldType: "
|
|
1012
|
-
acceptTypes: ["guardrail"],
|
|
1013
|
-
maxConnections: 10
|
|
1008
|
+
type: "output",
|
|
1009
|
+
label: "Blocked",
|
|
1010
|
+
name: "blocked",
|
|
1011
|
+
fieldType: "boolean"
|
|
1014
1012
|
}
|
|
1015
1013
|
},
|
|
1016
1014
|
{
|
|
1017
|
-
id: "
|
|
1018
|
-
label: "
|
|
1015
|
+
id: "violations",
|
|
1016
|
+
label: "Violations",
|
|
1017
|
+
type: "json",
|
|
1018
|
+
required: false,
|
|
1019
|
+
typeable: false,
|
|
1020
|
+
handle: {
|
|
1021
|
+
type: "output",
|
|
1022
|
+
label: "Violations",
|
|
1023
|
+
name: "violations",
|
|
1024
|
+
fieldType: "json"
|
|
1025
|
+
}
|
|
1026
|
+
},
|
|
1027
|
+
{
|
|
1028
|
+
id: "guardrails",
|
|
1029
|
+
label: "Guardrails (Todas as Fases)",
|
|
1019
1030
|
type: "guardrail",
|
|
1020
1031
|
required: false,
|
|
1021
1032
|
typeable: false,
|
|
1022
1033
|
handle: {
|
|
1023
1034
|
type: "input",
|
|
1024
|
-
label: "
|
|
1025
|
-
name: "
|
|
1035
|
+
label: "Guardrails",
|
|
1036
|
+
name: "guardrails",
|
|
1026
1037
|
fieldType: "guardrail",
|
|
1027
1038
|
acceptTypes: ["guardrail"],
|
|
1028
|
-
maxConnections:
|
|
1039
|
+
maxConnections: 20
|
|
1029
1040
|
}
|
|
1030
1041
|
},
|
|
1031
1042
|
{
|
|
@@ -1103,7 +1114,7 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
|
|
|
1103
1114
|
}
|
|
1104
1115
|
|
|
1105
1116
|
// src/utils/guardrail-executor.ts
|
|
1106
|
-
async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
1117
|
+
async function executeGuardrails(guardrails, content, mode = "fail-first", context = {}) {
|
|
1107
1118
|
const list = Array.isArray(guardrails) ? guardrails : [guardrails];
|
|
1108
1119
|
const result = {
|
|
1109
1120
|
passed: true,
|
|
@@ -1116,7 +1127,7 @@ async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
|
1116
1127
|
let guardPassed = true;
|
|
1117
1128
|
let violationMessage = "";
|
|
1118
1129
|
if (guard.type === "rule") {
|
|
1119
|
-
const { ruleType, config, action } = guard;
|
|
1130
|
+
const { ruleType, config, action, actionInstruction } = guard;
|
|
1120
1131
|
if (ruleType === "keywords" && config) {
|
|
1121
1132
|
const keywords = config.split(",").map((k) => k.trim().toLowerCase());
|
|
1122
1133
|
const lowerContent = content.toLowerCase();
|
|
@@ -1150,18 +1161,33 @@ async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
|
1150
1161
|
violationMessage = `Valor ${val} \xE9 menor que o m\xEDnimo permitido de ${min}.`;
|
|
1151
1162
|
}
|
|
1152
1163
|
}
|
|
1164
|
+
if (!guardPassed && actionInstruction) {
|
|
1165
|
+
violationMessage = actionInstruction;
|
|
1166
|
+
}
|
|
1153
1167
|
} else if (guard.type === "function") {
|
|
1154
|
-
const { nodeFunction, name } = guard;
|
|
1168
|
+
const { nodeFunction, name, actionInstruction } = guard;
|
|
1155
1169
|
if (typeof nodeFunction === "function") {
|
|
1156
1170
|
try {
|
|
1171
|
+
const executionContext = {
|
|
1172
|
+
input: content,
|
|
1173
|
+
...context
|
|
1174
|
+
};
|
|
1157
1175
|
const result2 = await nodeFunction({
|
|
1158
1176
|
input: content,
|
|
1159
|
-
fieldValues: { input: content },
|
|
1160
|
-
|
|
1177
|
+
fieldValues: { input: content, ...executionContext },
|
|
1178
|
+
// Envia contexto como fieldValues
|
|
1179
|
+
originalNodeData: guard.originalNodeData,
|
|
1180
|
+
$req: context?.$req,
|
|
1181
|
+
// Pass request context explicitly
|
|
1182
|
+
$vars: context?.$vars
|
|
1183
|
+
// Pass variables explicitly
|
|
1161
1184
|
});
|
|
1162
1185
|
if (result2 === false || typeof result2 === "object" && result2?.passed === false) {
|
|
1163
1186
|
guardPassed = false;
|
|
1164
1187
|
violationMessage = typeof result2?.message === "string" ? result2.message : `Fun\xE7\xE3o guardrail "${name}" reprovou o conte\xFAdo.`;
|
|
1188
|
+
if (actionInstruction) {
|
|
1189
|
+
violationMessage = actionInstruction;
|
|
1190
|
+
}
|
|
1165
1191
|
} else if (typeof result2 === "object" && result2?.modifiedContent) {
|
|
1166
1192
|
result2.modifiedContent = result2.modifiedContent;
|
|
1167
1193
|
}
|
|
@@ -1172,15 +1198,41 @@ async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
|
1172
1198
|
}
|
|
1173
1199
|
}
|
|
1174
1200
|
} else if (guard.type === "model") {
|
|
1175
|
-
const { model, evaluationPrompt, name } = guard;
|
|
1201
|
+
const { model, evaluationPrompt, name, actionInstruction } = guard;
|
|
1176
1202
|
if (model && typeof model.invoke === "function") {
|
|
1177
1203
|
try {
|
|
1178
|
-
|
|
1204
|
+
let prompt = evaluationPrompt;
|
|
1205
|
+
if (actionInstruction) {
|
|
1206
|
+
prompt += `
|
|
1207
|
+
|
|
1208
|
+
INSTRU\xC7\xC3O ADICIONAL DE A\xC7\xC3O:
|
|
1209
|
+
${actionInstruction}`;
|
|
1210
|
+
}
|
|
1211
|
+
if (prompt.includes("{{content}}")) {
|
|
1212
|
+
prompt = prompt.replace("{{content}}", content);
|
|
1213
|
+
} else {
|
|
1214
|
+
prompt = `${prompt}
|
|
1215
|
+
|
|
1216
|
+
CONTE\xDADO PARA AVALIAR:
|
|
1217
|
+
${content}`;
|
|
1218
|
+
}
|
|
1179
1219
|
const response = await model.invoke(prompt);
|
|
1180
1220
|
const resultText = typeof response.content === "string" ? response.content : String(response.content);
|
|
1181
|
-
if (
|
|
1182
|
-
|
|
1183
|
-
|
|
1221
|
+
if (guard.action === "fix") {
|
|
1222
|
+
if (resultText && resultText.trim() !== content.trim() && !resultText.includes("SAFE")) {
|
|
1223
|
+
result.modifiedContent = resultText;
|
|
1224
|
+
violationMessage = `Conte\xFAdo reescrito pelo guardrail "${name}".`;
|
|
1225
|
+
result.violations.push({
|
|
1226
|
+
name: guard.name,
|
|
1227
|
+
message: violationMessage,
|
|
1228
|
+
action: "fix"
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
} else {
|
|
1232
|
+
if (resultText.toUpperCase().includes("UNSAFE")) {
|
|
1233
|
+
guardPassed = false;
|
|
1234
|
+
violationMessage = actionInstruction || `Modelo de avalia\xE7\xE3o "${name}" detectou viola\xE7\xE3o de seguran\xE7a/pol\xEDtica.`;
|
|
1235
|
+
}
|
|
1184
1236
|
}
|
|
1185
1237
|
} catch (error) {
|
|
1186
1238
|
console.error(`Error executing model guardrail "${name}":`, error);
|
|
@@ -1196,7 +1248,7 @@ async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
|
1196
1248
|
});
|
|
1197
1249
|
if (guard.action === "block") {
|
|
1198
1250
|
result.blocked = true;
|
|
1199
|
-
result.message = violationMessage
|
|
1251
|
+
result.message = `GUARDRAILS ATIVADO: ${violationMessage}`;
|
|
1200
1252
|
if (mode === "fail-first") break;
|
|
1201
1253
|
}
|
|
1202
1254
|
}
|
|
@@ -1220,7 +1272,10 @@ GUARDRAIL [${guard.name}]: ${guard.prompt}`;
|
|
|
1220
1272
|
var IaAgentNodeFunction = async (inputs) => {
|
|
1221
1273
|
const { $field: _$field, $req: _$req, $inputs: _$inputs, $vars: _$vars } = inputs;
|
|
1222
1274
|
const fieldValues = inputs.fieldValues || {};
|
|
1223
|
-
const { model, tools, systemMessage, name, message,
|
|
1275
|
+
const { model, tools, systemMessage, name, message, guardrails, guardrailMode } = fieldValues;
|
|
1276
|
+
const guardrailsList = Array.isArray(guardrails) ? guardrails : guardrails ? [guardrails] : [];
|
|
1277
|
+
const beforeGuardrails = guardrailsList.filter((g) => g?.phase === "before");
|
|
1278
|
+
const afterGuardrails = guardrailsList.filter((g) => !g?.phase || g?.phase === "after");
|
|
1224
1279
|
const authToken = inputs.authToken;
|
|
1225
1280
|
const stream = Boolean(inputs?.stream);
|
|
1226
1281
|
const emitter = inputs?.emitter;
|
|
@@ -1231,8 +1286,8 @@ var IaAgentNodeFunction = async (inputs) => {
|
|
|
1231
1286
|
throw new Error("Agent 'name' is required. Please provide a unique name for the agent in the node properties.");
|
|
1232
1287
|
}
|
|
1233
1288
|
const prepareGuardrails = async (list) => {
|
|
1234
|
-
const
|
|
1235
|
-
return await Promise.all(
|
|
1289
|
+
const guardrailsList2 = Array.isArray(list) ? list : [list];
|
|
1290
|
+
return await Promise.all(guardrailsList2.map(async (g) => {
|
|
1236
1291
|
if (g?.type === "model" && g.model && !g.model.invoke) {
|
|
1237
1292
|
if (!authToken) throw new Error("Auth token required for model guardrail");
|
|
1238
1293
|
const modelInstance = await createLLMFromModel(g.model, authToken, false);
|
|
@@ -1241,9 +1296,12 @@ var IaAgentNodeFunction = async (inputs) => {
|
|
|
1241
1296
|
return g;
|
|
1242
1297
|
}));
|
|
1243
1298
|
};
|
|
1299
|
+
let isBlocked = false;
|
|
1300
|
+
let guardrailViolations = [];
|
|
1244
1301
|
if (message && beforeGuardrails) {
|
|
1245
1302
|
const preparedBefore = await prepareGuardrails(beforeGuardrails);
|
|
1246
|
-
const
|
|
1303
|
+
const context = { ...inputs, input: message };
|
|
1304
|
+
const beforeResults = await executeGuardrails(preparedBefore, message, guardrailMode, context);
|
|
1247
1305
|
if (beforeResults.blocked) {
|
|
1248
1306
|
console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Blocked by before-agent guardrail:`, beforeResults.message);
|
|
1249
1307
|
if (stream && emitter?.emitDelta) {
|
|
@@ -1256,9 +1314,9 @@ var IaAgentNodeFunction = async (inputs) => {
|
|
|
1256
1314
|
}
|
|
1257
1315
|
return {
|
|
1258
1316
|
agent: null,
|
|
1259
|
-
output: beforeResults.message || "Requisi\xE7\xE3o bloqueada por pol\xEDtica de seguran\xE7a.",
|
|
1317
|
+
output: beforeResults.message || "GUARDRAILS ATIVADO: Requisi\xE7\xE3o bloqueada por pol\xEDtica de seguran\xE7a.",
|
|
1260
1318
|
blocked: true,
|
|
1261
|
-
violations: beforeResults.violations
|
|
1319
|
+
violations: beforeResults.violations || []
|
|
1262
1320
|
};
|
|
1263
1321
|
}
|
|
1264
1322
|
}
|
|
@@ -1278,17 +1336,46 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1278
1336
|
finalSystemMessageContent = applyPromptGuardrails(finalSystemMessageContent, afterGuardrails);
|
|
1279
1337
|
}
|
|
1280
1338
|
const finalSystemMessage = new import_messages.SystemMessage(finalSystemMessageContent);
|
|
1339
|
+
const defaultModel = {
|
|
1340
|
+
model: "gemini-flash-latest",
|
|
1341
|
+
integrationId: "default-gemini"
|
|
1342
|
+
};
|
|
1343
|
+
const finalModel = model?.integrationId || typeof model?.bindTools === "function" ? model : defaultModel;
|
|
1344
|
+
console.log(`\u{1F916} IaAgentNode "${name}": Using model:`, finalModel === defaultModel ? "DEFAULT (Gemini)" : finalModel?.model || "instance");
|
|
1345
|
+
const callbacks = stream && emitter ? [
|
|
1346
|
+
{
|
|
1347
|
+
handleLLMNewToken: (token) => {
|
|
1348
|
+
if (emitter?.emitDelta) {
|
|
1349
|
+
emitter.emitDelta({
|
|
1350
|
+
content: token,
|
|
1351
|
+
actor: name,
|
|
1352
|
+
isAgent: true,
|
|
1353
|
+
isTool: false
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
] : [];
|
|
1281
1359
|
let llmInstance;
|
|
1282
|
-
if (
|
|
1360
|
+
if (finalModel?.integrationId) {
|
|
1283
1361
|
if (!authToken) {
|
|
1284
1362
|
throw new Error("Auth token is required to instantiate LLM from integration");
|
|
1285
1363
|
}
|
|
1286
|
-
llmInstance = await createLLMFromModel(
|
|
1287
|
-
} else if (typeof
|
|
1288
|
-
llmInstance =
|
|
1364
|
+
llmInstance = await createLLMFromModel(finalModel, authToken, stream);
|
|
1365
|
+
} else if (typeof finalModel?.bindTools === "function") {
|
|
1366
|
+
llmInstance = finalModel;
|
|
1289
1367
|
} else {
|
|
1290
1368
|
throw new Error("Invalid model: must have integrationId or be a valid LLM instance with bindTools method");
|
|
1291
1369
|
}
|
|
1370
|
+
if (stream && callbacks.length > 0) {
|
|
1371
|
+
if (llmInstance.callbacks) {
|
|
1372
|
+
if (Array.isArray(llmInstance.callbacks)) {
|
|
1373
|
+
llmInstance.callbacks.push(...callbacks);
|
|
1374
|
+
}
|
|
1375
|
+
} else {
|
|
1376
|
+
llmInstance.callbacks = callbacks;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1292
1379
|
const agent = (0, import_prebuilt.createReactAgent)({
|
|
1293
1380
|
llm: llmInstance,
|
|
1294
1381
|
tools: toolsArray,
|
|
@@ -1305,14 +1392,17 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1305
1392
|
let output = "";
|
|
1306
1393
|
if (message) {
|
|
1307
1394
|
try {
|
|
1308
|
-
const invokeConfig =
|
|
1395
|
+
const invokeConfig = {
|
|
1396
|
+
...checkpointer ? { configurable: { thread_id: sessionId } } : {},
|
|
1397
|
+
callbacks
|
|
1398
|
+
// Also pass here as backup
|
|
1399
|
+
};
|
|
1309
1400
|
if (stream && emitter) {
|
|
1310
1401
|
const streamIterator = await agent.stream(
|
|
1311
1402
|
{ messages: [new import_messages.HumanMessage(message)] },
|
|
1312
1403
|
invokeConfig
|
|
1313
1404
|
);
|
|
1314
1405
|
let lastMessages = [];
|
|
1315
|
-
const sentContents = /* @__PURE__ */ new Set();
|
|
1316
1406
|
for await (const step of streamIterator) {
|
|
1317
1407
|
if (step && typeof step === "object") {
|
|
1318
1408
|
for (const [key, value] of Object.entries(step)) {
|
|
@@ -1320,21 +1410,6 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1320
1410
|
const messages = value.messages;
|
|
1321
1411
|
if (Array.isArray(messages)) {
|
|
1322
1412
|
lastMessages = messages;
|
|
1323
|
-
for (const msg of messages) {
|
|
1324
|
-
const content = msg?.content;
|
|
1325
|
-
const contentStr = typeof content === "string" ? content : Array.isArray(content) ? content.map((p) => p.type === "text" ? p.text : "").filter(Boolean).join("") : "";
|
|
1326
|
-
if (contentStr && !sentContents.has(contentStr)) {
|
|
1327
|
-
sentContents.add(contentStr);
|
|
1328
|
-
if (emitter?.emitDelta) {
|
|
1329
|
-
emitter.emitDelta({
|
|
1330
|
-
content: contentStr,
|
|
1331
|
-
actor: name,
|
|
1332
|
-
isAgent: true,
|
|
1333
|
-
isTool: false
|
|
1334
|
-
});
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
1413
|
}
|
|
1339
1414
|
}
|
|
1340
1415
|
}
|
|
@@ -1376,22 +1451,42 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1376
1451
|
}
|
|
1377
1452
|
if (output && afterGuardrails) {
|
|
1378
1453
|
const preparedAfter = await prepareGuardrails(afterGuardrails);
|
|
1379
|
-
const
|
|
1454
|
+
const context = { ...inputs, input: output };
|
|
1455
|
+
const afterResults = await executeGuardrails(preparedAfter, output, guardrailMode, context);
|
|
1380
1456
|
if (afterResults.blocked) {
|
|
1381
1457
|
console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Response blocked by after-agent guardrail:`, afterResults.message);
|
|
1458
|
+
isBlocked = true;
|
|
1459
|
+
guardrailViolations = afterResults.violations || [];
|
|
1382
1460
|
if (stream && emitter?.emitDelta) {
|
|
1383
1461
|
emitter.emitDelta({
|
|
1384
1462
|
content: `
|
|
1385
1463
|
|
|
1386
|
-
|
|
1464
|
+
${afterResults.message}`,
|
|
1387
1465
|
actor: name,
|
|
1388
1466
|
isAgent: true,
|
|
1389
1467
|
isError: true
|
|
1390
1468
|
});
|
|
1391
1469
|
}
|
|
1392
|
-
output = afterResults.message || "Resposta bloqueada por pol\xEDtica de seguran\xE7a.";
|
|
1393
|
-
} else
|
|
1394
|
-
|
|
1470
|
+
output = afterResults.message || "GUARDRAILS ATIVADO: Resposta bloqueada por pol\xEDtica de seguran\xE7a.";
|
|
1471
|
+
} else {
|
|
1472
|
+
if (afterResults.modifiedContent) {
|
|
1473
|
+
output = afterResults.modifiedContent;
|
|
1474
|
+
}
|
|
1475
|
+
const warnings = afterResults.violations.filter((v) => v.action === "warn");
|
|
1476
|
+
if (warnings.length > 0) {
|
|
1477
|
+
const warningText = warnings.map((w) => `
|
|
1478
|
+
|
|
1479
|
+
\u26A0\uFE0F GUARDRAILS ATIVADO: ${w.message}`).join("");
|
|
1480
|
+
output += warningText;
|
|
1481
|
+
if (stream && emitter?.emitDelta) {
|
|
1482
|
+
emitter.emitDelta({
|
|
1483
|
+
content: warningText,
|
|
1484
|
+
actor: name,
|
|
1485
|
+
isAgent: true,
|
|
1486
|
+
isError: false
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1395
1490
|
}
|
|
1396
1491
|
}
|
|
1397
1492
|
} catch (error) {
|
|
@@ -1401,7 +1496,11 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1401
1496
|
}
|
|
1402
1497
|
return {
|
|
1403
1498
|
agent,
|
|
1404
|
-
|
|
1499
|
+
response: output || "",
|
|
1500
|
+
output: output || "",
|
|
1501
|
+
// Keep both for safety
|
|
1502
|
+
blocked: isBlocked,
|
|
1503
|
+
violations: guardrailViolations
|
|
1405
1504
|
};
|
|
1406
1505
|
};
|
|
1407
1506
|
|
|
@@ -2399,12 +2498,14 @@ var PromptGuardrailNode = {
|
|
|
2399
2498
|
|
|
2400
2499
|
// src/nodes/guardrails/prompt/function.ts
|
|
2401
2500
|
var PromptGuardrailNodeFunction = async (inputs) => {
|
|
2402
|
-
const
|
|
2403
|
-
const { name, prompt } =
|
|
2501
|
+
const values = inputs.fieldValues || inputs;
|
|
2502
|
+
const { name, prompt } = values;
|
|
2404
2503
|
return {
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2504
|
+
guardrail: {
|
|
2505
|
+
type: "prompt",
|
|
2506
|
+
name: name || "Prompt Guardrail",
|
|
2507
|
+
prompt: prompt || ""
|
|
2508
|
+
}
|
|
2408
2509
|
};
|
|
2409
2510
|
};
|
|
2410
2511
|
|
|
@@ -2415,7 +2516,8 @@ var RuleGuardrailNodeSchema = import_zod14.z.object({
|
|
|
2415
2516
|
phase: import_zod14.z.enum(["before", "after"]).describe("Fase de execu\xE7\xE3o"),
|
|
2416
2517
|
ruleType: import_zod14.z.enum(["maxValue", "minValue", "regex", "keywords", "pii"]).describe("Tipo de regra"),
|
|
2417
2518
|
config: import_zod14.z.any().describe("Configura\xE7\xE3o da regra"),
|
|
2418
|
-
action: import_zod14.z.enum(["block", "warn", "modify"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o")
|
|
2519
|
+
action: import_zod14.z.enum(["block", "warn", "modify"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o"),
|
|
2520
|
+
actionInstruction: import_zod14.z.string().optional().describe("Instru\xE7\xE3o adicional para a a\xE7\xE3o")
|
|
2419
2521
|
});
|
|
2420
2522
|
var RuleGuardrailNode = {
|
|
2421
2523
|
label: "Rule Guardrail",
|
|
@@ -2479,6 +2581,13 @@ var RuleGuardrailNode = {
|
|
|
2479
2581
|
{ label: "Modificar/Ocultar", value: "modify" }
|
|
2480
2582
|
]
|
|
2481
2583
|
},
|
|
2584
|
+
{
|
|
2585
|
+
id: "actionInstruction",
|
|
2586
|
+
label: "Instru\xE7\xE3o da A\xE7\xE3o",
|
|
2587
|
+
type: "textarea",
|
|
2588
|
+
required: false,
|
|
2589
|
+
placeholder: "Ex: Bloquear: 'N\xE3o posso falar sobre isso'. Warn: 'Aten\xE7\xE3o...'"
|
|
2590
|
+
},
|
|
2482
2591
|
{
|
|
2483
2592
|
id: "guardrail",
|
|
2484
2593
|
label: "Guardrail",
|
|
@@ -2497,15 +2606,21 @@ var RuleGuardrailNode = {
|
|
|
2497
2606
|
|
|
2498
2607
|
// src/nodes/guardrails/rule/function.ts
|
|
2499
2608
|
var RuleGuardrailNodeFunction = async (inputs) => {
|
|
2500
|
-
const
|
|
2501
|
-
const {
|
|
2609
|
+
const values = inputs.fieldValues || inputs;
|
|
2610
|
+
const {
|
|
2611
|
+
name,
|
|
2612
|
+
ruleType,
|
|
2613
|
+
config,
|
|
2614
|
+
action
|
|
2615
|
+
} = values;
|
|
2502
2616
|
return {
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2617
|
+
guardrail: {
|
|
2618
|
+
type: "rule",
|
|
2619
|
+
name: name || "Rule Guardrail",
|
|
2620
|
+
ruleType: ruleType || "keywords",
|
|
2621
|
+
config: config || "",
|
|
2622
|
+
action: action || "block"
|
|
2623
|
+
}
|
|
2509
2624
|
};
|
|
2510
2625
|
};
|
|
2511
2626
|
|
|
@@ -2515,7 +2630,8 @@ var FunctionGuardrailNodeSchema = import_zod15.z.object({
|
|
|
2515
2630
|
name: import_zod15.z.string().describe("Nome do guardrail"),
|
|
2516
2631
|
phase: import_zod15.z.enum(["before", "after"]).describe("Fase de execu\xE7\xE3o"),
|
|
2517
2632
|
description: import_zod15.z.string().optional().describe("Descri\xE7\xE3o do que a fun\xE7\xE3o valida"),
|
|
2518
|
-
action: import_zod15.z.enum(["block", "warn", "modify", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o")
|
|
2633
|
+
action: import_zod15.z.enum(["block", "warn", "modify", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o"),
|
|
2634
|
+
actionInstruction: import_zod15.z.string().optional().describe("Instru\xE7\xE3o adicional para a a\xE7\xE3o")
|
|
2519
2635
|
});
|
|
2520
2636
|
var FunctionGuardrailNode = {
|
|
2521
2637
|
label: "Function Guardrail",
|
|
@@ -2582,6 +2698,13 @@ var FunctionGuardrailNode = {
|
|
|
2582
2698
|
{ label: "Escalar (Human-in-the-loop)", value: "escalate" }
|
|
2583
2699
|
]
|
|
2584
2700
|
},
|
|
2701
|
+
{
|
|
2702
|
+
id: "actionInstruction",
|
|
2703
|
+
label: "Instru\xE7\xE3o da A\xE7\xE3o",
|
|
2704
|
+
type: "textarea",
|
|
2705
|
+
required: false,
|
|
2706
|
+
placeholder: "Ex: Instru\xE7\xE3o para a modifica\xE7\xE3o ou mensagem de bloqueio"
|
|
2707
|
+
},
|
|
2585
2708
|
{
|
|
2586
2709
|
id: "guardrail",
|
|
2587
2710
|
label: "Guardrail",
|
|
@@ -2600,26 +2723,24 @@ var FunctionGuardrailNode = {
|
|
|
2600
2723
|
|
|
2601
2724
|
// src/nodes/guardrails/function/function.ts
|
|
2602
2725
|
var FunctionGuardrailNodeFunction = async (inputs) => {
|
|
2603
|
-
const
|
|
2726
|
+
const values = inputs.fieldValues || inputs;
|
|
2604
2727
|
const {
|
|
2605
2728
|
name,
|
|
2606
|
-
phase,
|
|
2607
2729
|
description,
|
|
2608
2730
|
action,
|
|
2609
2731
|
nodeFunction,
|
|
2610
|
-
nodeType,
|
|
2611
2732
|
originalNodeData
|
|
2612
|
-
} =
|
|
2733
|
+
} = values;
|
|
2613
2734
|
return {
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2735
|
+
guardrail: {
|
|
2736
|
+
type: "function",
|
|
2737
|
+
name: name || "Function Guardrail",
|
|
2738
|
+
description,
|
|
2739
|
+
action: action || "block",
|
|
2740
|
+
// Injected by executeWorkflow.ts
|
|
2741
|
+
nodeFunction,
|
|
2742
|
+
originalNodeData
|
|
2743
|
+
}
|
|
2623
2744
|
};
|
|
2624
2745
|
};
|
|
2625
2746
|
|
|
@@ -2629,7 +2750,9 @@ var ModelGuardrailNodeSchema = import_zod16.z.object({
|
|
|
2629
2750
|
name: import_zod16.z.string().describe("Nome do guardrail"),
|
|
2630
2751
|
evaluationPrompt: import_zod16.z.string().describe("Prompt de avalia\xE7\xE3o"),
|
|
2631
2752
|
model: import_zod16.z.any().describe("Modelo LLM para avalia\xE7\xE3o"),
|
|
2632
|
-
action: import_zod16.z.enum(["block", "warn", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o")
|
|
2753
|
+
action: import_zod16.z.enum(["block", "warn", "fix", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o"),
|
|
2754
|
+
phase: import_zod16.z.enum(["before", "after"]).describe("Fase de execu\xE7\xE3o"),
|
|
2755
|
+
actionInstruction: import_zod16.z.string().optional().describe("Instru\xE7\xE3o adicional para a a\xE7\xE3o")
|
|
2633
2756
|
});
|
|
2634
2757
|
var ModelGuardrailNode = {
|
|
2635
2758
|
label: "Model Guardrail",
|
|
@@ -2680,10 +2803,29 @@ var ModelGuardrailNode = {
|
|
|
2680
2803
|
defaultValue: "block",
|
|
2681
2804
|
options: [
|
|
2682
2805
|
{ label: "Bloquear Resposta", value: "block" },
|
|
2806
|
+
{ label: "Corrigir/Reescrever (Fix)", value: "fix" },
|
|
2683
2807
|
{ label: "Avisar (Warning)", value: "warn" },
|
|
2684
2808
|
{ label: "Escalar (Human-in-the-loop)", value: "escalate" }
|
|
2685
2809
|
]
|
|
2686
2810
|
},
|
|
2811
|
+
{
|
|
2812
|
+
id: "actionInstruction",
|
|
2813
|
+
label: "Instru\xE7\xE3o da A\xE7\xE3o",
|
|
2814
|
+
type: "textarea",
|
|
2815
|
+
required: false,
|
|
2816
|
+
placeholder: "Ex: Se bloquear, diga 'Pol\xEDtica violada'. Se corrigir, 'Reescreva com...'"
|
|
2817
|
+
},
|
|
2818
|
+
{
|
|
2819
|
+
id: "phase",
|
|
2820
|
+
label: "Fase de Execu\xE7\xE3o",
|
|
2821
|
+
type: "select",
|
|
2822
|
+
required: true,
|
|
2823
|
+
defaultValue: "after",
|
|
2824
|
+
options: [
|
|
2825
|
+
{ label: "Antes do Agente (Input)", value: "before" },
|
|
2826
|
+
{ label: "Depois do Agente (Output)", value: "after" }
|
|
2827
|
+
]
|
|
2828
|
+
},
|
|
2687
2829
|
{
|
|
2688
2830
|
id: "guardrail",
|
|
2689
2831
|
label: "Guardrail",
|
|
@@ -2702,19 +2844,25 @@ var ModelGuardrailNode = {
|
|
|
2702
2844
|
|
|
2703
2845
|
// src/nodes/guardrails/model/function.ts
|
|
2704
2846
|
var ModelGuardrailNodeFunction = async (inputs) => {
|
|
2705
|
-
const
|
|
2847
|
+
const values = inputs.fieldValues || inputs;
|
|
2706
2848
|
const {
|
|
2707
2849
|
name,
|
|
2708
2850
|
evaluationPrompt,
|
|
2709
2851
|
model,
|
|
2710
2852
|
action
|
|
2711
|
-
} =
|
|
2853
|
+
} = values;
|
|
2854
|
+
const defaultModel = {
|
|
2855
|
+
model: "gemini-flash-latest",
|
|
2856
|
+
integrationId: "default-gemini"
|
|
2857
|
+
};
|
|
2712
2858
|
return {
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2859
|
+
guardrail: {
|
|
2860
|
+
type: "model",
|
|
2861
|
+
name: name || "Model Guardrail",
|
|
2862
|
+
evaluationPrompt: evaluationPrompt || "Analise se o conte\xFAdo a seguir \xE9 apropriado, seguro e segue as pol\xEDticas de \xE9tica. Responda 'SAFE' se estiver tudo bem ou 'UNSAFE' caso contr\xE1rio.\n\nCONTE\xDADO:\n{{content}}",
|
|
2863
|
+
model: model || defaultModel,
|
|
2864
|
+
action: action || "block"
|
|
2865
|
+
}
|
|
2718
2866
|
};
|
|
2719
2867
|
};
|
|
2720
2868
|
|
package/dist/index.js
CHANGED
|
@@ -913,39 +913,50 @@ var IaAgentNode = {
|
|
|
913
913
|
typeable: false,
|
|
914
914
|
handle: {
|
|
915
915
|
type: "output",
|
|
916
|
-
label: "
|
|
916
|
+
label: "Response",
|
|
917
917
|
name: "response",
|
|
918
918
|
fieldType: "string"
|
|
919
919
|
}
|
|
920
920
|
},
|
|
921
921
|
{
|
|
922
|
-
id: "
|
|
923
|
-
label: "
|
|
924
|
-
type: "
|
|
922
|
+
id: "blocked",
|
|
923
|
+
label: "Is Blocked",
|
|
924
|
+
type: "boolean",
|
|
925
925
|
required: false,
|
|
926
926
|
typeable: false,
|
|
927
927
|
handle: {
|
|
928
|
-
type: "
|
|
929
|
-
label: "
|
|
930
|
-
name: "
|
|
931
|
-
fieldType: "
|
|
932
|
-
acceptTypes: ["guardrail"],
|
|
933
|
-
maxConnections: 10
|
|
928
|
+
type: "output",
|
|
929
|
+
label: "Blocked",
|
|
930
|
+
name: "blocked",
|
|
931
|
+
fieldType: "boolean"
|
|
934
932
|
}
|
|
935
933
|
},
|
|
936
934
|
{
|
|
937
|
-
id: "
|
|
938
|
-
label: "
|
|
935
|
+
id: "violations",
|
|
936
|
+
label: "Violations",
|
|
937
|
+
type: "json",
|
|
938
|
+
required: false,
|
|
939
|
+
typeable: false,
|
|
940
|
+
handle: {
|
|
941
|
+
type: "output",
|
|
942
|
+
label: "Violations",
|
|
943
|
+
name: "violations",
|
|
944
|
+
fieldType: "json"
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
id: "guardrails",
|
|
949
|
+
label: "Guardrails (Todas as Fases)",
|
|
939
950
|
type: "guardrail",
|
|
940
951
|
required: false,
|
|
941
952
|
typeable: false,
|
|
942
953
|
handle: {
|
|
943
954
|
type: "input",
|
|
944
|
-
label: "
|
|
945
|
-
name: "
|
|
955
|
+
label: "Guardrails",
|
|
956
|
+
name: "guardrails",
|
|
946
957
|
fieldType: "guardrail",
|
|
947
958
|
acceptTypes: ["guardrail"],
|
|
948
|
-
maxConnections:
|
|
959
|
+
maxConnections: 20
|
|
949
960
|
}
|
|
950
961
|
},
|
|
951
962
|
{
|
|
@@ -1023,7 +1034,7 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
|
|
|
1023
1034
|
}
|
|
1024
1035
|
|
|
1025
1036
|
// src/utils/guardrail-executor.ts
|
|
1026
|
-
async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
1037
|
+
async function executeGuardrails(guardrails, content, mode = "fail-first", context = {}) {
|
|
1027
1038
|
const list = Array.isArray(guardrails) ? guardrails : [guardrails];
|
|
1028
1039
|
const result = {
|
|
1029
1040
|
passed: true,
|
|
@@ -1036,7 +1047,7 @@ async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
|
1036
1047
|
let guardPassed = true;
|
|
1037
1048
|
let violationMessage = "";
|
|
1038
1049
|
if (guard.type === "rule") {
|
|
1039
|
-
const { ruleType, config, action } = guard;
|
|
1050
|
+
const { ruleType, config, action, actionInstruction } = guard;
|
|
1040
1051
|
if (ruleType === "keywords" && config) {
|
|
1041
1052
|
const keywords = config.split(",").map((k) => k.trim().toLowerCase());
|
|
1042
1053
|
const lowerContent = content.toLowerCase();
|
|
@@ -1070,18 +1081,33 @@ async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
|
1070
1081
|
violationMessage = `Valor ${val} \xE9 menor que o m\xEDnimo permitido de ${min}.`;
|
|
1071
1082
|
}
|
|
1072
1083
|
}
|
|
1084
|
+
if (!guardPassed && actionInstruction) {
|
|
1085
|
+
violationMessage = actionInstruction;
|
|
1086
|
+
}
|
|
1073
1087
|
} else if (guard.type === "function") {
|
|
1074
|
-
const { nodeFunction, name } = guard;
|
|
1088
|
+
const { nodeFunction, name, actionInstruction } = guard;
|
|
1075
1089
|
if (typeof nodeFunction === "function") {
|
|
1076
1090
|
try {
|
|
1091
|
+
const executionContext = {
|
|
1092
|
+
input: content,
|
|
1093
|
+
...context
|
|
1094
|
+
};
|
|
1077
1095
|
const result2 = await nodeFunction({
|
|
1078
1096
|
input: content,
|
|
1079
|
-
fieldValues: { input: content },
|
|
1080
|
-
|
|
1097
|
+
fieldValues: { input: content, ...executionContext },
|
|
1098
|
+
// Envia contexto como fieldValues
|
|
1099
|
+
originalNodeData: guard.originalNodeData,
|
|
1100
|
+
$req: context?.$req,
|
|
1101
|
+
// Pass request context explicitly
|
|
1102
|
+
$vars: context?.$vars
|
|
1103
|
+
// Pass variables explicitly
|
|
1081
1104
|
});
|
|
1082
1105
|
if (result2 === false || typeof result2 === "object" && result2?.passed === false) {
|
|
1083
1106
|
guardPassed = false;
|
|
1084
1107
|
violationMessage = typeof result2?.message === "string" ? result2.message : `Fun\xE7\xE3o guardrail "${name}" reprovou o conte\xFAdo.`;
|
|
1108
|
+
if (actionInstruction) {
|
|
1109
|
+
violationMessage = actionInstruction;
|
|
1110
|
+
}
|
|
1085
1111
|
} else if (typeof result2 === "object" && result2?.modifiedContent) {
|
|
1086
1112
|
result2.modifiedContent = result2.modifiedContent;
|
|
1087
1113
|
}
|
|
@@ -1092,15 +1118,41 @@ async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
|
1092
1118
|
}
|
|
1093
1119
|
}
|
|
1094
1120
|
} else if (guard.type === "model") {
|
|
1095
|
-
const { model, evaluationPrompt, name } = guard;
|
|
1121
|
+
const { model, evaluationPrompt, name, actionInstruction } = guard;
|
|
1096
1122
|
if (model && typeof model.invoke === "function") {
|
|
1097
1123
|
try {
|
|
1098
|
-
|
|
1124
|
+
let prompt = evaluationPrompt;
|
|
1125
|
+
if (actionInstruction) {
|
|
1126
|
+
prompt += `
|
|
1127
|
+
|
|
1128
|
+
INSTRU\xC7\xC3O ADICIONAL DE A\xC7\xC3O:
|
|
1129
|
+
${actionInstruction}`;
|
|
1130
|
+
}
|
|
1131
|
+
if (prompt.includes("{{content}}")) {
|
|
1132
|
+
prompt = prompt.replace("{{content}}", content);
|
|
1133
|
+
} else {
|
|
1134
|
+
prompt = `${prompt}
|
|
1135
|
+
|
|
1136
|
+
CONTE\xDADO PARA AVALIAR:
|
|
1137
|
+
${content}`;
|
|
1138
|
+
}
|
|
1099
1139
|
const response = await model.invoke(prompt);
|
|
1100
1140
|
const resultText = typeof response.content === "string" ? response.content : String(response.content);
|
|
1101
|
-
if (
|
|
1102
|
-
|
|
1103
|
-
|
|
1141
|
+
if (guard.action === "fix") {
|
|
1142
|
+
if (resultText && resultText.trim() !== content.trim() && !resultText.includes("SAFE")) {
|
|
1143
|
+
result.modifiedContent = resultText;
|
|
1144
|
+
violationMessage = `Conte\xFAdo reescrito pelo guardrail "${name}".`;
|
|
1145
|
+
result.violations.push({
|
|
1146
|
+
name: guard.name,
|
|
1147
|
+
message: violationMessage,
|
|
1148
|
+
action: "fix"
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
} else {
|
|
1152
|
+
if (resultText.toUpperCase().includes("UNSAFE")) {
|
|
1153
|
+
guardPassed = false;
|
|
1154
|
+
violationMessage = actionInstruction || `Modelo de avalia\xE7\xE3o "${name}" detectou viola\xE7\xE3o de seguran\xE7a/pol\xEDtica.`;
|
|
1155
|
+
}
|
|
1104
1156
|
}
|
|
1105
1157
|
} catch (error) {
|
|
1106
1158
|
console.error(`Error executing model guardrail "${name}":`, error);
|
|
@@ -1116,7 +1168,7 @@ async function executeGuardrails(guardrails, content, mode = "fail-first") {
|
|
|
1116
1168
|
});
|
|
1117
1169
|
if (guard.action === "block") {
|
|
1118
1170
|
result.blocked = true;
|
|
1119
|
-
result.message = violationMessage
|
|
1171
|
+
result.message = `GUARDRAILS ATIVADO: ${violationMessage}`;
|
|
1120
1172
|
if (mode === "fail-first") break;
|
|
1121
1173
|
}
|
|
1122
1174
|
}
|
|
@@ -1140,7 +1192,10 @@ GUARDRAIL [${guard.name}]: ${guard.prompt}`;
|
|
|
1140
1192
|
var IaAgentNodeFunction = async (inputs) => {
|
|
1141
1193
|
const { $field: _$field, $req: _$req, $inputs: _$inputs, $vars: _$vars } = inputs;
|
|
1142
1194
|
const fieldValues = inputs.fieldValues || {};
|
|
1143
|
-
const { model, tools, systemMessage, name, message,
|
|
1195
|
+
const { model, tools, systemMessage, name, message, guardrails, guardrailMode } = fieldValues;
|
|
1196
|
+
const guardrailsList = Array.isArray(guardrails) ? guardrails : guardrails ? [guardrails] : [];
|
|
1197
|
+
const beforeGuardrails = guardrailsList.filter((g) => g?.phase === "before");
|
|
1198
|
+
const afterGuardrails = guardrailsList.filter((g) => !g?.phase || g?.phase === "after");
|
|
1144
1199
|
const authToken = inputs.authToken;
|
|
1145
1200
|
const stream = Boolean(inputs?.stream);
|
|
1146
1201
|
const emitter = inputs?.emitter;
|
|
@@ -1151,8 +1206,8 @@ var IaAgentNodeFunction = async (inputs) => {
|
|
|
1151
1206
|
throw new Error("Agent 'name' is required. Please provide a unique name for the agent in the node properties.");
|
|
1152
1207
|
}
|
|
1153
1208
|
const prepareGuardrails = async (list) => {
|
|
1154
|
-
const
|
|
1155
|
-
return await Promise.all(
|
|
1209
|
+
const guardrailsList2 = Array.isArray(list) ? list : [list];
|
|
1210
|
+
return await Promise.all(guardrailsList2.map(async (g) => {
|
|
1156
1211
|
if (g?.type === "model" && g.model && !g.model.invoke) {
|
|
1157
1212
|
if (!authToken) throw new Error("Auth token required for model guardrail");
|
|
1158
1213
|
const modelInstance = await createLLMFromModel(g.model, authToken, false);
|
|
@@ -1161,9 +1216,12 @@ var IaAgentNodeFunction = async (inputs) => {
|
|
|
1161
1216
|
return g;
|
|
1162
1217
|
}));
|
|
1163
1218
|
};
|
|
1219
|
+
let isBlocked = false;
|
|
1220
|
+
let guardrailViolations = [];
|
|
1164
1221
|
if (message && beforeGuardrails) {
|
|
1165
1222
|
const preparedBefore = await prepareGuardrails(beforeGuardrails);
|
|
1166
|
-
const
|
|
1223
|
+
const context = { ...inputs, input: message };
|
|
1224
|
+
const beforeResults = await executeGuardrails(preparedBefore, message, guardrailMode, context);
|
|
1167
1225
|
if (beforeResults.blocked) {
|
|
1168
1226
|
console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Blocked by before-agent guardrail:`, beforeResults.message);
|
|
1169
1227
|
if (stream && emitter?.emitDelta) {
|
|
@@ -1176,9 +1234,9 @@ var IaAgentNodeFunction = async (inputs) => {
|
|
|
1176
1234
|
}
|
|
1177
1235
|
return {
|
|
1178
1236
|
agent: null,
|
|
1179
|
-
output: beforeResults.message || "Requisi\xE7\xE3o bloqueada por pol\xEDtica de seguran\xE7a.",
|
|
1237
|
+
output: beforeResults.message || "GUARDRAILS ATIVADO: Requisi\xE7\xE3o bloqueada por pol\xEDtica de seguran\xE7a.",
|
|
1180
1238
|
blocked: true,
|
|
1181
|
-
violations: beforeResults.violations
|
|
1239
|
+
violations: beforeResults.violations || []
|
|
1182
1240
|
};
|
|
1183
1241
|
}
|
|
1184
1242
|
}
|
|
@@ -1198,17 +1256,46 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1198
1256
|
finalSystemMessageContent = applyPromptGuardrails(finalSystemMessageContent, afterGuardrails);
|
|
1199
1257
|
}
|
|
1200
1258
|
const finalSystemMessage = new SystemMessage(finalSystemMessageContent);
|
|
1259
|
+
const defaultModel = {
|
|
1260
|
+
model: "gemini-flash-latest",
|
|
1261
|
+
integrationId: "default-gemini"
|
|
1262
|
+
};
|
|
1263
|
+
const finalModel = model?.integrationId || typeof model?.bindTools === "function" ? model : defaultModel;
|
|
1264
|
+
console.log(`\u{1F916} IaAgentNode "${name}": Using model:`, finalModel === defaultModel ? "DEFAULT (Gemini)" : finalModel?.model || "instance");
|
|
1265
|
+
const callbacks = stream && emitter ? [
|
|
1266
|
+
{
|
|
1267
|
+
handleLLMNewToken: (token) => {
|
|
1268
|
+
if (emitter?.emitDelta) {
|
|
1269
|
+
emitter.emitDelta({
|
|
1270
|
+
content: token,
|
|
1271
|
+
actor: name,
|
|
1272
|
+
isAgent: true,
|
|
1273
|
+
isTool: false
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
] : [];
|
|
1201
1279
|
let llmInstance;
|
|
1202
|
-
if (
|
|
1280
|
+
if (finalModel?.integrationId) {
|
|
1203
1281
|
if (!authToken) {
|
|
1204
1282
|
throw new Error("Auth token is required to instantiate LLM from integration");
|
|
1205
1283
|
}
|
|
1206
|
-
llmInstance = await createLLMFromModel(
|
|
1207
|
-
} else if (typeof
|
|
1208
|
-
llmInstance =
|
|
1284
|
+
llmInstance = await createLLMFromModel(finalModel, authToken, stream);
|
|
1285
|
+
} else if (typeof finalModel?.bindTools === "function") {
|
|
1286
|
+
llmInstance = finalModel;
|
|
1209
1287
|
} else {
|
|
1210
1288
|
throw new Error("Invalid model: must have integrationId or be a valid LLM instance with bindTools method");
|
|
1211
1289
|
}
|
|
1290
|
+
if (stream && callbacks.length > 0) {
|
|
1291
|
+
if (llmInstance.callbacks) {
|
|
1292
|
+
if (Array.isArray(llmInstance.callbacks)) {
|
|
1293
|
+
llmInstance.callbacks.push(...callbacks);
|
|
1294
|
+
}
|
|
1295
|
+
} else {
|
|
1296
|
+
llmInstance.callbacks = callbacks;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1212
1299
|
const agent = createReactAgent({
|
|
1213
1300
|
llm: llmInstance,
|
|
1214
1301
|
tools: toolsArray,
|
|
@@ -1225,14 +1312,17 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1225
1312
|
let output = "";
|
|
1226
1313
|
if (message) {
|
|
1227
1314
|
try {
|
|
1228
|
-
const invokeConfig =
|
|
1315
|
+
const invokeConfig = {
|
|
1316
|
+
...checkpointer ? { configurable: { thread_id: sessionId } } : {},
|
|
1317
|
+
callbacks
|
|
1318
|
+
// Also pass here as backup
|
|
1319
|
+
};
|
|
1229
1320
|
if (stream && emitter) {
|
|
1230
1321
|
const streamIterator = await agent.stream(
|
|
1231
1322
|
{ messages: [new HumanMessage(message)] },
|
|
1232
1323
|
invokeConfig
|
|
1233
1324
|
);
|
|
1234
1325
|
let lastMessages = [];
|
|
1235
|
-
const sentContents = /* @__PURE__ */ new Set();
|
|
1236
1326
|
for await (const step of streamIterator) {
|
|
1237
1327
|
if (step && typeof step === "object") {
|
|
1238
1328
|
for (const [key, value] of Object.entries(step)) {
|
|
@@ -1240,21 +1330,6 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1240
1330
|
const messages = value.messages;
|
|
1241
1331
|
if (Array.isArray(messages)) {
|
|
1242
1332
|
lastMessages = messages;
|
|
1243
|
-
for (const msg of messages) {
|
|
1244
|
-
const content = msg?.content;
|
|
1245
|
-
const contentStr = typeof content === "string" ? content : Array.isArray(content) ? content.map((p) => p.type === "text" ? p.text : "").filter(Boolean).join("") : "";
|
|
1246
|
-
if (contentStr && !sentContents.has(contentStr)) {
|
|
1247
|
-
sentContents.add(contentStr);
|
|
1248
|
-
if (emitter?.emitDelta) {
|
|
1249
|
-
emitter.emitDelta({
|
|
1250
|
-
content: contentStr,
|
|
1251
|
-
actor: name,
|
|
1252
|
-
isAgent: true,
|
|
1253
|
-
isTool: false
|
|
1254
|
-
});
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
1333
|
}
|
|
1259
1334
|
}
|
|
1260
1335
|
}
|
|
@@ -1296,22 +1371,42 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1296
1371
|
}
|
|
1297
1372
|
if (output && afterGuardrails) {
|
|
1298
1373
|
const preparedAfter = await prepareGuardrails(afterGuardrails);
|
|
1299
|
-
const
|
|
1374
|
+
const context = { ...inputs, input: output };
|
|
1375
|
+
const afterResults = await executeGuardrails(preparedAfter, output, guardrailMode, context);
|
|
1300
1376
|
if (afterResults.blocked) {
|
|
1301
1377
|
console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Response blocked by after-agent guardrail:`, afterResults.message);
|
|
1378
|
+
isBlocked = true;
|
|
1379
|
+
guardrailViolations = afterResults.violations || [];
|
|
1302
1380
|
if (stream && emitter?.emitDelta) {
|
|
1303
1381
|
emitter.emitDelta({
|
|
1304
1382
|
content: `
|
|
1305
1383
|
|
|
1306
|
-
|
|
1384
|
+
${afterResults.message}`,
|
|
1307
1385
|
actor: name,
|
|
1308
1386
|
isAgent: true,
|
|
1309
1387
|
isError: true
|
|
1310
1388
|
});
|
|
1311
1389
|
}
|
|
1312
|
-
output = afterResults.message || "Resposta bloqueada por pol\xEDtica de seguran\xE7a.";
|
|
1313
|
-
} else
|
|
1314
|
-
|
|
1390
|
+
output = afterResults.message || "GUARDRAILS ATIVADO: Resposta bloqueada por pol\xEDtica de seguran\xE7a.";
|
|
1391
|
+
} else {
|
|
1392
|
+
if (afterResults.modifiedContent) {
|
|
1393
|
+
output = afterResults.modifiedContent;
|
|
1394
|
+
}
|
|
1395
|
+
const warnings = afterResults.violations.filter((v) => v.action === "warn");
|
|
1396
|
+
if (warnings.length > 0) {
|
|
1397
|
+
const warningText = warnings.map((w) => `
|
|
1398
|
+
|
|
1399
|
+
\u26A0\uFE0F GUARDRAILS ATIVADO: ${w.message}`).join("");
|
|
1400
|
+
output += warningText;
|
|
1401
|
+
if (stream && emitter?.emitDelta) {
|
|
1402
|
+
emitter.emitDelta({
|
|
1403
|
+
content: warningText,
|
|
1404
|
+
actor: name,
|
|
1405
|
+
isAgent: true,
|
|
1406
|
+
isError: false
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1315
1410
|
}
|
|
1316
1411
|
}
|
|
1317
1412
|
} catch (error) {
|
|
@@ -1321,7 +1416,11 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1321
1416
|
}
|
|
1322
1417
|
return {
|
|
1323
1418
|
agent,
|
|
1324
|
-
|
|
1419
|
+
response: output || "",
|
|
1420
|
+
output: output || "",
|
|
1421
|
+
// Keep both for safety
|
|
1422
|
+
blocked: isBlocked,
|
|
1423
|
+
violations: guardrailViolations
|
|
1325
1424
|
};
|
|
1326
1425
|
};
|
|
1327
1426
|
|
|
@@ -2319,12 +2418,14 @@ var PromptGuardrailNode = {
|
|
|
2319
2418
|
|
|
2320
2419
|
// src/nodes/guardrails/prompt/function.ts
|
|
2321
2420
|
var PromptGuardrailNodeFunction = async (inputs) => {
|
|
2322
|
-
const
|
|
2323
|
-
const { name, prompt } =
|
|
2421
|
+
const values = inputs.fieldValues || inputs;
|
|
2422
|
+
const { name, prompt } = values;
|
|
2324
2423
|
return {
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2424
|
+
guardrail: {
|
|
2425
|
+
type: "prompt",
|
|
2426
|
+
name: name || "Prompt Guardrail",
|
|
2427
|
+
prompt: prompt || ""
|
|
2428
|
+
}
|
|
2328
2429
|
};
|
|
2329
2430
|
};
|
|
2330
2431
|
|
|
@@ -2335,7 +2436,8 @@ var RuleGuardrailNodeSchema = z14.object({
|
|
|
2335
2436
|
phase: z14.enum(["before", "after"]).describe("Fase de execu\xE7\xE3o"),
|
|
2336
2437
|
ruleType: z14.enum(["maxValue", "minValue", "regex", "keywords", "pii"]).describe("Tipo de regra"),
|
|
2337
2438
|
config: z14.any().describe("Configura\xE7\xE3o da regra"),
|
|
2338
|
-
action: z14.enum(["block", "warn", "modify"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o")
|
|
2439
|
+
action: z14.enum(["block", "warn", "modify"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o"),
|
|
2440
|
+
actionInstruction: z14.string().optional().describe("Instru\xE7\xE3o adicional para a a\xE7\xE3o")
|
|
2339
2441
|
});
|
|
2340
2442
|
var RuleGuardrailNode = {
|
|
2341
2443
|
label: "Rule Guardrail",
|
|
@@ -2399,6 +2501,13 @@ var RuleGuardrailNode = {
|
|
|
2399
2501
|
{ label: "Modificar/Ocultar", value: "modify" }
|
|
2400
2502
|
]
|
|
2401
2503
|
},
|
|
2504
|
+
{
|
|
2505
|
+
id: "actionInstruction",
|
|
2506
|
+
label: "Instru\xE7\xE3o da A\xE7\xE3o",
|
|
2507
|
+
type: "textarea",
|
|
2508
|
+
required: false,
|
|
2509
|
+
placeholder: "Ex: Bloquear: 'N\xE3o posso falar sobre isso'. Warn: 'Aten\xE7\xE3o...'"
|
|
2510
|
+
},
|
|
2402
2511
|
{
|
|
2403
2512
|
id: "guardrail",
|
|
2404
2513
|
label: "Guardrail",
|
|
@@ -2417,15 +2526,21 @@ var RuleGuardrailNode = {
|
|
|
2417
2526
|
|
|
2418
2527
|
// src/nodes/guardrails/rule/function.ts
|
|
2419
2528
|
var RuleGuardrailNodeFunction = async (inputs) => {
|
|
2420
|
-
const
|
|
2421
|
-
const {
|
|
2529
|
+
const values = inputs.fieldValues || inputs;
|
|
2530
|
+
const {
|
|
2531
|
+
name,
|
|
2532
|
+
ruleType,
|
|
2533
|
+
config,
|
|
2534
|
+
action
|
|
2535
|
+
} = values;
|
|
2422
2536
|
return {
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2537
|
+
guardrail: {
|
|
2538
|
+
type: "rule",
|
|
2539
|
+
name: name || "Rule Guardrail",
|
|
2540
|
+
ruleType: ruleType || "keywords",
|
|
2541
|
+
config: config || "",
|
|
2542
|
+
action: action || "block"
|
|
2543
|
+
}
|
|
2429
2544
|
};
|
|
2430
2545
|
};
|
|
2431
2546
|
|
|
@@ -2435,7 +2550,8 @@ var FunctionGuardrailNodeSchema = z15.object({
|
|
|
2435
2550
|
name: z15.string().describe("Nome do guardrail"),
|
|
2436
2551
|
phase: z15.enum(["before", "after"]).describe("Fase de execu\xE7\xE3o"),
|
|
2437
2552
|
description: z15.string().optional().describe("Descri\xE7\xE3o do que a fun\xE7\xE3o valida"),
|
|
2438
|
-
action: z15.enum(["block", "warn", "modify", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o")
|
|
2553
|
+
action: z15.enum(["block", "warn", "modify", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o"),
|
|
2554
|
+
actionInstruction: z15.string().optional().describe("Instru\xE7\xE3o adicional para a a\xE7\xE3o")
|
|
2439
2555
|
});
|
|
2440
2556
|
var FunctionGuardrailNode = {
|
|
2441
2557
|
label: "Function Guardrail",
|
|
@@ -2502,6 +2618,13 @@ var FunctionGuardrailNode = {
|
|
|
2502
2618
|
{ label: "Escalar (Human-in-the-loop)", value: "escalate" }
|
|
2503
2619
|
]
|
|
2504
2620
|
},
|
|
2621
|
+
{
|
|
2622
|
+
id: "actionInstruction",
|
|
2623
|
+
label: "Instru\xE7\xE3o da A\xE7\xE3o",
|
|
2624
|
+
type: "textarea",
|
|
2625
|
+
required: false,
|
|
2626
|
+
placeholder: "Ex: Instru\xE7\xE3o para a modifica\xE7\xE3o ou mensagem de bloqueio"
|
|
2627
|
+
},
|
|
2505
2628
|
{
|
|
2506
2629
|
id: "guardrail",
|
|
2507
2630
|
label: "Guardrail",
|
|
@@ -2520,26 +2643,24 @@ var FunctionGuardrailNode = {
|
|
|
2520
2643
|
|
|
2521
2644
|
// src/nodes/guardrails/function/function.ts
|
|
2522
2645
|
var FunctionGuardrailNodeFunction = async (inputs) => {
|
|
2523
|
-
const
|
|
2646
|
+
const values = inputs.fieldValues || inputs;
|
|
2524
2647
|
const {
|
|
2525
2648
|
name,
|
|
2526
|
-
phase,
|
|
2527
2649
|
description,
|
|
2528
2650
|
action,
|
|
2529
2651
|
nodeFunction,
|
|
2530
|
-
nodeType,
|
|
2531
2652
|
originalNodeData
|
|
2532
|
-
} =
|
|
2653
|
+
} = values;
|
|
2533
2654
|
return {
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2655
|
+
guardrail: {
|
|
2656
|
+
type: "function",
|
|
2657
|
+
name: name || "Function Guardrail",
|
|
2658
|
+
description,
|
|
2659
|
+
action: action || "block",
|
|
2660
|
+
// Injected by executeWorkflow.ts
|
|
2661
|
+
nodeFunction,
|
|
2662
|
+
originalNodeData
|
|
2663
|
+
}
|
|
2543
2664
|
};
|
|
2544
2665
|
};
|
|
2545
2666
|
|
|
@@ -2549,7 +2670,9 @@ var ModelGuardrailNodeSchema = z16.object({
|
|
|
2549
2670
|
name: z16.string().describe("Nome do guardrail"),
|
|
2550
2671
|
evaluationPrompt: z16.string().describe("Prompt de avalia\xE7\xE3o"),
|
|
2551
2672
|
model: z16.any().describe("Modelo LLM para avalia\xE7\xE3o"),
|
|
2552
|
-
action: z16.enum(["block", "warn", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o")
|
|
2673
|
+
action: z16.enum(["block", "warn", "fix", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o"),
|
|
2674
|
+
phase: z16.enum(["before", "after"]).describe("Fase de execu\xE7\xE3o"),
|
|
2675
|
+
actionInstruction: z16.string().optional().describe("Instru\xE7\xE3o adicional para a a\xE7\xE3o")
|
|
2553
2676
|
});
|
|
2554
2677
|
var ModelGuardrailNode = {
|
|
2555
2678
|
label: "Model Guardrail",
|
|
@@ -2600,10 +2723,29 @@ var ModelGuardrailNode = {
|
|
|
2600
2723
|
defaultValue: "block",
|
|
2601
2724
|
options: [
|
|
2602
2725
|
{ label: "Bloquear Resposta", value: "block" },
|
|
2726
|
+
{ label: "Corrigir/Reescrever (Fix)", value: "fix" },
|
|
2603
2727
|
{ label: "Avisar (Warning)", value: "warn" },
|
|
2604
2728
|
{ label: "Escalar (Human-in-the-loop)", value: "escalate" }
|
|
2605
2729
|
]
|
|
2606
2730
|
},
|
|
2731
|
+
{
|
|
2732
|
+
id: "actionInstruction",
|
|
2733
|
+
label: "Instru\xE7\xE3o da A\xE7\xE3o",
|
|
2734
|
+
type: "textarea",
|
|
2735
|
+
required: false,
|
|
2736
|
+
placeholder: "Ex: Se bloquear, diga 'Pol\xEDtica violada'. Se corrigir, 'Reescreva com...'"
|
|
2737
|
+
},
|
|
2738
|
+
{
|
|
2739
|
+
id: "phase",
|
|
2740
|
+
label: "Fase de Execu\xE7\xE3o",
|
|
2741
|
+
type: "select",
|
|
2742
|
+
required: true,
|
|
2743
|
+
defaultValue: "after",
|
|
2744
|
+
options: [
|
|
2745
|
+
{ label: "Antes do Agente (Input)", value: "before" },
|
|
2746
|
+
{ label: "Depois do Agente (Output)", value: "after" }
|
|
2747
|
+
]
|
|
2748
|
+
},
|
|
2607
2749
|
{
|
|
2608
2750
|
id: "guardrail",
|
|
2609
2751
|
label: "Guardrail",
|
|
@@ -2622,19 +2764,25 @@ var ModelGuardrailNode = {
|
|
|
2622
2764
|
|
|
2623
2765
|
// src/nodes/guardrails/model/function.ts
|
|
2624
2766
|
var ModelGuardrailNodeFunction = async (inputs) => {
|
|
2625
|
-
const
|
|
2767
|
+
const values = inputs.fieldValues || inputs;
|
|
2626
2768
|
const {
|
|
2627
2769
|
name,
|
|
2628
2770
|
evaluationPrompt,
|
|
2629
2771
|
model,
|
|
2630
2772
|
action
|
|
2631
|
-
} =
|
|
2773
|
+
} = values;
|
|
2774
|
+
const defaultModel = {
|
|
2775
|
+
model: "gemini-flash-latest",
|
|
2776
|
+
integrationId: "default-gemini"
|
|
2777
|
+
};
|
|
2632
2778
|
return {
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2779
|
+
guardrail: {
|
|
2780
|
+
type: "model",
|
|
2781
|
+
name: name || "Model Guardrail",
|
|
2782
|
+
evaluationPrompt: evaluationPrompt || "Analise se o conte\xFAdo a seguir \xE9 apropriado, seguro e segue as pol\xEDticas de \xE9tica. Responda 'SAFE' se estiver tudo bem ou 'UNSAFE' caso contr\xE1rio.\n\nCONTE\xDADO:\n{{content}}",
|
|
2783
|
+
model: model || defaultModel,
|
|
2784
|
+
action: action || "block"
|
|
2785
|
+
}
|
|
2638
2786
|
};
|
|
2639
2787
|
};
|
|
2640
2788
|
|