From 69216db45d36491cde28a9a1bd441f3ecb14cb93 Mon Sep 17 00:00:00 2001 From: aakashb95 Date: Mon, 13 Jul 2026 18:36:25 +0530 Subject: [PATCH] fix(captain): compare FAQ languages by base code --- .../captain/llm/conversation_faq_service.rb | 10 +++++-- .../llm/conversation_faq_service_spec.rb | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/enterprise/app/services/captain/llm/conversation_faq_service.rb b/enterprise/app/services/captain/llm/conversation_faq_service.rb index de281dee6..aae67cd0d 100644 --- a/enterprise/app/services/captain/llm/conversation_faq_service.rb +++ b/enterprise/app/services/captain/llm/conversation_faq_service.rb @@ -152,11 +152,11 @@ class Captain::Llm::ConversationFaqService < Llm::BaseAiService end def open_suggestions_for_language - assistant.faq_suggestions.open.by_language(faq_language) + assistant.faq_suggestions.where(account_id: conversation.account_id).open.by_language(faq_language) end def approved_faqs_for_language - return assistant.responses.approved if faq_language == account_language + return assistant.responses.approved if base_language(faq_language) == base_language(account_language) assistant.responses.none end @@ -226,8 +226,12 @@ class Captain::Llm::ConversationFaqService < Llm::BaseAiService language.to_s.tr('-', '_') end + def base_language(language) + language.split('_').first + end + def language_name(language) - ISO_639.find(language.split('_').first)&.english_name&.downcase || 'english' + ISO_639.find(base_language(language))&.english_name&.downcase || 'english' end def parse_generation_response(response) diff --git a/spec/enterprise/services/captain/llm/conversation_faq_service_spec.rb b/spec/enterprise/services/captain/llm/conversation_faq_service_spec.rb index b4302447f..554900979 100644 --- a/spec/enterprise/services/captain/llm/conversation_faq_service_spec.rb +++ b/spec/enterprise/services/captain/llm/conversation_faq_service_spec.rb @@ -275,6 +275,36 @@ RSpec.describe Captain::Llm::ConversationFaqService do end end + context 'when conversation and account locales share a base language' do + let(:account) { create(:account, locale: 'pt_BR') } + let(:captain_assistant) { create(:captain_assistant, account: account) } + let(:conversation) do + create(:conversation, account: account, first_reply_created_at: Time.zone.now, + additional_attributes: { conversation_language: 'pt' }) + end + let!(:existing_response) do + create(:captain_assistant_response, assistant: captain_assistant, account: account, + question: 'Como ativo o recurso?', answer: 'Ative nas configuracoes.', + embedding: embedding_one) + end + let(:equivalence_response) { instance_double(RubyLLM::Message, content: { same_faq: true }.to_json) } + + before do + existing_response + allow(embedding_service).to receive(:get_embedding).and_return(embedding_one) + allow(mock_chat).to receive(:ask) do |input| + input.start_with?('{') ? equivalence_response : mock_response + end + end + + it 'deduplicates against approved FAQs in the same base language' do + expect do + service.generate_and_deduplicate + end.to change(Captain::FaqObservation.discarded, :count).by(2) + expect(captain_assistant.faq_suggestions.count).to be_zero + end + end + context 'when LLM API fails' do before do allow(mock_chat).to receive(:ask).and_raise(RubyLLM::Error.new(nil, 'API Error'))