diff --git a/enterprise/app/services/captain/contact_memories/prompt_injection_service.rb b/enterprise/app/services/captain/contact_memories/prompt_injection_service.rb new file mode 100644 index 000000000..0d48ae0a8 --- /dev/null +++ b/enterprise/app/services/captain/contact_memories/prompt_injection_service.rb @@ -0,0 +1,21 @@ +class Captain::ContactMemories::PromptInjectionService + def initialize(memories:) + @memories = memories + end + + def call + return '' if @memories.blank? + + lines = @memories.map do |memory| + %( <#{memory.memory_type} confidence="#{format('%.2f', memory.confidence)}">#{escape(memory.content)}) + end + + "\n#{lines.join("\n")}\n" + end + + private + + def escape(text) + text.to_s.gsub('&', '&').gsub('<', '<').gsub('>', '>') + end +end diff --git a/spec/enterprise/services/captain/contact_memories/prompt_injection_service_spec.rb b/spec/enterprise/services/captain/contact_memories/prompt_injection_service_spec.rb new file mode 100644 index 000000000..b5e607f3c --- /dev/null +++ b/spec/enterprise/services/captain/contact_memories/prompt_injection_service_spec.rb @@ -0,0 +1,30 @@ +require 'rails_helper' + +RSpec.describe Captain::ContactMemories::PromptInjectionService do + describe '#call' do + it 'returns empty string when memories empty' do + result = described_class.new(memories: []).call + expect(result).to eq('') + end + + it 'formats memories as XML block' do + memories = [ + build_stubbed(:captain_contact_memory, memory_type: 'preferencia', content: 'Prefere Stilo', confidence: 0.92), + build_stubbed(:captain_contact_memory, memory_type: 'data_comemorativa', content: 'Aniversário 14/02', confidence: 0.88) + ] + + result = described_class.new(memories: memories).call + + expect(result).to include('') + expect(result).to include('Prefere Stilo') + expect(result).to include('Aniversário 14/02') + expect(result).to include('') + end + + it 'escapes XML-unsafe chars in content' do + memories = [build_stubbed(:captain_contact_memory, content: 'texto com & ampersand')] + result = described_class.new(memories: memories).call + expect(result).to include('texto com <tag> & ampersand') + end + end +end