mirror of
https://github.com/discourse/discourse-ai.git
synced 2025-08-03 19:53:28 +00:00
- New AiPersona model which can store custom personas - Persona are restricted via group security - They can contain custom system messages - They can support a list of commands optionally To avoid expensive DB calls in the serializer a Multisite friendly Hash was introduced (which can be expired on transaction commit)
122 lines
3.1 KiB
Ruby
122 lines
3.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class AiPersona < ActiveRecord::Base
|
|
# places a hard limit, so per site we cache a maximum of 500 classes
|
|
MAX_PERSONAS_PER_SITE = 500
|
|
|
|
class MultisiteHash
|
|
def initialize(id)
|
|
@hash = Hash.new { |h, k| h[k] = {} }
|
|
@id = id
|
|
|
|
MessageBus.subscribe(channel_name) { |message| @hash[message.data] = {} }
|
|
end
|
|
|
|
def channel_name
|
|
"/multisite-hash-#{@id}"
|
|
end
|
|
|
|
def current_db
|
|
RailsMultisite::ConnectionManagement.current_db
|
|
end
|
|
|
|
def [](key)
|
|
@hash.dig(current_db, key)
|
|
end
|
|
|
|
def []=(key, val)
|
|
@hash[current_db][key] = val
|
|
end
|
|
|
|
def flush!
|
|
@hash[current_db] = {}
|
|
MessageBus.publish(channel_name, current_db)
|
|
end
|
|
end
|
|
|
|
def self.persona_cache
|
|
@persona_cache ||= MultisiteHash.new("persona_cache")
|
|
end
|
|
|
|
def self.all_personas
|
|
persona_cache[:value] ||= AiPersona
|
|
.order(:name)
|
|
.where(enabled: true)
|
|
.all
|
|
.limit(MAX_PERSONAS_PER_SITE)
|
|
.map do |ai_persona|
|
|
name = ai_persona.name
|
|
description = ai_persona.description
|
|
ai_persona_id = ai_persona.id
|
|
allowed_group_ids = ai_persona.allowed_group_ids
|
|
commands =
|
|
ai_persona.commands.filter_map do |inner_name|
|
|
begin
|
|
("DiscourseAi::AiBot::Commands::#{inner_name}").constantize
|
|
rescue StandardError
|
|
nil
|
|
end
|
|
end
|
|
|
|
Class.new(DiscourseAi::AiBot::Personas::Persona) do
|
|
define_singleton_method :name do
|
|
name
|
|
end
|
|
|
|
define_singleton_method :description do
|
|
description
|
|
end
|
|
|
|
define_singleton_method :allowed_group_ids do
|
|
allowed_group_ids
|
|
end
|
|
|
|
define_singleton_method :to_s do
|
|
"#<DiscourseAi::AiBot::Personas::Persona::Custom @name=#{self.name} @allowed_group_ids=#{self.allowed_group_ids.join(",")}>"
|
|
end
|
|
|
|
define_singleton_method :inspect do
|
|
"#<DiscourseAi::AiBot::Personas::Persona::Custom @name=#{self.name} @allowed_group_ids=#{self.allowed_group_ids.join(",")}>"
|
|
end
|
|
|
|
define_method :initialize do |*args, **kwargs|
|
|
@ai_persona = AiPersona.find_by(id: ai_persona_id)
|
|
super(*args, **kwargs)
|
|
end
|
|
|
|
define_method :commands do
|
|
commands
|
|
end
|
|
|
|
define_method :system_prompt do
|
|
@ai_persona&.system_prompt || "You are a helpful bot."
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
after_commit :bump_cache
|
|
|
|
def bump_cache
|
|
self.class.persona_cache.flush!
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: ai_personas
|
|
#
|
|
# id :bigint not null, primary key
|
|
# name :string(100) not null
|
|
# description :string(2000) not null
|
|
# commands :string default([]), not null, is an Array
|
|
# system_prompt :string not null
|
|
# allowed_group_ids :integer default([]), not null, is an Array
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_ai_personas_on_name (name) UNIQUE
|
|
#
|