@atomoz/workflows-nodes 0.1.22 → 0.1.24
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 +302 -97
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +302 -97
- 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
|
-
|
|
1013
|
-
|
|
1008
|
+
type: "output",
|
|
1009
|
+
label: "Blocked",
|
|
1010
|
+
name: "blocked",
|
|
1011
|
+
fieldType: "boolean"
|
|
1012
|
+
}
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
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"
|
|
1014
1025
|
}
|
|
1015
1026
|
},
|
|
1016
1027
|
{
|
|
1017
|
-
id: "
|
|
1018
|
-
label: "
|
|
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
|
{
|
|
@@ -1077,7 +1088,7 @@ async function createLLMFromModel(modelConfig, authToken, streaming = false) {
|
|
|
1077
1088
|
case "gemini":
|
|
1078
1089
|
return new import_google_gauth.ChatGoogle({
|
|
1079
1090
|
model: "gemini-flash-latest",
|
|
1080
|
-
apiKey: "
|
|
1091
|
+
apiKey: "AIzaSyBzrL8Hx6dHhXgwc2HfLlQsf5Y-9pdtc9M",
|
|
1081
1092
|
streaming
|
|
1082
1093
|
});
|
|
1083
1094
|
case "openai":
|
|
@@ -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,65 @@ 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 tokenUsage = { input: 0, output: 0, total: 0 };
|
|
1346
|
+
const callbacks = [
|
|
1347
|
+
{
|
|
1348
|
+
handleLLMEnd: (output2) => {
|
|
1349
|
+
let usage = output2.llmOutput?.tokenUsage || output2.llmOutput?.estimatedTokenUsage;
|
|
1350
|
+
if (!usage && output2.generations && Array.isArray(output2.generations) && output2.generations.length > 0) {
|
|
1351
|
+
const firstGen = output2.generations[0][0];
|
|
1352
|
+
if (firstGen?.generationInfo?.tokenUsage) {
|
|
1353
|
+
usage = firstGen.generationInfo.tokenUsage;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
if (usage) {
|
|
1357
|
+
tokenUsage.input += usage.promptTokens || usage.input_tokens || 0;
|
|
1358
|
+
tokenUsage.output += usage.completionTokens || usage.output_tokens || 0;
|
|
1359
|
+
tokenUsage.total += usage.totalTokens || usage.total_tokens || 0;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
];
|
|
1364
|
+
if (stream && emitter) {
|
|
1365
|
+
callbacks.push({
|
|
1366
|
+
handleLLMNewToken: (token) => {
|
|
1367
|
+
if (emitter?.emitDelta) {
|
|
1368
|
+
emitter.emitDelta({
|
|
1369
|
+
content: token,
|
|
1370
|
+
actor: name,
|
|
1371
|
+
isAgent: true,
|
|
1372
|
+
isTool: false
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1281
1378
|
let llmInstance;
|
|
1282
|
-
if (
|
|
1379
|
+
if (finalModel?.integrationId) {
|
|
1283
1380
|
if (!authToken) {
|
|
1284
1381
|
throw new Error("Auth token is required to instantiate LLM from integration");
|
|
1285
1382
|
}
|
|
1286
|
-
llmInstance = await createLLMFromModel(
|
|
1287
|
-
} else if (typeof
|
|
1288
|
-
llmInstance =
|
|
1383
|
+
llmInstance = await createLLMFromModel(finalModel, authToken, stream);
|
|
1384
|
+
} else if (typeof finalModel?.bindTools === "function") {
|
|
1385
|
+
llmInstance = finalModel;
|
|
1289
1386
|
} else {
|
|
1290
1387
|
throw new Error("Invalid model: must have integrationId or be a valid LLM instance with bindTools method");
|
|
1291
1388
|
}
|
|
1389
|
+
if (callbacks.length > 0) {
|
|
1390
|
+
if (llmInstance.callbacks) {
|
|
1391
|
+
if (Array.isArray(llmInstance.callbacks)) {
|
|
1392
|
+
llmInstance.callbacks.push(...callbacks);
|
|
1393
|
+
}
|
|
1394
|
+
} else {
|
|
1395
|
+
llmInstance.callbacks = callbacks;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1292
1398
|
const agent = (0, import_prebuilt.createReactAgent)({
|
|
1293
1399
|
llm: llmInstance,
|
|
1294
1400
|
tools: toolsArray,
|
|
@@ -1305,14 +1411,17 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1305
1411
|
let output = "";
|
|
1306
1412
|
if (message) {
|
|
1307
1413
|
try {
|
|
1308
|
-
const invokeConfig =
|
|
1414
|
+
const invokeConfig = {
|
|
1415
|
+
...checkpointer ? { configurable: { thread_id: sessionId } } : {},
|
|
1416
|
+
callbacks
|
|
1417
|
+
// Also pass here as backup
|
|
1418
|
+
};
|
|
1309
1419
|
if (stream && emitter) {
|
|
1310
1420
|
const streamIterator = await agent.stream(
|
|
1311
1421
|
{ messages: [new import_messages.HumanMessage(message)] },
|
|
1312
1422
|
invokeConfig
|
|
1313
1423
|
);
|
|
1314
1424
|
let lastMessages = [];
|
|
1315
|
-
const sentContents = /* @__PURE__ */ new Set();
|
|
1316
1425
|
for await (const step of streamIterator) {
|
|
1317
1426
|
if (step && typeof step === "object") {
|
|
1318
1427
|
for (const [key, value] of Object.entries(step)) {
|
|
@@ -1320,21 +1429,6 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1320
1429
|
const messages = value.messages;
|
|
1321
1430
|
if (Array.isArray(messages)) {
|
|
1322
1431
|
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
1432
|
}
|
|
1339
1433
|
}
|
|
1340
1434
|
}
|
|
@@ -1343,6 +1437,22 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1343
1437
|
if (lastMessages.length > 0) {
|
|
1344
1438
|
const lastMessage = lastMessages[lastMessages.length - 1];
|
|
1345
1439
|
const content = lastMessage?.content;
|
|
1440
|
+
const msg = lastMessage;
|
|
1441
|
+
const usageMetadata = msg?.response_metadata?.tokenUsage || msg?.response_metadata?.usage || msg?.usage_metadata;
|
|
1442
|
+
if (usageMetadata) {
|
|
1443
|
+
const input = usageMetadata.promptTokens || usageMetadata.input_tokens || 0;
|
|
1444
|
+
const output2 = usageMetadata.completionTokens || usageMetadata.output_tokens || 0;
|
|
1445
|
+
const total = usageMetadata.totalTokens || usageMetadata.total_tokens || 0;
|
|
1446
|
+
if (tokenUsage.total === 0) {
|
|
1447
|
+
if (input + output2 > 0) {
|
|
1448
|
+
tokenUsage.input = input;
|
|
1449
|
+
tokenUsage.output = output2;
|
|
1450
|
+
tokenUsage.total = input + output2;
|
|
1451
|
+
} else if (total > 0) {
|
|
1452
|
+
tokenUsage.total = total;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1346
1456
|
if (typeof content === "string") {
|
|
1347
1457
|
output = content;
|
|
1348
1458
|
} else if (Array.isArray(content)) {
|
|
@@ -1361,6 +1471,22 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1361
1471
|
if (result?.messages && result.messages.length > 0) {
|
|
1362
1472
|
const lastMessage = result.messages[result.messages.length - 1];
|
|
1363
1473
|
const content = lastMessage?.content;
|
|
1474
|
+
const msg = lastMessage;
|
|
1475
|
+
const usageMetadata = msg?.response_metadata?.tokenUsage || msg?.response_metadata?.usage || msg?.usage_metadata;
|
|
1476
|
+
if (usageMetadata) {
|
|
1477
|
+
const input = usageMetadata.promptTokens || usageMetadata.input_tokens || 0;
|
|
1478
|
+
const output2 = usageMetadata.completionTokens || usageMetadata.output_tokens || 0;
|
|
1479
|
+
const total = usageMetadata.totalTokens || usageMetadata.total_tokens || 0;
|
|
1480
|
+
if (tokenUsage.total === 0) {
|
|
1481
|
+
if (input + output2 > 0) {
|
|
1482
|
+
tokenUsage.input = input;
|
|
1483
|
+
tokenUsage.output = output2;
|
|
1484
|
+
tokenUsage.total = input + output2;
|
|
1485
|
+
} else if (total > 0) {
|
|
1486
|
+
tokenUsage.total = total;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1364
1490
|
if (typeof content === "string") {
|
|
1365
1491
|
output = content;
|
|
1366
1492
|
} else if (Array.isArray(content)) {
|
|
@@ -1376,22 +1502,42 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1376
1502
|
}
|
|
1377
1503
|
if (output && afterGuardrails) {
|
|
1378
1504
|
const preparedAfter = await prepareGuardrails(afterGuardrails);
|
|
1379
|
-
const
|
|
1505
|
+
const context = { ...inputs, input: output };
|
|
1506
|
+
const afterResults = await executeGuardrails(preparedAfter, output, guardrailMode, context);
|
|
1380
1507
|
if (afterResults.blocked) {
|
|
1381
1508
|
console.log(`\u{1F6E1}\uFE0F IaAgentNode "${name}": Response blocked by after-agent guardrail:`, afterResults.message);
|
|
1509
|
+
isBlocked = true;
|
|
1510
|
+
guardrailViolations = afterResults.violations || [];
|
|
1382
1511
|
if (stream && emitter?.emitDelta) {
|
|
1383
1512
|
emitter.emitDelta({
|
|
1384
1513
|
content: `
|
|
1385
1514
|
|
|
1386
|
-
|
|
1515
|
+
${afterResults.message}`,
|
|
1387
1516
|
actor: name,
|
|
1388
1517
|
isAgent: true,
|
|
1389
1518
|
isError: true
|
|
1390
1519
|
});
|
|
1391
1520
|
}
|
|
1392
|
-
output = afterResults.message || "Resposta bloqueada por pol\xEDtica de seguran\xE7a.";
|
|
1393
|
-
} else
|
|
1394
|
-
|
|
1521
|
+
output = afterResults.message || "GUARDRAILS ATIVADO: Resposta bloqueada por pol\xEDtica de seguran\xE7a.";
|
|
1522
|
+
} else {
|
|
1523
|
+
if (afterResults.modifiedContent) {
|
|
1524
|
+
output = afterResults.modifiedContent;
|
|
1525
|
+
}
|
|
1526
|
+
const warnings = afterResults.violations.filter((v) => v.action === "warn");
|
|
1527
|
+
if (warnings.length > 0) {
|
|
1528
|
+
const warningText = warnings.map((w) => `
|
|
1529
|
+
|
|
1530
|
+
\u26A0\uFE0F GUARDRAILS ATIVADO: ${w.message}`).join("");
|
|
1531
|
+
output += warningText;
|
|
1532
|
+
if (stream && emitter?.emitDelta) {
|
|
1533
|
+
emitter.emitDelta({
|
|
1534
|
+
content: warningText,
|
|
1535
|
+
actor: name,
|
|
1536
|
+
isAgent: true,
|
|
1537
|
+
isError: false
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1395
1541
|
}
|
|
1396
1542
|
}
|
|
1397
1543
|
} catch (error) {
|
|
@@ -1399,9 +1545,19 @@ IMPORTANT: You must base your response on the last message in the conversation h
|
|
|
1399
1545
|
output = `Error: ${error instanceof Error ? error.message : "Unknown error"}`;
|
|
1400
1546
|
}
|
|
1401
1547
|
}
|
|
1548
|
+
if (tokenUsage.total === 0 && (tokenUsage.input > 0 || tokenUsage.output > 0)) {
|
|
1549
|
+
tokenUsage.total = tokenUsage.input + tokenUsage.output;
|
|
1550
|
+
} else if (tokenUsage.total !== tokenUsage.input + tokenUsage.output) {
|
|
1551
|
+
tokenUsage.total = tokenUsage.input + tokenUsage.output;
|
|
1552
|
+
}
|
|
1553
|
+
console.log(`[TOKEN COUNT] Input: ${tokenUsage.input}, Output: ${tokenUsage.output}, Total: ${tokenUsage.total}`);
|
|
1402
1554
|
return {
|
|
1403
1555
|
agent,
|
|
1404
|
-
|
|
1556
|
+
response: output || "",
|
|
1557
|
+
output: output || "",
|
|
1558
|
+
// Keep both for safety
|
|
1559
|
+
blocked: isBlocked,
|
|
1560
|
+
violations: guardrailViolations
|
|
1405
1561
|
};
|
|
1406
1562
|
};
|
|
1407
1563
|
|
|
@@ -1821,7 +1977,7 @@ var import_langgraph_checkpoint_postgres = require("@langchain/langgraph-checkpo
|
|
|
1821
1977
|
var PostgresMemoryNodeFunction = async (inputs) => {
|
|
1822
1978
|
const { $field: _$field, $req: _$req, $inputs: _$inputs, $vars: _$vars } = inputs;
|
|
1823
1979
|
const fieldValues = inputs.fieldValues || {};
|
|
1824
|
-
const connectionString = fieldValues.connectionString || inputs.connectionString || "postgresql://
|
|
1980
|
+
const connectionString = fieldValues.connectionString || inputs.connectionString || "postgresql://yugabyte:yugabyte@localhost:5433/workflows";
|
|
1825
1981
|
try {
|
|
1826
1982
|
const checkpointer = import_langgraph_checkpoint_postgres.PostgresSaver.fromConnString(connectionString);
|
|
1827
1983
|
await checkpointer.setup();
|
|
@@ -2399,12 +2555,14 @@ var PromptGuardrailNode = {
|
|
|
2399
2555
|
|
|
2400
2556
|
// src/nodes/guardrails/prompt/function.ts
|
|
2401
2557
|
var PromptGuardrailNodeFunction = async (inputs) => {
|
|
2402
|
-
const
|
|
2403
|
-
const { name, prompt } =
|
|
2558
|
+
const values = inputs.fieldValues || inputs;
|
|
2559
|
+
const { name, prompt } = values;
|
|
2404
2560
|
return {
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2561
|
+
guardrail: {
|
|
2562
|
+
type: "prompt",
|
|
2563
|
+
name: name || "Prompt Guardrail",
|
|
2564
|
+
prompt: prompt || ""
|
|
2565
|
+
}
|
|
2408
2566
|
};
|
|
2409
2567
|
};
|
|
2410
2568
|
|
|
@@ -2415,7 +2573,8 @@ var RuleGuardrailNodeSchema = import_zod14.z.object({
|
|
|
2415
2573
|
phase: import_zod14.z.enum(["before", "after"]).describe("Fase de execu\xE7\xE3o"),
|
|
2416
2574
|
ruleType: import_zod14.z.enum(["maxValue", "minValue", "regex", "keywords", "pii"]).describe("Tipo de regra"),
|
|
2417
2575
|
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")
|
|
2576
|
+
action: import_zod14.z.enum(["block", "warn", "modify"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o"),
|
|
2577
|
+
actionInstruction: import_zod14.z.string().optional().describe("Instru\xE7\xE3o adicional para a a\xE7\xE3o")
|
|
2419
2578
|
});
|
|
2420
2579
|
var RuleGuardrailNode = {
|
|
2421
2580
|
label: "Rule Guardrail",
|
|
@@ -2479,6 +2638,13 @@ var RuleGuardrailNode = {
|
|
|
2479
2638
|
{ label: "Modificar/Ocultar", value: "modify" }
|
|
2480
2639
|
]
|
|
2481
2640
|
},
|
|
2641
|
+
{
|
|
2642
|
+
id: "actionInstruction",
|
|
2643
|
+
label: "Instru\xE7\xE3o da A\xE7\xE3o",
|
|
2644
|
+
type: "textarea",
|
|
2645
|
+
required: false,
|
|
2646
|
+
placeholder: "Ex: Bloquear: 'N\xE3o posso falar sobre isso'. Warn: 'Aten\xE7\xE3o...'"
|
|
2647
|
+
},
|
|
2482
2648
|
{
|
|
2483
2649
|
id: "guardrail",
|
|
2484
2650
|
label: "Guardrail",
|
|
@@ -2497,15 +2663,21 @@ var RuleGuardrailNode = {
|
|
|
2497
2663
|
|
|
2498
2664
|
// src/nodes/guardrails/rule/function.ts
|
|
2499
2665
|
var RuleGuardrailNodeFunction = async (inputs) => {
|
|
2500
|
-
const
|
|
2501
|
-
const {
|
|
2666
|
+
const values = inputs.fieldValues || inputs;
|
|
2667
|
+
const {
|
|
2668
|
+
name,
|
|
2669
|
+
ruleType,
|
|
2670
|
+
config,
|
|
2671
|
+
action
|
|
2672
|
+
} = values;
|
|
2502
2673
|
return {
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2674
|
+
guardrail: {
|
|
2675
|
+
type: "rule",
|
|
2676
|
+
name: name || "Rule Guardrail",
|
|
2677
|
+
ruleType: ruleType || "keywords",
|
|
2678
|
+
config: config || "",
|
|
2679
|
+
action: action || "block"
|
|
2680
|
+
}
|
|
2509
2681
|
};
|
|
2510
2682
|
};
|
|
2511
2683
|
|
|
@@ -2515,7 +2687,8 @@ var FunctionGuardrailNodeSchema = import_zod15.z.object({
|
|
|
2515
2687
|
name: import_zod15.z.string().describe("Nome do guardrail"),
|
|
2516
2688
|
phase: import_zod15.z.enum(["before", "after"]).describe("Fase de execu\xE7\xE3o"),
|
|
2517
2689
|
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")
|
|
2690
|
+
action: import_zod15.z.enum(["block", "warn", "modify", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o"),
|
|
2691
|
+
actionInstruction: import_zod15.z.string().optional().describe("Instru\xE7\xE3o adicional para a a\xE7\xE3o")
|
|
2519
2692
|
});
|
|
2520
2693
|
var FunctionGuardrailNode = {
|
|
2521
2694
|
label: "Function Guardrail",
|
|
@@ -2582,6 +2755,13 @@ var FunctionGuardrailNode = {
|
|
|
2582
2755
|
{ label: "Escalar (Human-in-the-loop)", value: "escalate" }
|
|
2583
2756
|
]
|
|
2584
2757
|
},
|
|
2758
|
+
{
|
|
2759
|
+
id: "actionInstruction",
|
|
2760
|
+
label: "Instru\xE7\xE3o da A\xE7\xE3o",
|
|
2761
|
+
type: "textarea",
|
|
2762
|
+
required: false,
|
|
2763
|
+
placeholder: "Ex: Instru\xE7\xE3o para a modifica\xE7\xE3o ou mensagem de bloqueio"
|
|
2764
|
+
},
|
|
2585
2765
|
{
|
|
2586
2766
|
id: "guardrail",
|
|
2587
2767
|
label: "Guardrail",
|
|
@@ -2600,26 +2780,24 @@ var FunctionGuardrailNode = {
|
|
|
2600
2780
|
|
|
2601
2781
|
// src/nodes/guardrails/function/function.ts
|
|
2602
2782
|
var FunctionGuardrailNodeFunction = async (inputs) => {
|
|
2603
|
-
const
|
|
2783
|
+
const values = inputs.fieldValues || inputs;
|
|
2604
2784
|
const {
|
|
2605
2785
|
name,
|
|
2606
|
-
phase,
|
|
2607
2786
|
description,
|
|
2608
2787
|
action,
|
|
2609
2788
|
nodeFunction,
|
|
2610
|
-
nodeType,
|
|
2611
2789
|
originalNodeData
|
|
2612
|
-
} =
|
|
2790
|
+
} = values;
|
|
2613
2791
|
return {
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2792
|
+
guardrail: {
|
|
2793
|
+
type: "function",
|
|
2794
|
+
name: name || "Function Guardrail",
|
|
2795
|
+
description,
|
|
2796
|
+
action: action || "block",
|
|
2797
|
+
// Injected by executeWorkflow.ts
|
|
2798
|
+
nodeFunction,
|
|
2799
|
+
originalNodeData
|
|
2800
|
+
}
|
|
2623
2801
|
};
|
|
2624
2802
|
};
|
|
2625
2803
|
|
|
@@ -2629,7 +2807,9 @@ var ModelGuardrailNodeSchema = import_zod16.z.object({
|
|
|
2629
2807
|
name: import_zod16.z.string().describe("Nome do guardrail"),
|
|
2630
2808
|
evaluationPrompt: import_zod16.z.string().describe("Prompt de avalia\xE7\xE3o"),
|
|
2631
2809
|
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")
|
|
2810
|
+
action: import_zod16.z.enum(["block", "warn", "fix", "escalate"]).describe("A\xE7\xE3o em caso de viola\xE7\xE3o"),
|
|
2811
|
+
phase: import_zod16.z.enum(["before", "after"]).describe("Fase de execu\xE7\xE3o"),
|
|
2812
|
+
actionInstruction: import_zod16.z.string().optional().describe("Instru\xE7\xE3o adicional para a a\xE7\xE3o")
|
|
2633
2813
|
});
|
|
2634
2814
|
var ModelGuardrailNode = {
|
|
2635
2815
|
label: "Model Guardrail",
|
|
@@ -2680,10 +2860,29 @@ var ModelGuardrailNode = {
|
|
|
2680
2860
|
defaultValue: "block",
|
|
2681
2861
|
options: [
|
|
2682
2862
|
{ label: "Bloquear Resposta", value: "block" },
|
|
2863
|
+
{ label: "Corrigir/Reescrever (Fix)", value: "fix" },
|
|
2683
2864
|
{ label: "Avisar (Warning)", value: "warn" },
|
|
2684
2865
|
{ label: "Escalar (Human-in-the-loop)", value: "escalate" }
|
|
2685
2866
|
]
|
|
2686
2867
|
},
|
|
2868
|
+
{
|
|
2869
|
+
id: "actionInstruction",
|
|
2870
|
+
label: "Instru\xE7\xE3o da A\xE7\xE3o",
|
|
2871
|
+
type: "textarea",
|
|
2872
|
+
required: false,
|
|
2873
|
+
placeholder: "Ex: Se bloquear, diga 'Pol\xEDtica violada'. Se corrigir, 'Reescreva com...'"
|
|
2874
|
+
},
|
|
2875
|
+
{
|
|
2876
|
+
id: "phase",
|
|
2877
|
+
label: "Fase de Execu\xE7\xE3o",
|
|
2878
|
+
type: "select",
|
|
2879
|
+
required: true,
|
|
2880
|
+
defaultValue: "after",
|
|
2881
|
+
options: [
|
|
2882
|
+
{ label: "Antes do Agente (Input)", value: "before" },
|
|
2883
|
+
{ label: "Depois do Agente (Output)", value: "after" }
|
|
2884
|
+
]
|
|
2885
|
+
},
|
|
2687
2886
|
{
|
|
2688
2887
|
id: "guardrail",
|
|
2689
2888
|
label: "Guardrail",
|
|
@@ -2702,19 +2901,25 @@ var ModelGuardrailNode = {
|
|
|
2702
2901
|
|
|
2703
2902
|
// src/nodes/guardrails/model/function.ts
|
|
2704
2903
|
var ModelGuardrailNodeFunction = async (inputs) => {
|
|
2705
|
-
const
|
|
2904
|
+
const values = inputs.fieldValues || inputs;
|
|
2706
2905
|
const {
|
|
2707
2906
|
name,
|
|
2708
2907
|
evaluationPrompt,
|
|
2709
2908
|
model,
|
|
2710
2909
|
action
|
|
2711
|
-
} =
|
|
2910
|
+
} = values;
|
|
2911
|
+
const defaultModel = {
|
|
2912
|
+
model: "gemini-flash-latest",
|
|
2913
|
+
integrationId: "default-gemini"
|
|
2914
|
+
};
|
|
2712
2915
|
return {
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2916
|
+
guardrail: {
|
|
2917
|
+
type: "model",
|
|
2918
|
+
name: name || "Model Guardrail",
|
|
2919
|
+
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}}",
|
|
2920
|
+
model: model || defaultModel,
|
|
2921
|
+
action: action || "block"
|
|
2922
|
+
}
|
|
2718
2923
|
};
|
|
2719
2924
|
};
|
|
2720
2925
|
|