plugin-sensitive-filter-xr 0.0.4 → 0.0.6

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.
@@ -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;AAwanB,qBAEa,yBAA0B,YAAW,wBAAwB,CAAC,qBAAqB,CAAC;IAE/F,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;IAEvC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CA6SlC;IAEK,gBAAgB,CACpB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,eAAe,CAAC;IAa3B,OAAO,CAAC,wBAAwB;IA+KhC,OAAO,CAAC,uBAAuB;CA2ShC;AAED,YAAY,EAAE,qBAAqB,EAAE,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;AAofnB,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;IA+OhC,OAAO,CAAC,uBAAuB;CAwahC;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 systemPrompt = toNonEmptyString(config.systemPrompt);
274
- const onLlmError = toNonEmptyString(config.onLlmError);
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
- : 'jsonSchema';
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
- systemPrompt,
289
+ rulePrompt,
290
+ systemPrompt: buildInternalLlmPrompt(rulePrompt),
293
291
  outputMethod,
294
- onLlmError,
295
- errorRewriteText,
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,54 @@ 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
+ }
350
+ function isMissingWrapWorkflowHandlerError(error) {
351
+ const message = getErrorText(error).toLowerCase();
352
+ return message.includes('no handler found') && message.includes('wrapworkflownodeexecutioncommand');
353
+ }
354
+ async function runWithWrapWorkflowFallback(runTracked, runFallback) {
355
+ try {
356
+ return await runTracked();
357
+ }
358
+ catch (error) {
359
+ if (isMissingWrapWorkflowHandlerError(error)) {
360
+ return runFallback();
361
+ }
362
+ throw error;
363
+ }
364
+ }
320
365
  let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
321
366
  constructor() {
322
367
  this.meta = {
@@ -534,19 +579,27 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
534
579
  },
535
580
  },
536
581
  },
537
- systemPrompt: {
582
+ rulePrompt: {
538
583
  type: 'string',
539
- title: { en_US: 'System Prompt', zh_Hans: '判定提示词' },
584
+ title: { en_US: 'Rule Prompt', zh_Hans: '审核规则说明' },
585
+ description: {
586
+ en_US: 'Describe your moderation rules in natural language. No JSON format is required.',
587
+ zh_Hans: '用自然语言描述审核规则,无需手写 JSON 格式。',
588
+ },
540
589
  'x-ui': {
541
590
  component: 'textarea',
542
591
  span: 2,
592
+ placeholder: {
593
+ en_US: 'e.g. Rewrite violent/privacy-sensitive content into a safe neutral response.',
594
+ zh_Hans: '例如:涉及暴力或隐私泄露内容时,改写为安全中性表达。',
595
+ },
543
596
  },
544
597
  },
545
598
  outputMethod: {
546
599
  type: 'string',
547
600
  title: { en_US: 'Output Method', zh_Hans: '结构化输出方式' },
548
601
  enum: ['functionCalling', 'jsonMode', 'jsonSchema'],
549
- default: 'jsonSchema',
602
+ default: 'jsonMode',
550
603
  'x-ui': {
551
604
  enumLabels: {
552
605
  functionCalling: { en_US: 'Function Calling', zh_Hans: '函数调用' },
@@ -555,25 +608,6 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
555
608
  },
556
609
  },
557
610
  },
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
611
  rewriteFallbackText: {
578
612
  type: 'string',
579
613
  title: { en_US: 'Rewrite Fallback Text', zh_Hans: '改写兜底文本' },
@@ -583,21 +617,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
583
617
  title: { en_US: 'Timeout (ms)', zh_Hans: '超时毫秒' },
584
618
  },
585
619
  },
586
- required: ['model', 'scope', 'systemPrompt', 'onLlmError'],
587
- allOf: [
588
- {
589
- if: {
590
- properties: {
591
- onLlmError: {
592
- const: 'rewrite',
593
- },
594
- },
595
- },
596
- then: {
597
- required: ['errorRewriteText'],
598
- },
599
- },
600
- ],
620
+ required: ['model', 'scope', 'rulePrompt'],
601
621
  },
602
622
  },
603
623
  required: ['mode'],
