plugin-sensitive-filter-xr 0.0.4 → 0.0.5
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/lib/sensitiveFilter.d.ts.map +1 -1
- package/dist/lib/sensitiveFilter.js +253 -112
- package/dist/lib/types.d.ts +8 -14
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +3 -11
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sensitiveFilter.d.ts","sourceRoot":"","sources":["../../src/lib/sensitiveFilter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,oBAAoB,EAA8B,MAAM,kBAAkB,CAAA;AAGnG,OAAO,EACL,eAAe,EAGf,uBAAuB,EACvB,wBAAwB,EAEzB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAOL,qBAAqB,EAMtB,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"sensitiveFilter.d.ts","sourceRoot":"","sources":["../../src/lib/sensitiveFilter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,oBAAoB,EAA8B,MAAM,kBAAkB,CAAA;AAGnG,OAAO,EACL,eAAe,EAGf,uBAAuB,EACvB,wBAAwB,EAEzB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAOL,qBAAqB,EAMtB,MAAM,YAAY,CAAA;AAienB,qBAEa,yBAA0B,YAAW,wBAAwB,CAAC,qBAAqB,CAAC;IAE/F,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IAEvC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAqRlC;IAEK,gBAAgB,CACpB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,eAAe,CAAC;IAa3B,OAAO,CAAC,wBAAwB;IAqOhC,OAAO,CAAC,uBAAuB;CA8YhC;AAED,YAAY,EAAE,qBAAqB,EAAE,CAAA"}
|
|
@@ -11,7 +11,7 @@ const DEFAULT_OUTPUT_BLOCK_MESSAGE = '输出内容触发敏感策略,已拦截
|
|
|
11
11
|
const DEFAULT_REWRITE_TEXT = '[已过滤]';
|
|
12
12
|
const CONFIG_PARSE_ERROR = '敏感词过滤配置格式不正确,请检查填写内容。';
|
|
13
13
|
const BUSINESS_RULES_VALIDATION_ERROR = '请至少配置 1 条有效业务规则(pattern/type/action/scope/severity),或启用通用规则包。';
|
|
14
|
-
const LLM_MODE_VALIDATION_ERROR = '请完善 LLM
|
|
14
|
+
const LLM_MODE_VALIDATION_ERROR = '请完善 LLM 过滤配置:需填写过滤模型、生效范围、审核规则说明。';
|
|
15
15
|
function isRecord(value) {
|
|
16
16
|
return typeof value === 'object' && value !== null;
|
|
17
17
|
}
|
|
@@ -212,6 +212,14 @@ function normalizeRuleDrafts(input) {
|
|
|
212
212
|
function modeIncludesScope(scope, phase) {
|
|
213
213
|
return scope === 'both' || scope === phase;
|
|
214
214
|
}
|
|
215
|
+
function buildInternalLlmPrompt(rulePrompt) {
|
|
216
|
+
return ('你是敏感内容审核器。请根据用户给出的审核规则判断文本是否命中敏感内容。' +
|
|
217
|
+
'\n你只能返回 JSON,不要输出额外说明。' +
|
|
218
|
+
'\nJSON字段: matched(boolean), action("block"|"rewrite"), replacementText(string), reason(string), categories(string[]).' +
|
|
219
|
+
'\n命中时请尽量给出 replacementText;未命中时返回 {"matched": false}。' +
|
|
220
|
+
'\n说明:系统会统一执行改写策略,即便你返回 action=block 也会按 rewrite 处理。' +
|
|
221
|
+
`\n\n用户审核规则:\n${rulePrompt}`);
|
|
222
|
+
}
|
|
215
223
|
function parseLlmDecision(raw, rewriteFallbackText) {
|
|
216
224
|
let payload = raw;
|
|
217
225
|
if (typeof payload === 'string') {
|
|
@@ -248,14 +256,6 @@ function parseLlmDecision(raw, rewriteFallbackText) {
|
|
|
248
256
|
categories,
|
|
249
257
|
};
|
|
250
258
|
}
|
|
251
|
-
if (decision.action === 'block') {
|
|
252
|
-
return {
|
|
253
|
-
matched: true,
|
|
254
|
-
action: 'block',
|
|
255
|
-
reason,
|
|
256
|
-
categories,
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
259
|
return {
|
|
260
260
|
matched: true,
|
|
261
261
|
action: 'rewrite',
|
|
@@ -270,30 +270,27 @@ function resolveRuntimeLlmConfig(config) {
|
|
|
270
270
|
}
|
|
271
271
|
const model = isRecord(config.model) ? config.model : null;
|
|
272
272
|
const scope = toNonEmptyString(config.scope);
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
if (!model || !scope || !systemPrompt || !onLlmError) {
|
|
273
|
+
const rulePrompt = toNonEmptyString(config.rulePrompt) ?? toNonEmptyString(config.systemPrompt);
|
|
274
|
+
if (!model || !scope || !rulePrompt) {
|
|
276
275
|
throw new Error(LLM_MODE_VALIDATION_ERROR);
|
|
277
276
|
}
|
|
278
277
|
const outputMethodRaw = toNonEmptyString(config.outputMethod);
|
|
279
278
|
const outputMethod = ['functionCalling', 'jsonMode', 'jsonSchema'].includes(outputMethodRaw ?? '')
|
|
280
279
|
? outputMethodRaw
|
|
281
|
-
: '
|
|
282
|
-
const errorRewriteText = toNonEmptyString(config.errorRewriteText) ?? undefined;
|
|
283
|
-
if (onLlmError === 'rewrite' && !errorRewriteText) {
|
|
284
|
-
throw new Error('请完善 LLM 过滤配置:当失败处理为改写时,必须填写失败改写文本。');
|
|
285
|
-
}
|
|
280
|
+
: 'jsonMode';
|
|
286
281
|
const timeoutMs = typeof config.timeoutMs === 'number' && Number.isFinite(config.timeoutMs) && config.timeoutMs > 0
|
|
287
282
|
? Math.min(Math.floor(config.timeoutMs), 120000)
|
|
288
283
|
: undefined;
|
|
284
|
+
const legacyOnLlmError = toNonEmptyString(config.onLlmError);
|
|
285
|
+
const legacyErrorRewriteText = toNonEmptyString(config.errorRewriteText) ?? undefined;
|
|
289
286
|
return {
|
|
290
287
|
model,
|
|
291
288
|
scope,
|
|
292
|
-
|
|
289
|
+
rulePrompt,
|
|
290
|
+
systemPrompt: buildInternalLlmPrompt(rulePrompt),
|
|
293
291
|
outputMethod,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
blockMessage: toNonEmptyString(config.blockMessage) ?? undefined,
|
|
292
|
+
legacyOnLlmError: legacyOnLlmError ?? undefined,
|
|
293
|
+
legacyErrorRewriteText,
|
|
297
294
|
rewriteFallbackText: toNonEmptyString(config.rewriteFallbackText) ?? DEFAULT_REWRITE_TEXT,
|
|
298
295
|
timeoutMs,
|
|
299
296
|
};
|
|
@@ -317,6 +314,39 @@ function withTimeout(promise, timeoutMs) {
|
|
|
317
314
|
});
|
|
318
315
|
});
|
|
319
316
|
}
|
|
317
|
+
function normalizeConfigurable(input) {
|
|
318
|
+
if (!isRecord(input)) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
return input;
|
|
322
|
+
}
|
|
323
|
+
function buildOutputMethodCandidates(preferred) {
|
|
324
|
+
const queue = [preferred, 'functionCalling', 'jsonMode', 'jsonSchema'];
|
|
325
|
+
const unique = [];
|
|
326
|
+
for (const method of queue) {
|
|
327
|
+
if (!unique.includes(method)) {
|
|
328
|
+
unique.push(method);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return unique;
|
|
332
|
+
}
|
|
333
|
+
function getErrorText(error) {
|
|
334
|
+
if (error instanceof Error) {
|
|
335
|
+
return error.message;
|
|
336
|
+
}
|
|
337
|
+
return String(error ?? '');
|
|
338
|
+
}
|
|
339
|
+
function isUnsupportedStructuredOutputError(error) {
|
|
340
|
+
const message = getErrorText(error).toLowerCase();
|
|
341
|
+
const patterns = [
|
|
342
|
+
'response_format type is unavailable',
|
|
343
|
+
'invalid response_format',
|
|
344
|
+
'response_format',
|
|
345
|
+
'unsupported schema',
|
|
346
|
+
'not support',
|
|
347
|
+
];
|
|
348
|
+
return patterns.some((pattern) => message.includes(pattern));
|
|
349
|
+
}
|
|
320
350
|
let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
321
351
|
constructor() {
|
|
322
352
|
this.meta = {
|
|
@@ -534,19 +564,27 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
534
564
|
},
|
|
535
565
|
},
|
|
536
566
|
},
|
|
537
|
-
|
|
567
|
+
rulePrompt: {
|
|
538
568
|
type: 'string',
|
|
539
|
-
title: { en_US: '
|
|
569
|
+
title: { en_US: 'Rule Prompt', zh_Hans: '审核规则说明' },
|
|
570
|
+
description: {
|
|
571
|
+
en_US: 'Describe your moderation rules in natural language. No JSON format is required.',
|
|
572
|
+
zh_Hans: '用自然语言描述审核规则,无需手写 JSON 格式。',
|
|
573
|
+
},
|
|
540
574
|
'x-ui': {
|
|
541
575
|
component: 'textarea',
|
|
542
576
|
span: 2,
|
|
577
|
+
placeholder: {
|
|
578
|
+
en_US: 'e.g. Rewrite violent/privacy-sensitive content into a safe neutral response.',
|
|
579
|
+
zh_Hans: '例如:涉及暴力或隐私泄露内容时,改写为安全中性表达。',
|
|
580
|
+
},
|
|
543
581
|
},
|
|
544
582
|
},
|
|
545
583
|
outputMethod: {
|
|
546
584
|
type: 'string',
|
|
547
585
|
title: { en_US: 'Output Method', zh_Hans: '结构化输出方式' },
|
|
548
586
|
enum: ['functionCalling', 'jsonMode', 'jsonSchema'],
|
|
549
|
-
default: '
|
|
587
|
+
default: 'jsonMode',
|
|
550
588
|
'x-ui': {
|
|
551
589
|
enumLabels: {
|
|
552
590
|
functionCalling: { en_US: 'Function Calling', zh_Hans: '函数调用' },
|
|
@@ -555,25 +593,6 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
555
593
|
},
|
|
556
594
|
},
|
|
557
595
|
},
|
|
558
|
-
onLlmError: {
|
|
559
|
-
type: 'string',
|
|
560
|
-
title: { en_US: 'On LLM Error', zh_Hans: 'LLM失败处理' },
|
|
561
|
-
enum: ['block', 'rewrite'],
|
|
562
|
-
'x-ui': {
|
|
563
|
-
enumLabels: {
|
|
564
|
-
block: { en_US: 'Block', zh_Hans: '拦截' },
|
|
565
|
-
rewrite: { en_US: 'Rewrite', zh_Hans: '改写' },
|
|
566
|
-
},
|
|
567
|
-
},
|
|
568
|
-
},
|
|
569
|
-
errorRewriteText: {
|
|
570
|
-
type: 'string',
|
|
571
|
-
title: { en_US: 'Error Rewrite Text', zh_Hans: '失败改写文本' },
|
|
572
|
-
},
|
|
573
|
-
blockMessage: {
|
|
574
|
-
type: 'string',
|
|
575
|
-
title: { en_US: 'Block Message', zh_Hans: '拦截提示文本' },
|
|
576
|
-
},
|
|
577
596
|
rewriteFallbackText: {
|
|
578
597
|
type: 'string',
|
|
579
598
|
title: { en_US: 'Rewrite Fallback Text', zh_Hans: '改写兜底文本' },
|
|
@@ -583,21 +602,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
583
602
|
title: { en_US: 'Timeout (ms)', zh_Hans: '超时毫秒' },
|
|
584
603
|
},
|
|
585
604
|
},
|
|
586
|
-
required: ['model', 'scope', '
|
|
587
|
-
allOf: [
|
|
588
|
-
{
|
|
589
|
-
if: {
|
|
590
|
-
properties: {
|
|
591
|
-
onLlmError: {
|
|
592
|
-
const: 'rewrite',
|
|
593
|
-
},
|
|
594
|
-
},
|
|
595
|
-
},
|
|
596
|
-
then: {
|
|
597
|
-
required: ['errorRewriteText'],
|
|
598
|
-
},
|
|
599
|
-
},
|
|
600
|
-
],
|
|
605
|
+
required: ['model', 'scope', 'rulePrompt'],
|
|
601
606
|
},
|
|
602
607
|
},
|
|
603
608
|
required: ['mode'],
|
|
@@ -626,9 +631,9 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
626
631
|
if (parsed.data.mode === 'llm') {
|
|
627
632
|
return this.createLlmModeMiddleware(parsed.data, context);
|
|
628
633
|
}
|
|
629
|
-
return this.createRuleModeMiddleware(parsed.data);
|
|
634
|
+
return this.createRuleModeMiddleware(parsed.data, context);
|
|
630
635
|
}
|
|
631
|
-
createRuleModeMiddleware(config) {
|
|
636
|
+
createRuleModeMiddleware(config, context) {
|
|
632
637
|
const caseSensitive = config.caseSensitive ?? false;
|
|
633
638
|
const normalize = config.normalize ?? true;
|
|
634
639
|
const customRules = normalizeRuleDrafts(config.rules ?? []);
|
|
@@ -669,6 +674,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
669
674
|
let pendingInputRewrite = null;
|
|
670
675
|
let finalAction = 'pass';
|
|
671
676
|
let auditEntries = [];
|
|
677
|
+
let runtimeConfigurable = null;
|
|
672
678
|
const resetRunState = () => {
|
|
673
679
|
inputBlockedMessage = null;
|
|
674
680
|
pendingInputRewrite = null;
|
|
@@ -682,10 +688,65 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
682
688
|
mode: 'rule',
|
|
683
689
|
});
|
|
684
690
|
};
|
|
691
|
+
const assignRuntimeConfigurable = (runtimeLike) => {
|
|
692
|
+
const configurable = normalizeConfigurable(runtimeLike?.configurable);
|
|
693
|
+
if (!configurable) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (configurable.thread_id && configurable.executionId) {
|
|
697
|
+
runtimeConfigurable = configurable;
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
const buildAuditSnapshot = () => {
|
|
701
|
+
const summary = {
|
|
702
|
+
total: auditEntries.length,
|
|
703
|
+
matched: auditEntries.filter((entry) => entry.matched).length,
|
|
704
|
+
blocked: auditEntries.filter((entry) => entry.action === 'block').length,
|
|
705
|
+
rewritten: auditEntries.filter((entry) => entry.action === 'rewrite').length,
|
|
706
|
+
errorPolicyTriggered: auditEntries.filter((entry) => entry.errorPolicyTriggered).length,
|
|
707
|
+
};
|
|
708
|
+
return {
|
|
709
|
+
mode: 'rule',
|
|
710
|
+
finalAction,
|
|
711
|
+
records: auditEntries,
|
|
712
|
+
summary,
|
|
713
|
+
};
|
|
714
|
+
};
|
|
715
|
+
const persistAuditSnapshot = async () => {
|
|
716
|
+
const configurable = runtimeConfigurable;
|
|
717
|
+
if (!configurable?.thread_id || !configurable.executionId || !this.commandBus) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
const { thread_id, checkpoint_ns, checkpoint_id, subscriber, executionId } = configurable;
|
|
721
|
+
const snapshot = buildAuditSnapshot();
|
|
722
|
+
await this.commandBus.execute(new WrapWorkflowNodeExecutionCommand(async () => {
|
|
723
|
+
return {
|
|
724
|
+
state: snapshot,
|
|
725
|
+
output: snapshot,
|
|
726
|
+
};
|
|
727
|
+
}, {
|
|
728
|
+
execution: {
|
|
729
|
+
category: 'workflow',
|
|
730
|
+
type: 'middleware',
|
|
731
|
+
title: `${context.node.title} Audit`,
|
|
732
|
+
inputs: {
|
|
733
|
+
mode: snapshot.mode,
|
|
734
|
+
total: snapshot.summary.total,
|
|
735
|
+
},
|
|
736
|
+
parentId: executionId,
|
|
737
|
+
threadId: thread_id,
|
|
738
|
+
checkpointNs: checkpoint_ns,
|
|
739
|
+
checkpointId: checkpoint_id,
|
|
740
|
+
agentKey: context.node.key,
|
|
741
|
+
},
|
|
742
|
+
subscriber,
|
|
743
|
+
}));
|
|
744
|
+
};
|
|
685
745
|
return {
|
|
686
746
|
name: SENSITIVE_FILTER_MIDDLEWARE_NAME,
|
|
687
747
|
beforeAgent: async (state, runtime) => {
|
|
688
748
|
resetRunState();
|
|
749
|
+
assignRuntimeConfigurable(runtime);
|
|
689
750
|
if (!hasEffectiveRules) {
|
|
690
751
|
throw new Error(BUSINESS_RULES_VALIDATION_ERROR);
|
|
691
752
|
}
|
|
@@ -722,6 +783,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
722
783
|
return undefined;
|
|
723
784
|
},
|
|
724
785
|
wrapModelCall: async (request, handler) => {
|
|
786
|
+
assignRuntimeConfigurable(request?.runtime);
|
|
725
787
|
if (!hasEffectiveRules) {
|
|
726
788
|
throw new Error(BUSINESS_RULES_VALIDATION_ERROR);
|
|
727
789
|
}
|
|
@@ -762,11 +824,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
762
824
|
return replaceModelResponseText(response, rewrittenOutput);
|
|
763
825
|
},
|
|
764
826
|
afterAgent: async () => {
|
|
765
|
-
|
|
766
|
-
mode: 'rule',
|
|
767
|
-
finalAction,
|
|
768
|
-
records: auditEntries,
|
|
769
|
-
}, null, 2));
|
|
827
|
+
await persistAuditSnapshot();
|
|
770
828
|
return undefined;
|
|
771
829
|
},
|
|
772
830
|
};
|
|
@@ -775,7 +833,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
775
833
|
const llmDraftConfig = config.llm;
|
|
776
834
|
let resolvedLlmConfig = null;
|
|
777
835
|
let modelPromise = null;
|
|
778
|
-
|
|
836
|
+
const structuredModelPromises = new Map();
|
|
779
837
|
const getLlmConfig = () => {
|
|
780
838
|
if (!resolvedLlmConfig) {
|
|
781
839
|
resolvedLlmConfig = resolveRuntimeLlmConfig(llmDraftConfig);
|
|
@@ -786,34 +844,36 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
786
844
|
const llmConfig = getLlmConfig();
|
|
787
845
|
if (!modelPromise) {
|
|
788
846
|
modelPromise = this.commandBus.execute(new CreateModelClientCommand(llmConfig.model, {
|
|
789
|
-
usageCallback: (
|
|
790
|
-
console.log('[SensitiveFilterMiddleware][LLM] Model Usage:', event);
|
|
791
|
-
},
|
|
847
|
+
usageCallback: () => { },
|
|
792
848
|
}));
|
|
793
849
|
}
|
|
794
850
|
return modelPromise;
|
|
795
851
|
};
|
|
796
|
-
const ensureStructuredModel = async () => {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
structuredModelPromise = (async () => {
|
|
852
|
+
const ensureStructuredModel = async (method) => {
|
|
853
|
+
if (!structuredModelPromises.has(method)) {
|
|
854
|
+
structuredModelPromises.set(method, (async () => {
|
|
800
855
|
const model = await ensureModel();
|
|
801
856
|
return model.withStructuredOutput?.(llmDecisionSchema, {
|
|
802
|
-
method
|
|
857
|
+
method,
|
|
803
858
|
}) ?? null;
|
|
804
|
-
})();
|
|
859
|
+
})());
|
|
805
860
|
}
|
|
806
|
-
return
|
|
861
|
+
return structuredModelPromises.get(method);
|
|
807
862
|
};
|
|
808
|
-
let inputBlockedMessage = null;
|
|
809
863
|
let pendingInputRewrite = null;
|
|
810
864
|
let finalAction = 'pass';
|
|
811
865
|
let auditEntries = [];
|
|
866
|
+
let runtimeConfigurable = null;
|
|
867
|
+
let resolvedOutputMethod;
|
|
868
|
+
let fallbackTriggered = false;
|
|
869
|
+
let methodAttempts = [];
|
|
812
870
|
const resetRunState = () => {
|
|
813
|
-
inputBlockedMessage = null;
|
|
814
871
|
pendingInputRewrite = null;
|
|
815
872
|
finalAction = 'pass';
|
|
816
873
|
auditEntries = [];
|
|
874
|
+
resolvedOutputMethod = undefined;
|
|
875
|
+
fallbackTriggered = false;
|
|
876
|
+
methodAttempts = [];
|
|
817
877
|
};
|
|
818
878
|
const pushAudit = (entry) => {
|
|
819
879
|
auditEntries.push({
|
|
@@ -822,6 +882,77 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
822
882
|
mode: 'llm',
|
|
823
883
|
});
|
|
824
884
|
};
|
|
885
|
+
const assignRuntimeConfigurable = (runtimeLike) => {
|
|
886
|
+
const configurable = normalizeConfigurable(runtimeLike?.configurable);
|
|
887
|
+
if (!configurable) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
if (configurable.thread_id && configurable.executionId) {
|
|
891
|
+
runtimeConfigurable = configurable;
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
const captureLlmOutputTrace = (trace) => {
|
|
895
|
+
for (const method of trace.methodAttempts) {
|
|
896
|
+
if (!methodAttempts.includes(method)) {
|
|
897
|
+
methodAttempts.push(method);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
resolvedOutputMethod = trace.resolvedOutputMethod;
|
|
901
|
+
fallbackTriggered = fallbackTriggered || trace.fallbackTriggered;
|
|
902
|
+
};
|
|
903
|
+
const buildAuditSnapshot = () => {
|
|
904
|
+
const summary = {
|
|
905
|
+
total: auditEntries.length,
|
|
906
|
+
matched: auditEntries.filter((entry) => entry.matched).length,
|
|
907
|
+
blocked: auditEntries.filter((entry) => entry.action === 'block').length,
|
|
908
|
+
rewritten: auditEntries.filter((entry) => entry.action === 'rewrite').length,
|
|
909
|
+
errorPolicyTriggered: auditEntries.filter((entry) => entry.errorPolicyTriggered).length,
|
|
910
|
+
};
|
|
911
|
+
return {
|
|
912
|
+
mode: 'llm',
|
|
913
|
+
finalAction,
|
|
914
|
+
records: auditEntries,
|
|
915
|
+
summary,
|
|
916
|
+
llmOutput: resolvedLlmConfig
|
|
917
|
+
? {
|
|
918
|
+
requestedOutputMethod: resolvedLlmConfig.outputMethod,
|
|
919
|
+
resolvedOutputMethod,
|
|
920
|
+
methodAttempts,
|
|
921
|
+
fallbackTriggered,
|
|
922
|
+
}
|
|
923
|
+
: undefined,
|
|
924
|
+
};
|
|
925
|
+
};
|
|
926
|
+
const persistAuditSnapshot = async () => {
|
|
927
|
+
const configurable = runtimeConfigurable;
|
|
928
|
+
if (!configurable?.thread_id || !configurable.executionId || !this.commandBus) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
const { thread_id, checkpoint_ns, checkpoint_id, subscriber, executionId } = configurable;
|
|
932
|
+
const snapshot = buildAuditSnapshot();
|
|
933
|
+
await this.commandBus.execute(new WrapWorkflowNodeExecutionCommand(async () => {
|
|
934
|
+
return {
|
|
935
|
+
state: snapshot,
|
|
936
|
+
output: snapshot,
|
|
937
|
+
};
|
|
938
|
+
}, {
|
|
939
|
+
execution: {
|
|
940
|
+
category: 'workflow',
|
|
941
|
+
type: 'middleware',
|
|
942
|
+
title: `${context.node.title} Audit`,
|
|
943
|
+
inputs: {
|
|
944
|
+
mode: snapshot.mode,
|
|
945
|
+
total: snapshot.summary.total,
|
|
946
|
+
},
|
|
947
|
+
parentId: executionId,
|
|
948
|
+
threadId: thread_id,
|
|
949
|
+
checkpointNs: checkpoint_ns,
|
|
950
|
+
checkpointId: checkpoint_id,
|
|
951
|
+
agentKey: context.node.key,
|
|
952
|
+
},
|
|
953
|
+
subscriber,
|
|
954
|
+
}));
|
|
955
|
+
};
|
|
825
956
|
const buildEvaluationMessages = (phase, text, llmConfig) => {
|
|
826
957
|
return [
|
|
827
958
|
{ role: 'system', content: llmConfig.systemPrompt },
|
|
@@ -836,14 +967,49 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
836
967
|
const invokeAndTrack = async (phase, text, runtime, llmConfig) => {
|
|
837
968
|
const invokeCore = async () => {
|
|
838
969
|
const messages = buildEvaluationMessages(phase, text, llmConfig);
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
970
|
+
const model = await ensureModel();
|
|
971
|
+
const candidates = buildOutputMethodCandidates(llmConfig.outputMethod);
|
|
972
|
+
const attempts = [];
|
|
973
|
+
for (const method of candidates) {
|
|
974
|
+
attempts.push(method);
|
|
975
|
+
try {
|
|
976
|
+
const structuredModel = await ensureStructuredModel(method);
|
|
977
|
+
if (!structuredModel) {
|
|
978
|
+
throw new Error(`Structured output is not available for method: ${method}`);
|
|
979
|
+
}
|
|
980
|
+
const raw = await withTimeout(structuredModel.invoke(messages), llmConfig.timeoutMs);
|
|
981
|
+
return {
|
|
982
|
+
raw,
|
|
983
|
+
trace: {
|
|
984
|
+
requestedOutputMethod: llmConfig.outputMethod,
|
|
985
|
+
resolvedOutputMethod: method,
|
|
986
|
+
methodAttempts: attempts,
|
|
987
|
+
fallbackTriggered: method !== llmConfig.outputMethod || attempts.length > 1,
|
|
988
|
+
},
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
catch (error) {
|
|
992
|
+
if (isUnsupportedStructuredOutputError(error)) {
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
throw error;
|
|
996
|
+
}
|
|
842
997
|
}
|
|
843
|
-
|
|
998
|
+
attempts.push('plainText');
|
|
999
|
+
const raw = await withTimeout(model.invoke(messages), llmConfig.timeoutMs);
|
|
1000
|
+
return {
|
|
1001
|
+
raw,
|
|
1002
|
+
trace: {
|
|
1003
|
+
requestedOutputMethod: llmConfig.outputMethod,
|
|
1004
|
+
resolvedOutputMethod: 'plainText',
|
|
1005
|
+
methodAttempts: attempts,
|
|
1006
|
+
fallbackTriggered: true,
|
|
1007
|
+
},
|
|
1008
|
+
};
|
|
844
1009
|
};
|
|
845
1010
|
const parseCore = async () => {
|
|
846
|
-
const raw = await invokeCore();
|
|
1011
|
+
const { raw, trace } = await invokeCore();
|
|
1012
|
+
captureLlmOutputTrace(trace);
|
|
847
1013
|
return parseLlmDecision(raw, llmConfig.rewriteFallbackText);
|
|
848
1014
|
};
|
|
849
1015
|
const configurable = (runtime?.configurable ?? {});
|
|
@@ -878,28 +1044,18 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
878
1044
|
};
|
|
879
1045
|
const resolveOnErrorDecision = (llmConfig, error) => {
|
|
880
1046
|
const reason = `llm-error:${error instanceof Error ? error.message : String(error)}`;
|
|
881
|
-
if (llmConfig.onLlmError === 'block') {
|
|
882
|
-
return {
|
|
883
|
-
matched: true,
|
|
884
|
-
action: 'block',
|
|
885
|
-
reason,
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
1047
|
return {
|
|
889
1048
|
matched: true,
|
|
890
1049
|
action: 'rewrite',
|
|
891
|
-
replacementText: llmConfig.
|
|
1050
|
+
replacementText: llmConfig.legacyErrorRewriteText ?? llmConfig.rewriteFallbackText,
|
|
892
1051
|
reason,
|
|
893
1052
|
};
|
|
894
1053
|
};
|
|
895
|
-
const resolveBlockMessage = (llmConfig, phase) => {
|
|
896
|
-
return llmConfig.blockMessage ??
|
|
897
|
-
(phase === 'input' ? DEFAULT_INPUT_BLOCK_MESSAGE : DEFAULT_OUTPUT_BLOCK_MESSAGE);
|
|
898
|
-
};
|
|
899
1054
|
return {
|
|
900
1055
|
name: SENSITIVE_FILTER_MIDDLEWARE_NAME,
|
|
901
1056
|
beforeAgent: async (state, runtime) => {
|
|
902
1057
|
resetRunState();
|
|
1058
|
+
assignRuntimeConfigurable(runtime);
|
|
903
1059
|
const llmConfig = getLlmConfig();
|
|
904
1060
|
if (!modeIncludesScope(llmConfig.scope, 'input')) {
|
|
905
1061
|
pushAudit({
|
|
@@ -942,20 +1098,13 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
942
1098
|
if (!decision.matched || !decision.action) {
|
|
943
1099
|
return undefined;
|
|
944
1100
|
}
|
|
945
|
-
if (decision.action === 'block') {
|
|
946
|
-
finalAction = 'block';
|
|
947
|
-
inputBlockedMessage = resolveBlockMessage(llmConfig, 'input');
|
|
948
|
-
return undefined;
|
|
949
|
-
}
|
|
950
1101
|
finalAction = 'rewrite';
|
|
951
1102
|
pendingInputRewrite = toNonEmptyString(decision.replacementText) ?? llmConfig.rewriteFallbackText;
|
|
952
1103
|
return undefined;
|
|
953
1104
|
},
|
|
954
1105
|
wrapModelCall: async (request, handler) => {
|
|
1106
|
+
assignRuntimeConfigurable(request?.runtime);
|
|
955
1107
|
const llmConfig = getLlmConfig();
|
|
956
|
-
if (inputBlockedMessage) {
|
|
957
|
-
return new AIMessage(inputBlockedMessage);
|
|
958
|
-
}
|
|
959
1108
|
const modelRequest = pendingInputRewrite ? rewriteModelRequestInput(request, pendingInputRewrite) : request;
|
|
960
1109
|
pendingInputRewrite = null;
|
|
961
1110
|
const response = await handler(modelRequest);
|
|
@@ -1000,19 +1149,11 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
|
|
|
1000
1149
|
if (!decision.matched || !decision.action) {
|
|
1001
1150
|
return response;
|
|
1002
1151
|
}
|
|
1003
|
-
if (decision.action === 'block') {
|
|
1004
|
-
finalAction = 'block';
|
|
1005
|
-
return replaceModelResponseText(response, resolveBlockMessage(llmConfig, 'output'));
|
|
1006
|
-
}
|
|
1007
1152
|
finalAction = 'rewrite';
|
|
1008
1153
|
return replaceModelResponseText(response, toNonEmptyString(decision.replacementText) ?? llmConfig.rewriteFallbackText);
|
|
1009
1154
|
},
|
|
1010
1155
|
afterAgent: async () => {
|
|
1011
|
-
|
|
1012
|
-
mode: 'llm',
|
|
1013
|
-
finalAction,
|
|
1014
|
-
records: auditEntries,
|
|
1015
|
-
}, null, 2));
|
|
1156
|
+
await persistAuditSnapshot();
|
|
1016
1157
|
return undefined;
|
|
1017
1158
|
},
|
|
1018
1159
|
};
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export type LlmErrorAction = 'block' | 'rewrite';
|
|
|
26
26
|
export type LlmFilterConfig = {
|
|
27
27
|
model?: ICopilotModel;
|
|
28
28
|
scope?: LlmScope;
|
|
29
|
+
rulePrompt?: string;
|
|
29
30
|
systemPrompt?: string;
|
|
30
31
|
outputMethod?: LlmOutputMethod;
|
|
31
32
|
onLlmError?: LlmErrorAction;
|
|
@@ -134,6 +135,7 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
|
|
|
134
135
|
llm: z.ZodDefault<z.ZodNullable<z.ZodOptional<z.ZodObject<{
|
|
135
136
|
model: z.ZodNullable<z.ZodOptional<z.ZodType<ICopilotModel, z.ZodTypeDef, ICopilotModel>>>;
|
|
136
137
|
scope: z.ZodNullable<z.ZodOptional<z.ZodEnum<["input", "output", "both"]>>>;
|
|
138
|
+
rulePrompt: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
137
139
|
systemPrompt: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
138
140
|
outputMethod: z.ZodDefault<z.ZodOptional<z.ZodEnum<["functionCalling", "jsonMode", "jsonSchema"]>>>;
|
|
139
141
|
onLlmError: z.ZodNullable<z.ZodOptional<z.ZodEnum<["block", "rewrite"]>>>;
|
|
@@ -144,6 +146,7 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
|
|
|
144
146
|
}, "strip", z.ZodTypeAny, {
|
|
145
147
|
model?: ICopilotModel;
|
|
146
148
|
scope?: "input" | "output" | "both";
|
|
149
|
+
rulePrompt?: string;
|
|
147
150
|
systemPrompt?: string;
|
|
148
151
|
outputMethod?: "functionCalling" | "jsonMode" | "jsonSchema";
|
|
149
152
|
onLlmError?: "block" | "rewrite";
|
|
@@ -154,6 +157,7 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
|
|
|
154
157
|
}, {
|
|
155
158
|
model?: ICopilotModel;
|
|
156
159
|
scope?: "input" | "output" | "both";
|
|
160
|
+
rulePrompt?: string;
|
|
157
161
|
systemPrompt?: string;
|
|
158
162
|
outputMethod?: "functionCalling" | "jsonMode" | "jsonSchema";
|
|
159
163
|
onLlmError?: "block" | "rewrite";
|
|
@@ -171,6 +175,7 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
|
|
|
171
175
|
llm?: {
|
|
172
176
|
model?: ICopilotModel;
|
|
173
177
|
scope?: "input" | "output" | "both";
|
|
178
|
+
rulePrompt?: string;
|
|
174
179
|
systemPrompt?: string;
|
|
175
180
|
outputMethod?: "functionCalling" | "jsonMode" | "jsonSchema";
|
|
176
181
|
onLlmError?: "block" | "rewrite";
|
|
@@ -188,6 +193,7 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
|
|
|
188
193
|
llm?: {
|
|
189
194
|
model?: ICopilotModel;
|
|
190
195
|
scope?: "input" | "output" | "both";
|
|
196
|
+
rulePrompt?: string;
|
|
191
197
|
systemPrompt?: string;
|
|
192
198
|
outputMethod?: "functionCalling" | "jsonMode" | "jsonSchema";
|
|
193
199
|
onLlmError?: "block" | "rewrite";
|
|
@@ -201,9 +207,9 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
|
|
|
201
207
|
generalPack?: unknown;
|
|
202
208
|
caseSensitive?: unknown;
|
|
203
209
|
}>]>;
|
|
204
|
-
export declare const llmDecisionSchema: z.
|
|
210
|
+
export declare const llmDecisionSchema: z.ZodObject<{
|
|
205
211
|
matched: z.ZodBoolean;
|
|
206
|
-
action: z.ZodOptional<z.ZodEnum<["block", "rewrite"]
|
|
212
|
+
action: z.ZodNullable<z.ZodOptional<z.ZodEnum<["block", "rewrite"]>>>;
|
|
207
213
|
replacementText: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
208
214
|
reason: z.ZodNullable<z.ZodOptional<z.ZodString>>;
|
|
209
215
|
categories: z.ZodNullable<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
|
|
@@ -219,18 +225,6 @@ export declare const llmDecisionSchema: z.ZodEffects<z.ZodObject<{
|
|
|
219
225
|
matched?: boolean;
|
|
220
226
|
reason?: string;
|
|
221
227
|
categories?: string[];
|
|
222
|
-
}>, {
|
|
223
|
-
action?: "block" | "rewrite";
|
|
224
|
-
replacementText?: string;
|
|
225
|
-
matched?: boolean;
|
|
226
|
-
reason?: string;
|
|
227
|
-
categories?: string[];
|
|
228
|
-
}, {
|
|
229
|
-
action?: "block" | "rewrite";
|
|
230
|
-
replacementText?: string;
|
|
231
|
-
matched?: boolean;
|
|
232
|
-
reason?: string;
|
|
233
|
-
categories?: string[];
|
|
234
228
|
}>;
|
|
235
229
|
export declare function resolveGeneralPackRules(config?: GeneralPackConfig): SensitiveRule[];
|
|
236
230
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/lib/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAA;AAE1B,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,SAAS,GAAG,OAAO,CAAA;IACzB,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IAClC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAA;IAC3B,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,CAAA;IAC5C,WAAW,CAAC,EAAE,iBAAiB,CAAA;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;AAClD,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,UAAU,GAAG,YAAY,CAAA;AAC3E,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,CAAA;AAEhD,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,KAAK,CAAC,EAAE,QAAQ,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/types.js
CHANGED
|
@@ -34,8 +34,9 @@ const llmConfigSchema = z
|
|
|
34
34
|
.object({
|
|
35
35
|
model: z.custom().optional().nullable(),
|
|
36
36
|
scope: z.enum(['input', 'output', 'both']).optional().nullable(),
|
|
37
|
+
rulePrompt: z.string().optional().nullable(),
|
|
37
38
|
systemPrompt: z.string().optional().nullable(),
|
|
38
|
-
outputMethod: z.enum(['functionCalling', 'jsonMode', 'jsonSchema']).optional().default('
|
|
39
|
+
outputMethod: z.enum(['functionCalling', 'jsonMode', 'jsonSchema']).optional().default('jsonMode'),
|
|
39
40
|
onLlmError: z.enum(['block', 'rewrite']).optional().nullable(),
|
|
40
41
|
errorRewriteText: z.string().optional().nullable(),
|
|
41
42
|
blockMessage: z.string().optional().nullable(),
|
|
@@ -62,19 +63,10 @@ export const sensitiveFilterConfigSchema = z.union([ruleModeConfigSchema, llmMod
|
|
|
62
63
|
export const llmDecisionSchema = z
|
|
63
64
|
.object({
|
|
64
65
|
matched: z.boolean(),
|
|
65
|
-
action: z.enum(['block', 'rewrite']).optional(),
|
|
66
|
+
action: z.enum(['block', 'rewrite']).optional().nullable(),
|
|
66
67
|
replacementText: z.string().optional().nullable(),
|
|
67
68
|
reason: z.string().optional().nullable(),
|
|
68
69
|
categories: z.array(z.string()).optional().nullable(),
|
|
69
|
-
})
|
|
70
|
-
.superRefine((data, ctx) => {
|
|
71
|
-
if (data.matched && !data.action) {
|
|
72
|
-
ctx.addIssue({
|
|
73
|
-
code: z.ZodIssueCode.custom,
|
|
74
|
-
path: ['action'],
|
|
75
|
-
message: 'action is required when matched is true',
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
70
|
});
|
|
79
71
|
function escapeRegExp(value) {
|
|
80
72
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|