@@ -719,9 +719,11 @@ func TestOmitIOOmitsContentAttributes(t *testing.T) {
719719 assertStringAttr (t , toolSpan .Attributes , "gen_ai.tool.call.arguments" , "<REDACTED>" )
720720 assertStringAttr (t , toolSpan .Attributes , "gen_ai.tool.call.result" , "<REDACTED>" )
721721
722- // Chat span should have redacted prompt/response content.
723- assertStringAttr (t , chatSpan .Attributes , "gen_ai.input.messages" , "<REDACTED>" )
724- assertStringAttr (t , chatSpan .Attributes , "gen_ai.output.messages" , "<REDACTED>" )
722+ // Chat span should have redacted prompt/response content but preserve JSON structure.
723+ assertAttrContains (t , chatSpan .Attributes , "gen_ai.input.messages" , `"role":"user"` )
724+ assertAttrContains (t , chatSpan .Attributes , "gen_ai.input.messages" , `REDACTED` )
725+ assertAttrContains (t , chatSpan .Attributes , "gen_ai.output.messages" , `"role":"assistant"` )
726+ assertAttrContains (t , chatSpan .Attributes , "gen_ai.output.messages" , `REDACTED` )
725727}
726728
727729func TestUserPromptSubmitStampsChatSpanID (t * testing.T ) {
@@ -877,3 +879,15 @@ func assertStringAttr(t *testing.T, attrs []otlp.Attribute, key, want string) {
877879 }
878880 t .Errorf ("attribute %q not found" , key )
879881}
882+
883+ func assertAttrContains (t * testing.T , attrs []otlp.Attribute , key , substr string ) {
884+ t .Helper ()
885+ for _ , a := range attrs {
886+ if a .Key == key {
887+ require .NotNil (t , a .Value .StringValue , "attribute %q should have string value" , key )
888+ assert .Contains (t , * a .Value .StringValue , substr , "attribute %q should contain %q" , key , substr )
889+ return
890+ }
891+ }
892+ t .Errorf ("attribute %q not found" , key )
893+ }
0 commit comments