@@ -626,9 +646,9 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
626
646
  if (parsed.data.mode === 'llm') {
627
647
  return this.createLlmModeMiddleware(parsed.data, context);
628
648
  }
629
- return this.createRuleModeMiddleware(parsed.data);
649
+ return this.createRuleModeMiddleware(parsed.data, context);
630
650
  }
631
- createRuleModeMiddleware(config) {
651
+ createRuleModeMiddleware(config, context) {
632
652
  const caseSensitive = config.caseSensitive ?? false;
633
653
  const normalize = config.normalize ?? true;
634
654
  const customRules = normalizeRuleDrafts(config.rules ?? []);
@@ -669,6 +689,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
669
689
  let pendingInputRewrite = null;
670
690
  let finalAction = 'pass';
671
691
  let auditEntries = [];
692
+ let runtimeConfigurable = null;
672
693
  const resetRunState = () => {
673
694
  inputBlockedMessage = null;
674
695
  pendingInputRewrite = null;
@@ -682,10 +703,72 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
682
703
  mode: 'rule',
683
704
  });
684
705
  };
706
+ const assignRuntimeConfigurable = (runtimeLike) => {
707
+ const configurable = normalizeConfigurable(runtimeLike?.configurable);
708
+ if (!configurable) {
709
+ return;
710
+ }
711
+ if (configurable.thread_id && configurable.executionId) {
712
+ runtimeConfigurable = configurable;
713
+ }
714
+ };
715
+ const buildAuditSnapshot = () => {
716
+ const summary = {
717
+ total: auditEntries.length,
718
+ matched: auditEntries.filter((entry) => entry.matched).length,
719
+ blocked: auditEntries.filter((entry) => entry.action === 'block').length,
720
+ rewritten: auditEntries.filter((entry) => entry.action === 'rewrite').length,
721
+ errorPolicyTriggered: auditEntries.filter((entry) => entry.errorPolicyTriggered).length,
722
+ };
723
+ return {
724
+ mode: 'rule',
725
+ finalAction,
726
+ records: auditEntries,
727
+ summary,
728
+ };
729
+ };
730
+ const persistAuditSnapshot = async () => {
731
+ const configurable = runtimeConfigurable;
732
+ if (!configurable?.thread_id || !configurable.executionId || !this.commandBus) {
733
+ return;
734
+ }
735
+ const { thread_id, checkpoint_ns, checkpoint_id, subscriber, executionId } = configurable;
736
+ const snapshot = buildAuditSnapshot();
737
+ const writeSnapshot = async () => {
738
+ return {
739
+ state: snapshot,
740
+ output: snapshot,
741
+ };
742
+ };
743
+ await runWithWrapWorkflowFallback(async () => {
744
+ await this.commandBus.execute(new WrapWorkflowNodeExecutionCommand(writeSnapshot, {
745
+ execution: {
746
+ category: 'workflow',
747
+ type: 'middleware',
748
+ title: `${context.node.title} Audit`,
749
+ inputs: {
750
+ mode: snapshot.mode,
751
+ total: snapshot.summary.total,
752
+ },
753
+ parentId: executionId,
754
+ threadId: thread_id,
755
+ checkpointNs: checkpoint_ns,
756
+ checkpointId: checkpoint_id,
757
+ agentKey: context.node.key,
758
+ },
759
+ subscriber,
760
+ }));
761
+ return undefined;
762
+ }, async () => {
763
+ await writeSnapshot();
764
+ return undefined;
765
+ });
766
+ };
685
767
  return {
686
768
  name: SENSITIVE_FILTER_MIDDLEWARE_NAME,
687
769
  beforeAgent: async (state, runtime) => {
688
770
  resetRunState();
771
+ assignRuntimeConfigurable(runtime);
689
772
  if (!hasEffectiveRules) {
690
773
  throw new Error(BUSINESS_RULES_VALIDATION_ERROR);
691
774
  }
@@ -722,6 +805,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
722
805
  return undefined;
723
806
  },
724
807
  wrapModelCall: async (request, handler) => {
808
+ assignRuntimeConfigurable(request?.runtime);
725
809
  if (!hasEffectiveRules) {
726
810
  throw new Error(BUSINESS_RULES_VALIDATION_ERROR);
727
811
  }
@@ -762,11 +846,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
762
846
  return replaceModelResponseText(response, rewrittenOutput);
763
847
  },
764
848
  afterAgent: async () => {
765
- console.log('[SensitiveFilterMiddleware][audit]', JSON.stringify({
766
- mode: 'rule',
767
- finalAction,
768
- records: auditEntries,
769
- }, null, 2));
849
+ await persistAuditSnapshot();
770
850
  return undefined;
771
851
  },
772
852
  };
