message responder backend now works
This commit is contained in:
parent
4fcf12287f
commit
cd7790f39a
|
@ -10,7 +10,13 @@ module ::Jobs
|
||||||
persona_id = args[:persona_id]
|
persona_id = args[:persona_id]
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
persona =
|
||||||
|
if args[:skip_persona_security_check]
|
||||||
|
persona = AiPersona.all_personas.find { |persona| persona.id == persona_id }
|
||||||
|
else
|
||||||
persona = DiscourseAi::AiBot::Personas::Persona.find_by(user: post.user, id: persona_id)
|
persona = DiscourseAi::AiBot::Personas::Persona.find_by(user: post.user, id: persona_id)
|
||||||
|
end
|
||||||
|
|
||||||
raise DiscourseAi::AiBot::Bot::BOT_NOT_FOUND if persona.nil?
|
raise DiscourseAi::AiBot::Bot::BOT_NOT_FOUND if persona.nil?
|
||||||
|
|
||||||
bot = DiscourseAi::AiBot::Bot.as(bot_user, persona: persona.new)
|
bot = DiscourseAi::AiBot::Bot.as(bot_user, persona: persona.new)
|
||||||
|
|
|
@ -132,6 +132,7 @@ class AiPersona < ActiveRecord::Base
|
||||||
vision_max_pixels = self.vision_max_pixels
|
vision_max_pixels = self.vision_max_pixels
|
||||||
rag_conversation_chunks = self.rag_conversation_chunks
|
rag_conversation_chunks = self.rag_conversation_chunks
|
||||||
question_consolidator_llm = self.question_consolidator_llm
|
question_consolidator_llm = self.question_consolidator_llm
|
||||||
|
role = self.role
|
||||||
role_whispers = self.role_whispers
|
role_whispers = self.role_whispers
|
||||||
|
|
||||||
persona_class = DiscourseAi::AiBot::Personas::Persona.system_personas_by_id[self.id]
|
persona_class = DiscourseAi::AiBot::Personas::Persona.system_personas_by_id[self.id]
|
||||||
|
@ -184,6 +185,10 @@ class AiPersona < ActiveRecord::Base
|
||||||
role_whispers
|
role_whispers
|
||||||
end
|
end
|
||||||
|
|
||||||
|
persona_class.define_singleton_method :role do
|
||||||
|
role
|
||||||
|
end
|
||||||
|
|
||||||
return persona_class
|
return persona_class
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -275,6 +280,10 @@ class AiPersona < ActiveRecord::Base
|
||||||
question_consolidator_llm
|
question_consolidator_llm
|
||||||
end
|
end
|
||||||
|
|
||||||
|
define_singleton_method :role do
|
||||||
|
role
|
||||||
|
end
|
||||||
|
|
||||||
define_singleton_method :role_whispers do
|
define_singleton_method :role_whispers do
|
||||||
role_whispers
|
role_whispers
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,23 @@ module DiscourseAi
|
||||||
module Personas
|
module Personas
|
||||||
class Persona
|
class Persona
|
||||||
class << self
|
class << self
|
||||||
|
def as_bot
|
||||||
|
if self.respond_to?(:user_id) && self.respond_to?(:default_llm)
|
||||||
|
if self.default_llm
|
||||||
|
user = User.find_by(id: user_id)
|
||||||
|
DiscourseAi::AiBot::Bot.new(user, self.new, self.default_llm) if user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def role
|
||||||
|
"bot"
|
||||||
|
end
|
||||||
|
|
||||||
|
def role_whispers
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
def rag_conversation_chunks
|
def rag_conversation_chunks
|
||||||
10
|
10
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,9 +18,30 @@ module DiscourseAi
|
||||||
user_id.to_i <= 0
|
user_id.to_i <= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.find_responder_persona(post)
|
||||||
|
if post.post_number == 1 && post.topic && post.topic.archetype == Archetype.private_message
|
||||||
|
# only supported responder for PMs is based on role_group_ids
|
||||||
|
group_ids = post.topic.allowed_groups.pluck(:id)
|
||||||
|
|
||||||
|
info =
|
||||||
|
group_ids
|
||||||
|
.lazy
|
||||||
|
.map { |group_id| AiPersona.message_responder_for(group_id: group_id) }
|
||||||
|
.find { |found| !found.nil? }
|
||||||
|
|
||||||
|
AiPersona.all_personas.find { |persona| persona.id == info[:id] } if info && info[:id]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.schedule_reply(post)
|
def self.schedule_reply(post)
|
||||||
return if is_bot_user_id?(post.user_id)
|
return if is_bot_user_id?(post.user_id)
|
||||||
|
|
||||||
|
if responder_persona_class = find_responder_persona(post)
|
||||||
|
bot = responder_persona_class.as_bot
|
||||||
|
new(bot).schedule_bot_reply(post, skip_persona_security_check: true) if bot
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
bot_ids = DiscourseAi::AiBot::EntryPoint::BOT_USER_IDS
|
bot_ids = DiscourseAi::AiBot::EntryPoint::BOT_USER_IDS
|
||||||
mentionables = AiPersona.mentionables(user: post.user)
|
mentionables = AiPersona.mentionables(user: post.user)
|
||||||
|
|
||||||
|
@ -126,11 +147,12 @@ module DiscourseAi
|
||||||
FROM upload_references ref
|
FROM upload_references ref
|
||||||
WHERE ref.target_type = 'Post' AND ref.target_id = posts.id
|
WHERE ref.target_type = 'Post' AND ref.target_id = posts.id
|
||||||
) as upload_ids",
|
) as upload_ids",
|
||||||
|
"post_number",
|
||||||
)
|
)
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
|
|
||||||
context.reverse_each do |raw, username, custom_prompt, upload_ids|
|
context.reverse_each do |raw, username, custom_prompt, upload_ids, post_number|
|
||||||
custom_prompt_translation =
|
custom_prompt_translation =
|
||||||
Proc.new do |message|
|
Proc.new do |message|
|
||||||
# We can't keep backwards-compatibility for stored functions.
|
# We can't keep backwards-compatibility for stored functions.
|
||||||
|
@ -151,8 +173,14 @@ module DiscourseAi
|
||||||
if custom_prompt.present?
|
if custom_prompt.present?
|
||||||
custom_prompt.each(&custom_prompt_translation)
|
custom_prompt.each(&custom_prompt_translation)
|
||||||
else
|
else
|
||||||
|
content = raw
|
||||||
|
if post_number == 1 && bot.persona.class.role.include?("responder")
|
||||||
|
title = Topic.where("id = ?", post.topic_id).pluck(:title).first
|
||||||
|
content = "# #{title}\n\n#{content}"
|
||||||
|
end
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
content: raw,
|
content: content,
|
||||||
type: (available_bot_usernames.include?(username) ? :model : :user),
|
type: (available_bot_usernames.include?(username) ? :model : :user),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,8 +216,11 @@ module DiscourseAi
|
||||||
reply = +""
|
reply = +""
|
||||||
start = Time.now
|
start = Time.now
|
||||||
|
|
||||||
post_type =
|
post_type = Post.types[:regular]
|
||||||
post.post_type == Post.types[:whisper] ? Post.types[:whisper] : Post.types[:regular]
|
|
||||||
|
if post.post_type == Post.types[:whisper] || bot.persona.class.role_whispers
|
||||||
|
post_type = Post.types[:whisper]
|
||||||
|
end
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
site_url: Discourse.base_url,
|
site_url: Discourse.base_url,
|
||||||
|
@ -208,7 +239,7 @@ module DiscourseAi
|
||||||
reply_user = User.find_by(id: bot.persona.class.user_id) || reply_user
|
reply_user = User.find_by(id: bot.persona.class.user_id) || reply_user
|
||||||
end
|
end
|
||||||
|
|
||||||
stream_reply = post.topic.private_message?
|
stream_reply = post.topic.private_message? && !bot.persona.role.include?("responder")
|
||||||
|
|
||||||
# we need to ensure persona user is allowed to reply to the pm
|
# we need to ensure persona user is allowed to reply to the pm
|
||||||
if post.topic.private_message?
|
if post.topic.private_message?
|
||||||
|
@ -309,6 +340,19 @@ module DiscourseAi
|
||||||
.concat(DiscourseAi::AiBot::EntryPoint::BOTS.map(&:second))
|
.concat(DiscourseAi::AiBot::EntryPoint::BOTS.map(&:second))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def schedule_bot_reply(post, skip_persona_security_check: false)
|
||||||
|
persona_id =
|
||||||
|
DiscourseAi::AiBot::Personas::Persona.system_personas[bot.persona.class] ||
|
||||||
|
bot.persona.class.id
|
||||||
|
::Jobs.enqueue(
|
||||||
|
:create_ai_reply,
|
||||||
|
post_id: post.id,
|
||||||
|
bot_user_id: bot.bot_user.id,
|
||||||
|
persona_id: persona_id,
|
||||||
|
skip_persona_security_check: skip_persona_security_check,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def publish_final_update(reply_post)
|
def publish_final_update(reply_post)
|
||||||
|
@ -347,18 +391,6 @@ module DiscourseAi
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def schedule_bot_reply(post)
|
|
||||||
persona_id =
|
|
||||||
DiscourseAi::AiBot::Personas::Persona.system_personas[bot.persona.class] ||
|
|
||||||
bot.persona.class.id
|
|
||||||
::Jobs.enqueue(
|
|
||||||
:create_ai_reply,
|
|
||||||
post_id: post.id,
|
|
||||||
bot_user_id: bot.bot_user.id,
|
|
||||||
persona_id: persona_id,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def context(topic)
|
def context(topic)
|
||||||
{
|
{
|
||||||
site_url: Discourse.base_url,
|
site_url: Discourse.base_url,
|
||||||
|
|
|
@ -530,4 +530,60 @@ RSpec.describe DiscourseAi::AiBot::Playground do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "message_responder role" do
|
||||||
|
fab!(:group) do
|
||||||
|
Fabricate(:group, name: "test-group", messageable_level: Group::ALIAS_LEVELS[:everyone])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "can reply with a whisper to group PM" do
|
||||||
|
Jobs.run_immediately!
|
||||||
|
|
||||||
|
persona =
|
||||||
|
AiPersona.create!(
|
||||||
|
name: "Responder Persona",
|
||||||
|
description: "A responder",
|
||||||
|
system_prompt: "You are a responder",
|
||||||
|
enabled: true,
|
||||||
|
role: "message_responder",
|
||||||
|
role_group_ids: [group.id],
|
||||||
|
role_whispers: true,
|
||||||
|
default_llm: "anthropic:claude-2",
|
||||||
|
)
|
||||||
|
|
||||||
|
persona.create_user!
|
||||||
|
|
||||||
|
post = nil
|
||||||
|
|
||||||
|
prompts = nil
|
||||||
|
DiscourseAi::Completions::Llm.with_prepared_responses(
|
||||||
|
["An amazing post just for you"],
|
||||||
|
) do |_, _, _prompts|
|
||||||
|
post =
|
||||||
|
create_post(
|
||||||
|
title: "an amazing title",
|
||||||
|
raw: "Howdy I need help!!",
|
||||||
|
archetype: Archetype.private_message,
|
||||||
|
target_group_names: [group.name],
|
||||||
|
)
|
||||||
|
prompts = _prompts
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(prompts.length).to eq(1)
|
||||||
|
expect(prompts[0].messages[0][:content]).to eq("You are a responder")
|
||||||
|
expect(prompts[0].messages[1][:content]).to eq("# An amazing title\n\nHowdy I need help!!")
|
||||||
|
|
||||||
|
reply = post.topic.posts.find_by(post_number: 2)
|
||||||
|
|
||||||
|
expect(reply.post_type).to eq(Post.types[:whisper])
|
||||||
|
expect(reply.raw).to eq("An amazing post just for you")
|
||||||
|
|
||||||
|
# should be done responding at this point so llm will not be called
|
||||||
|
create_post(
|
||||||
|
raw: "should ignore posts that are not first on message",
|
||||||
|
topic: post.topic,
|
||||||
|
user: post.user,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue