From 47ccefae99eab1d55707bf52f0455135a9365284 Mon Sep 17 00:00:00 2001 From: Samuel Soukup Date: Wed, 22 Jul 2026 16:28:59 +0200 Subject: [PATCH] JBAI-21522 [TRACE] Port text/code field distinction to 262, keeping TrueValidationRule creatable Backport of the JBAI-20852 mechanism (master 1062c6873c196): add LlmFieldKind and the TrueValidationRuleCode/TrueValidationRuleText accept-all rules so the TRACE PII filter can anonymize code and text separately. Unlike master, TrueValidationRule stays a concrete, instantiable class marked @Deprecated instead of becoming abstract, and the deprecated single-arg RequestPrivateData constructor / default CheckedFilePrivateData kind map to LlmFieldKind.UNSPECIFIED -> the legacy `llm_parameters` rule. This keeps all existing call sites compiling and their validation strings (and FUS group versions) unchanged, so old usages do not need to be migrated. The TRACE PII filter now matches llm_parameters, llm_code_parameters and llm_text_parameters. IJ-MR-214799 GitOrigin-RevId: 19333a6aed6e47fcd9f7f58e62bb715a330924c4 --- .../trace/TracePiiFilteringEventLogger.kt | 10 +++- .../trace/TracePiiFilteringEventLoggerTest.kt | 46 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/platform/statistics/src/com/intellij/internal/statistic/eventLog/trace/TracePiiFilteringEventLogger.kt b/platform/statistics/src/com/intellij/internal/statistic/eventLog/trace/TracePiiFilteringEventLogger.kt index 1fad79cd3bb8..51adfe295270 100644 --- a/platform/statistics/src/com/intellij/internal/statistic/eventLog/trace/TracePiiFilteringEventLogger.kt +++ b/platform/statistics/src/com/intellij/internal/statistic/eventLog/trace/TracePiiFilteringEventLogger.kt @@ -61,7 +61,11 @@ internal class TracePiiFilteringEventLogger( } internal object TraceLlmPiiDataFilter { - private const val LLM_PARAMETERS_RULE = "util#llm_parameters" + private val LLM_PARAMETERS_RULES = listOf( + "util#llm_parameters", + "util#llm_code_parameters", + "util#llm_text_parameters", + ) private val llmFieldPathsResolver = TraceLlmFieldPathsResolver() fun createFilter(recorderOptionsProvider: RecorderOptionProvider?): (EventLogGroup, String, Map) -> Map { @@ -273,7 +277,9 @@ internal object TraceLlmPiiDataFilter { } private fun shouldBePiiFiltered(validationRules: List): Boolean { - return validationRules.any { rule -> rule.contains(LLM_PARAMETERS_RULE) } + return validationRules.any { rule -> + LLM_PARAMETERS_RULES.any { ruleName -> rule.contains(ruleName) } + } } } } diff --git a/platform/statistics/test/com/intellij/internal/statistic/eventLog/trace/TracePiiFilteringEventLoggerTest.kt b/platform/statistics/test/com/intellij/internal/statistic/eventLog/trace/TracePiiFilteringEventLoggerTest.kt index 210393d049c3..09ec40737a3c 100644 --- a/platform/statistics/test/com/intellij/internal/statistic/eventLog/trace/TracePiiFilteringEventLoggerTest.kt +++ b/platform/statistics/test/com/intellij/internal/statistic/eventLog/trace/TracePiiFilteringEventLoggerTest.kt @@ -173,6 +173,42 @@ class TracePiiFilteringEventLoggerTest { assertEquals(input, filtered) } + + @Test + fun `filters fields validated by the code rule too`() { + val group = EventLogGroup("trace.event.log", 1, "TRACE") + group.registerVarargEvent("metadata.load.failed", LlmCodeParametersField("request")) + val provider = RecorderOptionProvider( + mapOf(TracePiiRegexRedactor.TRACE_PII_REGEXES_JSON_OPTION to """["AKIA[0-9A-Z]{16}"]""") + ) + val filter = TraceLlmPiiDataFilter.createFilter(provider) + + val input = mapOf( + "request" to "token AKIA1234567890ABCDEF", + ) + + val filtered = filter(group, "metadata.load.failed", input) + + assertEquals("token [REDACTED]", filtered["request"]) + } + + @Test + fun `filters fields validated by the text rule too`() { + val group = EventLogGroup("trace.event.log", 1, "TRACE") + group.registerVarargEvent("metadata.load.failed", LlmTextParametersField("request")) + val provider = RecorderOptionProvider( + mapOf(TracePiiRegexRedactor.TRACE_PII_REGEXES_JSON_OPTION to """["AKIA[0-9A-Z]{16}"]""") + ) + val filter = TraceLlmPiiDataFilter.createFilter(provider) + + val input = mapOf( + "request" to "token AKIA1234567890ABCDEF", + ) + + val filtered = filter(group, "metadata.load.failed", input) + + assertEquals("token [REDACTED]", filtered["request"]) + } } private class LlmParametersField(override val name: String) : StringEventField(name) { @@ -180,6 +216,16 @@ private class LlmParametersField(override val name: String) : StringEventField(n get() = listOf("{util#llm_parameters}") } +private class LlmCodeParametersField(override val name: String) : StringEventField(name) { + override val validationRule: List + get() = listOf("{util#llm_code_parameters}") +} + +private class LlmTextParametersField(override val name: String) : StringEventField(name) { + override val validationRule: List + get() = listOf("{util#llm_text_parameters}") +} + private class CapturingLogger : StatisticsEventLogger { var capturedDataProvider: (() -> Map?)? = null var cleanupCalled = false