FIX: Use a dedicated prompt for thread titles (#464)

This commit is contained in:
Rafael dos Santos Silva 2024-02-07 15:05:50 -03:00 committed by GitHub
parent 0ff5c0c2c4
commit bccb7efdd6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 70 additions and 22 deletions

View File

@ -8,26 +8,38 @@ module DiscourseAi
end end
def suggested_title def suggested_title
return nil if thread_content.blank? @thread.then { thread_content(_1) }.then { call_llm(_1) }.then { cleanup(_1) }
prompt = CompletionPrompt.enabled_by_name("generate_titles")
raise Discourse::InvalidParameters.new(:mode) if !prompt
response =
DiscourseAi::AiHelper::Assistant.new.generate_and_send_prompt(
prompt,
thread_content,
thread.original_message_user,
)
response.dig(:suggestions)&.first
end end
private def call_llm(thread_content)
return nil if thread_content.blank?
attr_reader :thread chat = "<input>\n#{thread_content}\n</input>"
def thread_content prompt =
# Replace me by a proper API call DiscourseAi::Completions::Prompt.new(
<<~TEXT.strip,
I want you to act as a title generator for chat between users. I will provide you with the chat transcription,
and you will generate a single attention-grabbing title. Please keep the title concise and under 15 words
and ensure that the meaning is maintained. The title will utilize the same language type of the chat.
I want you to only reply the suggested title and nothing else, do not write explanations.
You will find the chat between <input></input> XML tags.
TEXT
messages: [{ type: :user, content: chat, id: "User" }],
)
DiscourseAi::Completions::Llm.proxy(SiteSetting.ai_helper_model).generate(
prompt,
user: Discourse.system_user,
)
end
def cleanup(title)
title.split("\n").first.then { _1.match?(/^("|')(.*)("|')$/) ? title[1..-2] : _1 }
end
def thread_content(thread)
# TODO: Replace me by a proper API call
thread thread
.chat_messages .chat_messages
.joins(:user) .joins(:user)
@ -35,6 +47,8 @@ module DiscourseAi
.map { |username, message| "#{username}: #{message}" } .map { |username, message| "#{username}: #{message}" }
.join("\n") .join("\n")
end end
attr_reader :thread
end end
end end
end end

View File

@ -6,17 +6,51 @@ RSpec.describe DiscourseAi::AiHelper::ChatThreadTitler do
before { SiteSetting.ai_helper_model = "fake:fake" } before { SiteSetting.ai_helper_model = "fake:fake" }
fab!(:thread) { Fabricate(:chat_thread) } fab!(:thread) { Fabricate(:chat_thread) }
fab!(:chat_message) { Fabricate(:chat_message, thread: thread) }
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
describe "#suggested_title" do describe "#cleanup" do
it "suggest the first option from the generate_titles prompt" do it "picks the first when there are multiple" do
titles = titles = "The solitary horse\nThe horse etched in gold"
"<item>The solitary horse</item><item>The horse etched in gold</item><item>A horse's infinite journey</item><item>A horse lost in time</item><item>A horse's last ride</item>"
expected_title = "The solitary horse" expected_title = "The solitary horse"
result =
DiscourseAi::Completions::Llm.with_prepared_responses([titles]) { titler.suggested_title } result = titler.cleanup(titles)
expect(result).to eq(expected_title)
end
it "cleans up double quotes enclosing the whole title" do
titles = '"The solitary horse"'
expected_title = "The solitary horse"
result = titler.cleanup(titles)
expect(result).to eq(expected_title)
end
it "cleans up single quotes enclosing the whole title" do
titles = "'The solitary horse'"
expected_title = "The solitary horse"
result = titler.cleanup(titles)
expect(result).to eq(expected_title)
end
it "leaves quotes in the middle of title" do
titles = "The 'solitary' horse"
expected_title = "The 'solitary' horse"
result = titler.cleanup(titles)
expect(result).to eq(expected_title) expect(result).to eq(expected_title)
end end
end end
describe "#thread_content" do
it "returns the chat message and user" do
expect(titler.thread_content(thread)).to include(chat_message.message)
expect(titler.thread_content(thread)).to include(chat_message.user.username)
end
end
end end