@paroicms/site-generator-plugin
Version:
ParoiCMS Site Generator Plugin
106 lines (105 loc) • 3.82 kB
JavaScript
import { boolVal, listValOrUndef, strVal, strValOrUndef, } from "@paroi/data-formatters-lib";
import { insertIssueEvent } from "../../db/db-write.queries.js";
import { invokeClaude } from "../lib/calling-llm-anthropic.js";
import { createPromptTemplate } from "../lib/create-prompt.js";
import { debugLlmOutput } from "../lib/debug-utils.js";
import { parseLlmResponseAsProperties } from "../lib/parse-llm-response.js";
const guardPrompt = await createPromptTemplate({
filename: "message-guard.md",
});
const invalidCauses = new Set(["rude", "malicious", "outOfScope", "technicalLimits", "noSense"]);
export async function invokeMessageGuard(ctx, input, { skipOutOfScopeCheck = false } = {}) {
const llmTaskName = "guard";
const llmInput = {
message: input.prompt,
};
const debug = await debugLlmOutput(ctx, llmTaskName, ctx.anthropicModelName, undefined, {
message: llmInput.message,
});
let llmOutput = debug.stored;
if (!llmOutput) {
const { messageContent, report } = await invokeClaude(ctx, {
llmTaskName,
prompt: guardPrompt(llmInput),
maxTokens: 200,
temperature: 0.2,
systemInstruction: "beFast",
});
llmOutput = await debug.getMessageContent(messageContent, report);
}
const rawReport = parseLlmResponseAsProperties(llmOutput.output, [
{
tagName: "guard_yaml",
key: "guard",
format: "yaml",
},
]);
const report = formatMessageGuardReport(rawReport.guard);
if (report.causes && skipOutOfScopeCheck) {
report.causes = report.causes.filter((cause) => cause !== "outOfScope");
if (report.causes.length === 0) {
report.valid = true;
}
}
if (report.valid)
return;
await insertIssueEvent(ctx, {
eventType: "blockedByGuard",
llmTaskName: "guard",
issueMessage: report.explanation,
});
return {
success: false,
language: report.language,
userMessage: report.explanation ?? toUserMessage(report.causes, report.language),
};
}
function toUserMessage(causes, language) {
if (!causes || causes.length === 0)
return "Invalid message";
return causes.map((cause) => getCauseLabel(cause, language)).join(", ");
}
function getCauseLabel(cause, language) {
if (language === "fr") {
switch (cause) {
case "rude":
case "malicious":
return "Nous ne pouvons pas répondre à cette demande";
case "outOfScope":
return "Hors sujet";
case "technicalLimits":
return "Limites techniques";
case "noSense":
return "Nous ne comprenons pas la demande";
default:
return cause;
}
}
switch (cause) {
case "rude":
case "malicious":
return "We cannot answer to this request";
case "outOfScope":
return "Out of scope";
case "technicalLimits":
return "Technical limits";
case "noSense":
return "We do not understand the request";
default:
return cause;
}
}
function formatMessageGuardReport(rawReport) {
return {
language: strValOrUndef(rawReport.language, { varName: "language" }),
valid: boolVal(rawReport.valid, { varName: "valid" }),
causes: listValOrUndef(rawReport.causes, formatGuardInvalidCause),
explanation: strValOrUndef(rawReport.explanation, { varName: "explanation" }),
};
}
function formatGuardInvalidCause(cause) {
const v = strVal(cause, { varName: "cause" });
if (!invalidCauses.has(v))
throw new Error(`Invalid cause: "${v}"`);
return v;
}