@@ -775,7 +855,7 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
775
855
  const llmDraftConfig = config.llm;
776
856
  let resolvedLlmConfig = null;
777
857
  let modelPromise = null;
778
- let structuredModelPromise = null;
858
+ const structuredModelPromises = new Map();
779
859
  const getLlmConfig = () => {
780
860
  if (!resolvedLlmConfig) {
781
861
  resolvedLlmConfig = resolveRuntimeLlmConfig(llmDraftConfig);
@@ -786,34 +866,36 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
786
866
  const llmConfig = getLlmConfig();
787
867
  if (!modelPromise) {
788
868
  modelPromise = this.commandBus.execute(new CreateModelClientCommand(llmConfig.model, {
789
- usageCallback: (event) => {
790
- console.log('[SensitiveFilterMiddleware][LLM] Model Usage:', event);
791
- },
869
+ usageCallback: () => { },
792
870
  }));
793
871
  }
794
872
  return modelPromise;
795
873
  };
796
- const ensureStructuredModel = async () => {
797
- const llmConfig = getLlmConfig();
798
- if (!structuredModelPromise) {
799
- structuredModelPromise = (async () => {
874
+ const ensureStructuredModel = async (method) => {
875
+ if (!structuredModelPromises.has(method)) {
876
+ structuredModelPromises.set(method, (async () => {
800
877
  const model = await ensureModel();
801
878
  return model.withStructuredOutput?.(llmDecisionSchema, {
802
- method: llmConfig.outputMethod,
879
+ method,
803
880
  }) ?? null;
804
- })();
881
+ })());
805
882
  }
806
- return structuredModelPromise;
883
+ return structuredModelPromises.get(method);
807
884
  };
808
- let inputBlockedMessage = null;
809
885
  let pendingInputRewrite = null;
810
886
  let finalAction = 'pass';
811
887
  let auditEntries = [];
888
+ let runtimeConfigurable = null;
889
+ let resolvedOutputMethod;
890
+ let fallbackTriggered = false;
891
+ let methodAttempts = [];
812
892
  const resetRunState = () => {
813
- inputBlockedMessage = null;
814
893
  pendingInputRewrite = null;
815
894
  finalAction = 'pass';
816
895
  auditEntries = [];
896
+ resolvedOutputMethod = undefined;
897
+ fallbackTriggered = false;
898
+ methodAttempts = [];
817
899
  };
818
900
  const pushAudit = (entry) => {
819
901
  auditEntries.push({
@@ -822,6 +904,84 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
822
904
  mode: 'llm',
823
905
  });
824
906
  };
907
+ const assignRuntimeConfigurable = (runtimeLike) => {
908
+ const configurable = normalizeConfigurable(runtimeLike?.configurable);
909
+ if (!configurable) {
910
+ return;
911
+ }
912
+ if (configurable.thread_id && configurable.executionId) {
913
+ runtimeConfigurable = configurable;
914
+ }
915
+ };
916
+ const captureLlmOutputTrace = (trace) => {
917
+ for (const method of trace.methodAttempts) {
918
+ if (!methodAttempts.includes(method)) {
919
+ methodAttempts.push(method);
920
+ }
921
+ }
922
+ resolvedOutputMethod = trace.resolvedOutputMethod;
923
+ fallbackTriggered = fallbackTriggered || trace.fallbackTriggered;
924
+ };
925
+ const buildAuditSnapshot = () => {
926
+ const summary = {
927
+ total: auditEntries.length,
928
+ matched: auditEntries.filter((entry) => entry.matched).length,
929
+ blocked: auditEntries.filter((entry) => entry.action === 'block').length,
930
+ rewritten: auditEntries.filter((entry) => entry.action === 'rewrite').length,
931
+ errorPolicyTriggered: auditEntries.filter((entry) => entry.errorPolicyTriggered).length,
932
+ };
933
+ return {
934
+ mode: 'llm',
935
+ finalAction,
936
+ records: auditEntries,
937
+ summary,
938
+ llmOutput: resolvedLlmConfig
939
+ ? {
940
+ requestedOutputMethod: resolvedLlmConfig.outputMethod,
941
+ resolvedOutputMethod,
942
+ methodAttempts,
943
+ fallbackTriggered,
944
+ }
945
+ : undefined,
946
+ };
947
+ };
948
+ const persistAuditSnapshot = async () => {
949
+ const configurable = runtimeConfigurable;
950
+ if (!configurable?.thread_id || !configurable.executionId || !this.commandBus) {
951
+ return;
952
+ }
953
+ const { thread_id, checkpoint_ns, checkpoint_id, subscriber, executionId } = configurable;
954
+ const snapshot = buildAuditSnapshot();
955
+ const writeSnapshot = async () => {
956
+ return {
957
+ state: snapshot,
958
+ output: snapshot,
959
+ };
960
+ };
961
+ await runWithWrapWorkflowFallback(async () => {
962
+ await this.commandBus.execute(new WrapWorkflowNodeExecutionCommand(writeSnapshot, {
963
+ execution: {
964
+ category: 'workflow',
965
+ type: 'middleware',
966
+ title: `${context.node.title} Audit`,
967
+ inputs: {
968
+ mode: snapshot.mode,
969
+ total: snapshot.summary.total,
970
+ },
971
+ parentId: executionId,
972
+ threadId: thread_id,
973
+ checkpointNs: checkpoint_ns,
974
+ checkpointId: checkpoint_id,
975
+ agentKey: context.node.key,
976
+ },
977
+ subscriber,
978
+ }));
979
+ return undefined;
980
+ }, async () => {
981
+ await writeSnapshot();
982
+ return undefined;
983
+ });
984
+ };
825
985
  const buildEvaluationMessages = (phase, text, llmConfig) => {
826
986
  return [
827
987
  { role: 'system', content: llmConfig.systemPrompt },
@@ -836,14 +996,49 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
836
996
  const invokeAndTrack = async (phase, text, runtime, llmConfig) => {
837
997
  const invokeCore = async () => {
838
998
  const messages = buildEvaluationMessages(phase, text, llmConfig);
839
- const [model, structuredModel] = await Promise.all([ensureModel(), ensureStructuredModel()]);
840
- if (structuredModel) {
841
- return withTimeout(structuredModel.invoke(messages), llmConfig.timeoutMs);
999
+ const model = await ensureModel();
1000
+ const candidates = buildOutputMethodCandidates(llmConfig.outputMethod);
1001
+ const attempts = [];
1002
+ for (const method of candidates) {
1003
+ attempts.push(method);
1004
+ try {
1005
+ const structuredModel = await ensureStructuredModel(method);
1006
+ if (!structuredModel) {
1007
+ throw new Error(`Structured output is not available for method: ${method}`);
1008
+ }
1009
+ const raw = await withTimeout(structuredModel.invoke(messages), llmConfig.timeoutMs);
1010
+ return {
1011
+ raw,
1012
+ trace: {
1013
+ requestedOutputMethod: llmConfig.outputMethod,
1014
+ resolvedOutputMethod: method,
1015
+ methodAttempts: attempts,
1016
+ fallbackTriggered: method !== llmConfig.outputMethod || attempts.length > 1,
1017
+ },
1018
+ };
1019
+ }
1020
+ catch (error) {
1021
+ if (isUnsupportedStructuredOutputError(error)) {
1022
+ continue;
1023
+ }
1024
+ throw error;
1025
+ }
842
1026
  }
843
- return withTimeout(model.invoke(messages), llmConfig.timeoutMs);
1027
+ attempts.push('plainText');
1028
+ const raw = await withTimeout(model.invoke(messages), llmConfig.timeoutMs);
1029
+ return {
1030
+ raw,
1031
+ trace: {
1032
+ requestedOutputMethod: llmConfig.outputMethod,
1033
+ resolvedOutputMethod: 'plainText',
1034
+ methodAttempts: attempts,
1035
+ fallbackTriggered: true,
1036
+ },
1037
+ };
844
1038
  };
845
1039
  const parseCore = async () => {
846
- const raw = await invokeCore();
1040
+ const { raw, trace } = await invokeCore();
1041
+ captureLlmOutputTrace(trace);
847
1042
  return parseLlmDecision(raw, llmConfig.rewriteFallbackText);
848
1043
  };
849
1044
  const configurable = (runtime?.configurable ?? {});
@@ -851,55 +1046,56 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
851
1046
  if (!thread_id || !executionId) {
852
1047
  return parseCore();
853
1048
  }
854
- const tracked = await this.commandBus.execute(new WrapWorkflowNodeExecutionCommand(async () => {
855
- const decision = await parseCore();
856
- return {
857
- state: decision,
858
- output: decision,
859
- };
860
- }, {
861
- execution: {
862
- category: 'workflow',
863
- type: 'middleware',
864
- inputs: {
865
- phase,
866
- text,
1049
+ let trackedDecision = null;
1050
+ await runWithWrapWorkflowFallback(async () => {
1051
+ await this.commandBus.execute(new WrapWorkflowNodeExecutionCommand(async () => {
1052
+ const decision = await parseCore();
1053
+ trackedDecision = decision;
1054
+ return {
1055
+ state: decision,
1056
+ output: decision,
1057
+ };
1058
+ }, {
1059
+ execution: {
1060
+ category: 'workflow',
1061
+ type: 'middleware',
1062
+ inputs: {
1063
+ phase,
1064
+ text,
1065
+ },
1066
+ parentId: executionId,
1067
+ threadId: thread_id,
1068
+ checkpointNs: checkpoint_ns,
1069
+ checkpointId: checkpoint_id,
1070
+ agentKey: context.node.key,
1071
+ title: context.node.title,
867
1072
  },
868
- parentId: executionId,
869
- threadId: thread_id,
870
- checkpointNs: checkpoint_ns,
871
- checkpointId: checkpoint_id,
872
- agentKey: context.node.key,
873
- title: context.node.title,
874
- },
875
- subscriber,
876
- }));
877
- return tracked;
1073
+ subscriber,
1074
+ }));
1075
+ return undefined;
1076
+ }, async () => {
1077
+ trackedDecision = await parseCore();
1078
+ return undefined;
1079
+ });
1080
+ if (!trackedDecision) {
1081
+ throw new Error('LLM decision tracking failed: no decision resolved');
1082
+ }
1083
+ return trackedDecision;
878
1084
  };
879
1085
  const resolveOnErrorDecision = (llmConfig, error) => {
880
1086
  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
1087
  return {
889
1088
  matched: true,
890
1089
  action: 'rewrite',
891
- replacementText: llmConfig.errorRewriteText ?? llmConfig.rewriteFallbackText,
1090
+ replacementText: llmConfig.legacyErrorRewriteText ?? llmConfig.rewriteFallbackText,
892
1091
  reason,
893
1092
  };
894
1093
  };
895
- const resolveBlockMessage = (llmConfig, phase) => {
896
- return llmConfig.blockMessage ??
897
- (phase === 'input' ? DEFAULT_INPUT_BLOCK_MESSAGE : DEFAULT_OUTPUT_BLOCK_MESSAGE);
898
- };
899
1094
  return {
900
1095
  name: SENSITIVE_FILTER_MIDDLEWARE_NAME,
901
1096
  beforeAgent: async (state, runtime) => {
902
1097
  resetRunState();
1098
+ assignRuntimeConfigurable(runtime);
903
1099
  const llmConfig = getLlmConfig();
904
1100
  if (!modeIncludesScope(llmConfig.scope, 'input')) {
905
1101
  pushAudit({
@@ -942,20 +1138,13 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
942
1138
  if (!decision.matched || !decision.action) {
943
1139
  return undefined;
944
1140
  }
945
- if (decision.action === 'block') {
946
- finalAction = 'block';
947
- inputBlockedMessage = resolveBlockMessage(llmConfig, 'input');
948
- return undefined;
949
- }
950
1141
  finalAction = 'rewrite';
951
1142
  pendingInputRewrite = toNonEmptyString(decision.replacementText) ?? llmConfig.rewriteFallbackText;
952
1143
  return undefined;
953
1144
  },
954
1145
  wrapModelCall: async (request, handler) => {
1146
+ assignRuntimeConfigurable(request?.runtime);
955
1147
  const llmConfig = getLlmConfig();
956
- if (inputBlockedMessage) {
957
- return new AIMessage(inputBlockedMessage);
958
- }
959
1148
  const modelRequest = pendingInputRewrite ? rewriteModelRequestInput(request, pendingInputRewrite) : request;
960
1149
  pendingInputRewrite = null;
961
1150
  const response = await handler(modelRequest);
@@ -1000,19 +1189,11 @@ let SensitiveFilterMiddleware = class SensitiveFilterMiddleware {
1000
1189
  if (!decision.matched || !decision.action) {
1001
1190
  return response;
1002
1191
  }
1003
- if (decision.action === 'block') {
1004
- finalAction = 'block';
1005
- return replaceModelResponseText(response, resolveBlockMessage(llmConfig, 'output'));
1006
- }
1007
1192
  finalAction = 'rewrite';
1008
1193
  return replaceModelResponseText(response, toNonEmptyString(decision.replacementText) ?? llmConfig.rewriteFallbackText);
1009
1194
  },
1010
1195
  afterAgent: async () => {
1011
- console.log('[SensitiveFilterMiddleware][audit]', JSON.stringify({
1012
- mode: 'llm',
1013
- finalAction,
1014
- records: auditEntries,
1015
- }, null, 2));
1196
+ await persistAuditSnapshot();
1016
1197
  return undefined;
1017
1198
  },
1018
1199
  };
@@ -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;
@@ -64,17 +65,17 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
64
65
  action: z.ZodNullable<z.ZodOptional<z.ZodEnum<["block", "rewrite"]>>>;
65
66
  replacementText: z.ZodNullable<z.ZodOptional<z.ZodString>>;
66
67
  }, "strip", z.ZodTypeAny, {
67
- type?: "keyword" | "regex";
68
68
  id?: string;
69
69
  pattern?: string;
70
+ type?: "keyword" | "regex";
70
71
  scope?: "input" | "output" | "both";
71
72
  severity?: "high" | "medium";
72
73
  action?: "block" | "rewrite";
73
74
  replacementText?: string;
74
75
  }, {
75
- type?: "keyword" | "regex";
76
76
  id?: string;
77
77
  pattern?: string;
78
+ type?: "keyword" | "regex";
78
79
  scope?: "input" | "output" | "both";
79
80
  severity?: "high" | "medium";
80
81
  action?: "block" | "rewrite";
@@ -94,13 +95,11 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
94
95
  normalize: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
95
96
  llm: z.ZodOptional<z.ZodUnknown>;
96
97
  }, "strip", z.ZodTypeAny, {
97
- normalize?: boolean;
98
- llm?: unknown;
99
98
  mode?: "rule";
100
99
  rules?: {
101
- type?: "keyword" | "regex";
102
100
  id?: string;
103
101
  pattern?: string;
102
+ type?: "keyword" | "regex";
104
103
  scope?: "input" | "output" | "both";
105
104
  severity?: "high" | "medium";
106
105
  action?: "block" | "rewrite";
@@ -111,14 +110,14 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
111
110
  profile?: "strict" | "balanced";
112
111
  };
113
112
  caseSensitive?: boolean;
114
- }, {
115
113
  normalize?: boolean;
116
114
  llm?: unknown;
115
+ }, {
117
116
  mode?: "rule";
118
117
  rules?: {
119
- type?: "keyword" | "regex";
120
118
  id?: string;
121
119
  pattern?: string;
120
+ type?: "keyword" | "regex";
122
121
  scope?: "input" | "output" | "both";
123
122
  severity?: "high" | "medium";
124
123
  action?: "block" | "rewrite";
@@ -129,11 +128,14 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
129
128
  profile?: "strict" | "balanced";
130
129
  };
131
130
  caseSensitive?: boolean;
131
+ normalize?: boolean;
132
+ llm?: unknown;
132
133
  }>, z.ZodObject<{
133
134
  mode: z.ZodLiteral<"llm">;
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"]>>>;
@@ -142,8 +144,9 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
142
144
  rewriteFallbackText: z.ZodNullable<z.ZodOptional<z.ZodString>>;
143
145
  timeoutMs: z.ZodNullable<z.ZodOptional<z.ZodNumber>>;
144
146
  }, "strip", z.ZodTypeAny, {
145
- model?: ICopilotModel;
146
147
  scope?: "input" | "output" | "both";
148
+ model?: ICopilotModel;
149
+ rulePrompt?: string;
147
150
  systemPrompt?: string;
148
151
  outputMethod?: "functionCalling" | "jsonMode" | "jsonSchema";
149
152
  onLlmError?: "block" | "rewrite";
@@ -152,8 +155,9 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
152
155
  rewriteFallbackText?: string;
153
156
  timeoutMs?: number;
154
157
  }, {
155
- model?: ICopilotModel;
156
158
  scope?: "input" | "output" | "both";
159
+ model?: ICopilotModel;
160
+ rulePrompt?: string;
157
161
  systemPrompt?: string;
158
162
  outputMethod?: "functionCalling" | "jsonMode" | "jsonSchema";
159
163
  onLlmError?: "block" | "rewrite";
@@ -167,10 +171,15 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
167
171
  caseSensitive: z.ZodOptional<z.ZodUnknown>;
168
172
  normalize: z.ZodOptional<z.ZodUnknown>;
169
173
  }, "strip", z.ZodTypeAny, {
174
+ mode?: "llm";
175
+ rules?: unknown;
176
+ generalPack?: unknown;
177
+ caseSensitive?: unknown;
170
178
  normalize?: unknown;
171
179
  llm?: {
172
- model?: ICopilotModel;
173
180
  scope?: "input" | "output" | "both";
181
+ model?: ICopilotModel;
182
+ rulePrompt?: string;
174
183
  systemPrompt?: string;
175
184
  outputMethod?: "functionCalling" | "jsonMode" | "jsonSchema";
176
185
  onLlmError?: "block" | "rewrite";
@@ -179,15 +188,16 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
179
188
  rewriteFallbackText?: string;
180
189
  timeoutMs?: number;
181
190
  };
191
+ }, {
182
192
  mode?: "llm";
183
193
  rules?: unknown;
184
194
  generalPack?: unknown;
185
195
  caseSensitive?: unknown;
186
- }, {
187
196
  normalize?: unknown;
188
197
  llm?: {
189
- model?: ICopilotModel;
190
198
  scope?: "input" | "output" | "both";
199
+ model?: ICopilotModel;
200
+ rulePrompt?: string;
191
201
  systemPrompt?: string;
192
202
  outputMethod?: "functionCalling" | "jsonMode" | "jsonSchema";
193
203
  onLlmError?: "block" | "rewrite";
@@ -196,14 +206,10 @@ export declare const sensitiveFilterConfigSchema: z.ZodUnion<[z.ZodObject<{
196
206
  rewriteFallbackText?: string;
197
207
  timeoutMs?: number;
198
208
  };
199
- mode?: "llm";
200
- rules?: unknown;
201
- generalPack?: unknown;
202
- caseSensitive?: unknown;
203
209
  }>]>;
204
- export declare const llmDecisionSchema: z.ZodEffects<z.ZodObject<{
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
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"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"}
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,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,YAAY,CAAC,EAAE,eAAe,CAAA;IAE9B,UAAU,CAAC,EAAE,cAAc,CAAA;IAE3B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAEzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,KAAK,CAAA;IACX,GAAG,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,cAAc,GAAG,aAAa,CAAA;AAElE,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG;IAClD,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,eAAO,MAAM,mBAAmB,wSAA8R,CAAA;AAmE9T,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAuD,CAAA;AAE/F,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;EAO1B,CAAA;AAYJ,wBAAgB,uBAAuB,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,aAAa,EAAE,CA6CnF"}
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('jsonSchema'),
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, '\\$&');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-sensitive-filter-xr",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "author": {
5
5
  "name": "XpertAI",
6
6
  "url": "https://xpertai.cn"