content_moderation_input_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. package service
  2. import (
  3. "testing"
  4. "github.com/stretchr/testify/require"
  5. )
  6. // 当数组末尾不是用户消息时(典型场景:Agent 工具循环结束于 tool/assistant),
  7. // 应直接跳过审计——不再回溯查找历史中的某条用户消息。
  8. func TestExtractContentModerationInput_AnthropicAgentToolLoopSkipsAudit(t *testing.T) {
  9. body := []byte(`{
  10. "messages": [
  11. {"role":"user","content":"调用一下天气工具"},
  12. {"role":"assistant","content":[{"type":"tool_use","id":"tool_1","name":"weather","input":{}}]},
  13. {"role":"user","content":[{"type":"tool_result","tool_use_id":"tool_1","content":"晴 25 度"}]}
  14. ]
  15. }`)
  16. input := ExtractContentModerationInput(ContentModerationProtocolAnthropicMessages, body)
  17. require.Empty(t, input.Text)
  18. require.Empty(t, input.Images)
  19. }
  20. func TestExtractContentModerationInput_AnthropicFirstTurnExtractsUser(t *testing.T) {
  21. body := []byte(`{
  22. "messages": [
  23. {"role":"user","content":"Q1"}
  24. ]
  25. }`)
  26. input := ExtractContentModerationInput(ContentModerationProtocolAnthropicMessages, body)
  27. require.Equal(t, "Q1", input.Text)
  28. }
  29. func TestExtractContentModerationInput_AnthropicMultiTurnExtractsLatestUser(t *testing.T) {
  30. body := []byte(`{
  31. "messages": [
  32. {"role":"user","content":"Q1"},
  33. {"role":"assistant","content":"A1"},
  34. {"role":"user","content":"Q2"}
  35. ]
  36. }`)
  37. input := ExtractContentModerationInput(ContentModerationProtocolAnthropicMessages, body)
  38. require.Equal(t, "Q2", input.Text)
  39. }
  40. func TestExtractContentModerationInput_AnthropicStreamResendExtractsResend(t *testing.T) {
  41. body := []byte(`{
  42. "messages": [
  43. {"role":"user","content":"原问题"},
  44. {"role":"assistant","content":"部分回答……"},
  45. {"role":"user","content":"重发"}
  46. ]
  47. }`)
  48. input := ExtractContentModerationInput(ContentModerationProtocolAnthropicMessages, body)
  49. require.Equal(t, "重发", input.Text)
  50. }
  51. func TestExtractContentModerationInput_OpenAIChatAgentToolLoopSkipsAudit(t *testing.T) {
  52. body := []byte(`{
  53. "messages": [
  54. {"role":"system","content":"sys"},
  55. {"role":"user","content":"列出我的订单"},
  56. {"role":"assistant","content":null,"tool_calls":[{"id":"call_1","type":"function","function":{"name":"orders","arguments":"{}"}}]},
  57. {"role":"tool","tool_call_id":"call_1","content":"[]"}
  58. ]
  59. }`)
  60. input := ExtractContentModerationInput(ContentModerationProtocolOpenAIChat, body)
  61. require.Empty(t, input.Text)
  62. require.Empty(t, input.Images)
  63. }
  64. func TestExtractContentModerationInput_OpenAIChatMultiTurnExtractsLatestUser(t *testing.T) {
  65. body := []byte(`{
  66. "messages": [
  67. {"role":"user","content":"Q1"},
  68. {"role":"assistant","content":"A1"},
  69. {"role":"user","content":"Q2"}
  70. ]
  71. }`)
  72. input := ExtractContentModerationInput(ContentModerationProtocolOpenAIChat, body)
  73. require.Equal(t, "Q2", input.Text)
  74. }
  75. func TestExtractContentModerationInput_GeminiAgentToolLoopSkipsAudit(t *testing.T) {
  76. body := []byte(`{
  77. "contents": [
  78. {"role":"user","parts":[{"text":"查询天气"}]},
  79. {"role":"model","parts":[{"functionCall":{"name":"weather","args":{}}}]},
  80. {"role":"user","parts":[{"functionResponse":{"name":"weather","response":{"temp":25}}}]}
  81. ]
  82. }`)
  83. input := ExtractContentModerationInput(ContentModerationProtocolGemini, body)
  84. require.Empty(t, input.Text)
  85. require.Empty(t, input.Images)
  86. }
  87. func TestExtractContentModerationInput_GeminiFirstTurnExtractsUser(t *testing.T) {
  88. body := []byte(`{
  89. "contents": [
  90. {"role":"user","parts":[{"text":"你好"}]}
  91. ]
  92. }`)
  93. input := ExtractContentModerationInput(ContentModerationProtocolGemini, body)
  94. require.Equal(t, "你好", input.Text)
  95. }
  96. func TestExtractContentModerationInput_GeminiMultiTurnExtractsLatestUser(t *testing.T) {
  97. body := []byte(`{
  98. "contents": [
  99. {"role":"user","parts":[{"text":"Q1"}]},
  100. {"role":"model","parts":[{"text":"A1"}]},
  101. {"role":"user","parts":[{"text":"Q2"}]}
  102. ]
  103. }`)
  104. input := ExtractContentModerationInput(ContentModerationProtocolGemini, body)
  105. require.Equal(t, "Q2", input.Text)
  106. }
  107. func TestExtractContentModerationInput_ResponsesAgentToolLoopSkipsAudit(t *testing.T) {
  108. body := []byte(`{
  109. "input":[
  110. {"type":"message","role":"user","content":[{"type":"input_text","text":"运行测试"}]},
  111. {"type":"function_call","call_id":"call_1","name":"run_tests","arguments":"{}"},
  112. {"type":"function_call_output","call_id":"call_1","output":"all passed"}
  113. ]
  114. }`)
  115. input := ExtractContentModerationInput(ContentModerationProtocolOpenAIResponses, body)
  116. require.Empty(t, input.Text)
  117. require.Empty(t, input.Images)
  118. }
  119. func TestExtractContentModerationInput_ResponsesLastUserMessageExtracted(t *testing.T) {
  120. body := []byte(`{
  121. "input":[
  122. {"type":"message","role":"user","content":[{"type":"input_text","text":"first"}]},
  123. {"type":"message","role":"assistant","content":[{"type":"output_text","text":"answer"}]},
  124. {"type":"message","role":"user","content":[{"type":"input_text","text":"latest"}]}
  125. ]
  126. }`)
  127. input := ExtractContentModerationInput(ContentModerationProtocolOpenAIResponses, body)
  128. require.Equal(t, "latest", input.Text)
  129. }
  130. func TestExtractContentModerationInput_ResponsesLastIsAssistantSkipped(t *testing.T) {
  131. body := []byte(`{
  132. "input":[
  133. {"type":"message","role":"user","content":[{"type":"input_text","text":"q1"}]},
  134. {"type":"message","role":"assistant","content":[{"type":"output_text","text":"a1"}]}
  135. ]
  136. }`)
  137. input := ExtractContentModerationInput(ContentModerationProtocolOpenAIResponses, body)
  138. require.Empty(t, input.Text)
  139. require.Empty(t, input.Images)
  140. }