Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ba091aa8b |
+19
@@ -0,0 +1,19 @@
|
|||||||
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
|
export default class AdminPluginsShowDiscourseAiAgentsEdit extends DiscourseRoute {
|
||||||
|
async model(params) {
|
||||||
|
const allAgents = this.modelFor(
|
||||||
|
"adminPlugins.show.discourse-ai-agents"
|
||||||
|
);
|
||||||
|
const id = parseInt(params.id, 10);
|
||||||
|
return allAgents.findBy("id", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupController(controller, model) {
|
||||||
|
super.setupController(controller, model);
|
||||||
|
controller.set(
|
||||||
|
"allAgents",
|
||||||
|
this.modelFor("adminPlugins.show.discourse-ai-agents")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
-5
@@ -1,16 +1,16 @@
|
|||||||
import { AUTO_GROUPS } from "discourse/lib/constants";
|
import { AUTO_GROUPS } from "discourse/lib/constants";
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
export default class AdminPluginsShowDiscourseAiPersonasNew extends DiscourseRoute {
|
export default class AdminPluginsShowDiscourseAiAgentsNew extends DiscourseRoute {
|
||||||
async model() {
|
async model() {
|
||||||
const record = this.store.createRecord("ai-persona");
|
const record = this.store.createRecord("ai-agent");
|
||||||
record.set("allowed_group_ids", [AUTO_GROUPS.trust_level_0.id]);
|
record.set("allowed_group_ids", [AUTO_GROUPS.trust_level_0.id]);
|
||||||
record.set("rag_uploads", []);
|
record.set("rag_uploads", []);
|
||||||
// these match the defaults on the table
|
// these match the defaults on the table
|
||||||
record.set("rag_chunk_tokens", 374);
|
record.set("rag_chunk_tokens", 374);
|
||||||
record.set("rag_chunk_overlap_tokens", 10);
|
record.set("rag_chunk_overlap_tokens", 10);
|
||||||
record.set("rag_conversation_chunks", 10);
|
record.set("rag_conversation_chunks", 10);
|
||||||
record.set("allow_personal_messages", true);
|
record.set("allow_agentl_messages", true);
|
||||||
record.set("tool_details", false);
|
record.set("tool_details", false);
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
@@ -18,8 +18,8 @@ export default class AdminPluginsShowDiscourseAiPersonasNew extends DiscourseRou
|
|||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
super.setupController(controller, model);
|
super.setupController(controller, model);
|
||||||
controller.set(
|
controller.set(
|
||||||
"allPersonas",
|
"allAgents",
|
||||||
this.modelFor("adminPlugins.show.discourse-ai-personas")
|
this.modelFor("adminPlugins.show.discourse-ai-agents")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
|
export default class DiscourseAiAiAgentsRoute extends DiscourseRoute {
|
||||||
|
model() {
|
||||||
|
return this.store.findAll("ai-agent");
|
||||||
|
}
|
||||||
|
}
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
|
||||||
|
|
||||||
export default class AdminPluginsShowDiscourseAiPersonasEdit extends DiscourseRoute {
|
|
||||||
async model(params) {
|
|
||||||
const allPersonas = this.modelFor(
|
|
||||||
"adminPlugins.show.discourse-ai-personas"
|
|
||||||
);
|
|
||||||
const id = parseInt(params.id, 10);
|
|
||||||
return allPersonas.findBy("id", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(controller, model);
|
|
||||||
controller.set(
|
|
||||||
"allPersonas",
|
|
||||||
this.modelFor("adminPlugins.show.discourse-ai-personas")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import DiscourseRoute from "discourse/routes/discourse";
|
|
||||||
|
|
||||||
export default class DiscourseAiAiPersonasRoute extends DiscourseRoute {
|
|
||||||
model() {
|
|
||||||
return this.store.findAll("ai-persona");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
<AiAgentListEditor
|
||||||
|
@agents={{this.allAgents}}
|
||||||
|
@currentAgent={{this.model}}
|
||||||
|
/>
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
<AiAgentListEditor @agents={{this.model}} />
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
<AiAgentListEditor
|
||||||
|
@agents={{this.allAgents}}
|
||||||
|
@currentAgent={{this.model}}
|
||||||
|
/>
|
||||||
+8
-8
@@ -15,7 +15,7 @@ export default RouteTemplate(
|
|||||||
const prefix = "discourse_ai.features.list.header";
|
const prefix = "discourse_ai.features.list.header";
|
||||||
return [
|
return [
|
||||||
i18n(`${prefix}.name`),
|
i18n(`${prefix}.name`),
|
||||||
i18n(`${prefix}.persona`),
|
i18n(`${prefix}.agent`),
|
||||||
i18n(`${prefix}.groups`),
|
i18n(`${prefix}.groups`),
|
||||||
"",
|
"",
|
||||||
];
|
];
|
||||||
@@ -75,21 +75,21 @@ export default RouteTemplate(
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class="d-admin-row__detail ai-feature-list__row-item ai-feature-list__persona"
|
class="d-admin-row__detail ai-feature-list__row-item ai-feature-list__agent"
|
||||||
>
|
>
|
||||||
<DButton
|
<DButton
|
||||||
class="btn-flat btn-small ai-feature-list__row-item-persona"
|
class="btn-flat btn-small ai-feature-list__row-item-agent"
|
||||||
@translatedLabel={{feature.persona.name}}
|
@translatedLabel={{feature.agent.name}}
|
||||||
@route="adminPlugins.show.discourse-ai-personas.edit"
|
@route="adminPlugins.show.discourse-ai-agents.edit"
|
||||||
@routeModels={{feature.persona.id}}
|
@routeModels={{feature.agent.id}}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
class="d-admin-row__detail ai-feature-list__row-item ai-feature-list__groups"
|
class="d-admin-row__detail ai-feature-list__row-item ai-feature-list__groups"
|
||||||
>
|
>
|
||||||
{{#if (gt feature.persona.allowed_groups.length 0)}}
|
{{#if (gt feature.agent.allowed_groups.length 0)}}
|
||||||
<ul class="ai-feature-list__row-item-groups">
|
<ul class="ai-feature-list__row-item-groups">
|
||||||
{{#each feature.persona.allowed_groups as |group|}}
|
{{#each feature.agent.allowed_groups as |group|}}
|
||||||
<li>{{group.name}}</li>
|
<li>{{group.name}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
<AiPersonaListEditor
|
|
||||||
@personas={{this.allPersonas}}
|
|
||||||
@currentPersona={{this.model}}
|
|
||||||
/>
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
<AiPersonaListEditor @personas={{this.model}} />
|
|
||||||
-4
@@ -1,4 +0,0 @@
|
|||||||
<AiPersonaListEditor
|
|
||||||
@personas={{this.allPersonas}}
|
|
||||||
@currentPersona={{this.model}}
|
|
||||||
/>
|
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
<section class="ai-persona-tool-editor__current admin-detail pull-left">
|
<section class="ai-agent-tool-editor__current admin-detail pull-left">
|
||||||
<AiToolEditor
|
<AiToolEditor
|
||||||
@tools={{this.allTools}}
|
@tools={{this.allTools}}
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
<section class="ai-persona-tool-editor__current admin-detail pull-left">
|
<section class="ai-agent-tool-editor__current admin-detail pull-left">
|
||||||
<AiToolEditor
|
<AiToolEditor
|
||||||
@tools={{this.allTools}}
|
@tools={{this.allTools}}
|
||||||
@model={{this.model}}
|
@model={{this.model}}
|
||||||
|
|||||||
+39
-39
@@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
module DiscourseAi
|
module DiscourseAi
|
||||||
module Admin
|
module Admin
|
||||||
class AiPersonasController < ::Admin::AdminController
|
class AiAgentsController < ::Admin::AdminController
|
||||||
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
||||||
|
|
||||||
before_action :find_ai_persona, only: %i[edit update destroy create_user]
|
before_action :find_ai_agent, only: %i[edit update destroy create_user]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
ai_personas =
|
ai_agents =
|
||||||
AiPersona.ordered.map do |persona|
|
AiAgent.ordered.map do |agent|
|
||||||
# we use a special serializer here cause names and descriptions are
|
# we use a special serializer here cause names and descriptions are
|
||||||
# localized for system personas
|
# localized for system agents
|
||||||
LocalizedAiPersonaSerializer.new(persona, root: false)
|
LocalizedAiAgentSerializer.new(agent, root: false)
|
||||||
end
|
end
|
||||||
tools =
|
tools =
|
||||||
DiscourseAi::Personas::Persona.all_available_tools.map do |tool|
|
DiscourseAi::Agents::Agent.all_available_tools.map do |tool|
|
||||||
AiToolSerializer.new(tool, root: false)
|
AiToolSerializer.new(tool, root: false)
|
||||||
end
|
end
|
||||||
AiTool
|
AiTool
|
||||||
@@ -36,7 +36,7 @@ module DiscourseAi
|
|||||||
allowed_seeded_llm_ids: SiteSetting.ai_bot_allowed_seeded_models_map,
|
allowed_seeded_llm_ids: SiteSetting.ai_bot_allowed_seeded_models_map,
|
||||||
)
|
)
|
||||||
render json: {
|
render json: {
|
||||||
ai_personas: ai_personas,
|
ai_agents: ai_agents,
|
||||||
meta: {
|
meta: {
|
||||||
tools: tools,
|
tools: tools,
|
||||||
llms: llms,
|
llms: llms,
|
||||||
@@ -51,55 +51,55 @@ module DiscourseAi
|
|||||||
end
|
end
|
||||||
|
|
||||||
def edit
|
def edit
|
||||||
render json: LocalizedAiPersonaSerializer.new(@ai_persona)
|
render json: LocalizedAiAgentSerializer.new(@ai_agent)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
ai_persona = AiPersona.new(ai_persona_params.except(:rag_uploads))
|
ai_agent = AiAgent.new(ai_agent_params.except(:rag_uploads))
|
||||||
if ai_persona.save
|
if ai_agent.save
|
||||||
RagDocumentFragment.link_target_and_uploads(ai_persona, attached_upload_ids)
|
RagDocumentFragment.link_target_and_uploads(ai_agent, attached_upload_ids)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
ai_persona: LocalizedAiPersonaSerializer.new(ai_persona, root: false),
|
ai_agent: LocalizedAiAgentSerializer.new(ai_agent, root: false),
|
||||||
},
|
},
|
||||||
status: :created
|
status: :created
|
||||||
else
|
else
|
||||||
render_json_error ai_persona
|
render_json_error ai_agent
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_user
|
def create_user
|
||||||
user = @ai_persona.create_user!
|
user = @ai_agent.create_user!
|
||||||
render json: BasicUserSerializer.new(user, root: "user")
|
render json: BasicUserSerializer.new(user, root: "user")
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
if @ai_persona.update(ai_persona_params.except(:rag_uploads))
|
if @ai_agent.update(ai_agent_params.except(:rag_uploads))
|
||||||
RagDocumentFragment.update_target_uploads(@ai_persona, attached_upload_ids)
|
RagDocumentFragment.update_target_uploads(@ai_agent, attached_upload_ids)
|
||||||
|
|
||||||
render json: LocalizedAiPersonaSerializer.new(@ai_persona, root: false)
|
render json: LocalizedAiAgentSerializer.new(@ai_agent, root: false)
|
||||||
else
|
else
|
||||||
render_json_error @ai_persona
|
render_json_error @ai_agent
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
if @ai_persona.destroy
|
if @ai_agent.destroy
|
||||||
head :no_content
|
head :no_content
|
||||||
else
|
else
|
||||||
render_json_error @ai_persona
|
render_json_error @ai_agent
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def stream_reply
|
def stream_reply
|
||||||
persona =
|
agent =
|
||||||
AiPersona.find_by(name: params[:persona_name]) ||
|
AiAgent.find_by(name: params[:agent_name]) ||
|
||||||
AiPersona.find_by(id: params[:persona_id])
|
AiAgent.find_by(id: params[:agent_id])
|
||||||
return render_json_error(I18n.t("discourse_ai.errors.persona_not_found")) if persona.nil?
|
return render_json_error(I18n.t("discourse_ai.errors.agent_not_found")) if agent.nil?
|
||||||
|
|
||||||
return render_json_error(I18n.t("discourse_ai.errors.persona_disabled")) if !persona.enabled
|
return render_json_error(I18n.t("discourse_ai.errors.agent_disabled")) if !agent.enabled
|
||||||
|
|
||||||
if persona.default_llm.blank?
|
if agent.default_llm.blank?
|
||||||
return render_json_error(I18n.t("discourse_ai.errors.no_default_llm"))
|
return render_json_error(I18n.t("discourse_ai.errors.no_default_llm"))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -107,8 +107,8 @@ module DiscourseAi
|
|||||||
return render_json_error(I18n.t("discourse_ai.errors.no_query_specified"))
|
return render_json_error(I18n.t("discourse_ai.errors.no_query_specified"))
|
||||||
end
|
end
|
||||||
|
|
||||||
if !persona.user_id
|
if !agent.user_id
|
||||||
return render_json_error(I18n.t("discourse_ai.errors.no_user_for_persona"))
|
return render_json_error(I18n.t("discourse_ai.errors.no_user_for_agent"))
|
||||||
end
|
end
|
||||||
|
|
||||||
if !params[:username] && !params[:user_unique_id]
|
if !params[:username] && !params[:user_unique_id]
|
||||||
@@ -142,7 +142,7 @@ module DiscourseAi
|
|||||||
|
|
||||||
DiscourseAi::AiBot::ResponseHttpStreamer.queue_streamed_reply(
|
DiscourseAi::AiBot::ResponseHttpStreamer.queue_streamed_reply(
|
||||||
io: io,
|
io: io,
|
||||||
persona: persona,
|
agent: agent,
|
||||||
user: user,
|
user: user,
|
||||||
topic: topic,
|
topic: topic,
|
||||||
query: params[:query].to_s,
|
query: params[:query].to_s,
|
||||||
@@ -178,17 +178,17 @@ module DiscourseAi
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_ai_persona
|
def find_ai_agent
|
||||||
@ai_persona = AiPersona.find(params[:id])
|
@ai_agent = AiAgent.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
def attached_upload_ids
|
def attached_upload_ids
|
||||||
ai_persona_params[:rag_uploads].to_a.map { |h| h[:id] }
|
ai_agent_params[:rag_uploads].to_a.map { |h| h[:id] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def ai_persona_params
|
def ai_agent_params
|
||||||
permitted =
|
permitted =
|
||||||
params.require(:ai_persona).permit(
|
params.require(:ai_agent).permit(
|
||||||
:name,
|
:name,
|
||||||
:description,
|
:description,
|
||||||
:enabled,
|
:enabled,
|
||||||
@@ -209,7 +209,7 @@ module DiscourseAi
|
|||||||
:allow_chat_channel_mentions,
|
:allow_chat_channel_mentions,
|
||||||
:allow_chat_direct_messages,
|
:allow_chat_direct_messages,
|
||||||
:allow_topic_mentions,
|
:allow_topic_mentions,
|
||||||
:allow_personal_messages,
|
:allow_agentl_messages,
|
||||||
:tool_details,
|
:tool_details,
|
||||||
:forced_tool_count,
|
:forced_tool_count,
|
||||||
:force_default_llm,
|
:force_default_llm,
|
||||||
@@ -217,15 +217,15 @@ module DiscourseAi
|
|||||||
rag_uploads: [:id],
|
rag_uploads: [:id],
|
||||||
)
|
)
|
||||||
|
|
||||||
if tools = params.dig(:ai_persona, :tools)
|
if tools = params.dig(:ai_agent, :tools)
|
||||||
permitted[:tools] = permit_tools(tools)
|
permitted[:tools] = permit_tools(tools)
|
||||||
end
|
end
|
||||||
|
|
||||||
if response_format = params.dig(:ai_persona, :response_format)
|
if response_format = params.dig(:ai_agent, :response_format)
|
||||||
permitted[:response_format] = permit_response_format(response_format)
|
permitted[:response_format] = permit_response_format(response_format)
|
||||||
end
|
end
|
||||||
|
|
||||||
if examples = params.dig(:ai_persona, :examples)
|
if examples = params.dig(:ai_agent, :examples)
|
||||||
permitted[:examples] = permit_examples(examples)
|
permitted[:examples] = permit_examples(examples)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -17,19 +17,19 @@ module DiscourseAi
|
|||||||
private
|
private
|
||||||
|
|
||||||
def serialize_features(features)
|
def serialize_features(features)
|
||||||
features.map { |feature| feature.merge(persona: serialize_persona(feature[:persona])) }
|
features.map { |feature| feature.merge(agent: serialize_agent(feature[:agent])) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def serialize_feature(feature)
|
def serialize_feature(feature)
|
||||||
return nil if feature.blank?
|
return nil if feature.blank?
|
||||||
|
|
||||||
feature.merge(persona: serialize_persona(feature[:persona]))
|
feature.merge(agent: serialize_agent(feature[:agent]))
|
||||||
end
|
end
|
||||||
|
|
||||||
def serialize_persona(persona)
|
def serialize_agent(agent)
|
||||||
return nil if persona.blank?
|
return nil if agent.blank?
|
||||||
|
|
||||||
serialize_data(persona, AiFeaturesPersonaSerializer, root: false)
|
serialize_data(agent, AiFeaturesAgentSerializer, root: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ module DiscourseAi
|
|||||||
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
requires_plugin ::DiscourseAi::PLUGIN_NAME
|
||||||
|
|
||||||
def indexing_status_check
|
def indexing_status_check
|
||||||
if params[:target_type] == "AiPersona"
|
if params[:target_type] == "AiAgent"
|
||||||
@target = AiPersona.find(params[:target_id])
|
@target = AiAgent.find(params[:target_id])
|
||||||
elsif params[:target_type] == "AiTool"
|
elsif params[:target_type] == "AiTool"
|
||||||
@target = AiTool.find(params[:target_id])
|
@target = AiTool.find(params[:target_id])
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -46,17 +46,17 @@ module DiscourseAi
|
|||||||
end
|
end
|
||||||
|
|
||||||
def discover
|
def discover
|
||||||
ai_persona =
|
ai_agent =
|
||||||
AiPersona
|
AiAgent
|
||||||
.all_personas(enabled_only: false)
|
.all_agents(enabled_only: false)
|
||||||
.find { |persona| persona.id == SiteSetting.ai_bot_discover_persona.to_i }
|
.find { |agent| agent.id == SiteSetting.ai_bot_discover_agent.to_i }
|
||||||
|
|
||||||
if ai_persona.nil? || !current_user.in_any_groups?(ai_persona.allowed_group_ids.to_a)
|
if ai_agent.nil? || !current_user.in_any_groups?(ai_agent.allowed_group_ids.to_a)
|
||||||
raise Discourse::InvalidAccess.new
|
raise Discourse::InvalidAccess.new
|
||||||
end
|
end
|
||||||
|
|
||||||
if ai_persona.default_llm_id.blank?
|
if ai_agent.default_llm_id.blank?
|
||||||
render_json_error "Discover persona is missing a default LLM model.", status: 503
|
render_json_error "Discover agent is missing a default LLM model.", status: 503
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ module DiscourseAi
|
|||||||
|
|
||||||
user = User.find(params[:user_id])
|
user = User.find(params[:user_id])
|
||||||
|
|
||||||
bot_user_id = AiPersona.find_by(id: SiteSetting.ai_bot_discover_persona).user_id
|
bot_user_id = AiAgent.find_by(id: SiteSetting.ai_bot_discover_agent).user_id
|
||||||
bot_username = User.find_by(id: bot_user_id).username
|
bot_username = User.find_by(id: bot_user_id).username
|
||||||
|
|
||||||
query = params[:query]
|
query = params[:query]
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ module ::Jobs
|
|||||||
message = ::Chat::Message.find_by(id: args[:message_id])
|
message = ::Chat::Message.find_by(id: args[:message_id])
|
||||||
return if message.blank?
|
return if message.blank?
|
||||||
|
|
||||||
personaClass =
|
agentClass =
|
||||||
DiscourseAi::Personas::Persona.find_by(id: args[:persona_id], user: message.user)
|
DiscourseAi::Agents::Agent.find_by(id: args[:agent_id], user: message.user)
|
||||||
return if personaClass.blank?
|
return if agentClass.blank?
|
||||||
|
|
||||||
user = User.find_by(id: personaClass.user_id)
|
user = User.find_by(id: agentClass.user_id)
|
||||||
bot = DiscourseAi::Personas::Bot.as(user, persona: personaClass.new)
|
bot = DiscourseAi::Agents::Bot.as(user, agent: agentClass.new)
|
||||||
|
|
||||||
DiscourseAi::AiBot::Playground.new(bot).reply_to_chat_message(
|
DiscourseAi::AiBot::Playground.new(bot).reply_to_chat_message(
|
||||||
message,
|
message,
|
||||||
|
|||||||
@@ -7,18 +7,18 @@ module ::Jobs
|
|||||||
def execute(args)
|
def execute(args)
|
||||||
return unless bot_user = User.find_by(id: args[:bot_user_id])
|
return unless bot_user = User.find_by(id: args[:bot_user_id])
|
||||||
return unless post = Post.includes(:topic).find_by(id: args[:post_id])
|
return unless post = Post.includes(:topic).find_by(id: args[:post_id])
|
||||||
persona_id = args[:persona_id]
|
agent_id = args[:agent_id]
|
||||||
|
|
||||||
begin
|
begin
|
||||||
persona = DiscourseAi::Personas::Persona.find_by(user: post.user, id: persona_id)
|
agent = DiscourseAi::Agents::Agent.find_by(user: post.user, id: agent_id)
|
||||||
raise DiscourseAi::Personas::Bot::BOT_NOT_FOUND if persona.nil?
|
raise DiscourseAi::Agents::Bot::BOT_NOT_FOUND if agent.nil?
|
||||||
|
|
||||||
bot = DiscourseAi::Personas::Bot.as(bot_user, persona: persona.new)
|
bot = DiscourseAi::Agents::Bot.as(bot_user, agent: agent.new)
|
||||||
|
|
||||||
DiscourseAi::AiBot::Playground.new(bot).reply_to(post)
|
DiscourseAi::AiBot::Playground.new(bot).reply_to(post)
|
||||||
rescue DiscourseAi::Personas::Bot::BOT_NOT_FOUND
|
rescue DiscourseAi::Agents::Bot::BOT_NOT_FOUND
|
||||||
Rails.logger.warn(
|
Rails.logger.warn(
|
||||||
"Bot not found for post #{post.id} - perhaps persona was deleted or bot was disabled",
|
"Bot not found for post #{post.id} - perhaps agent was deleted or bot was disabled",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ module Jobs
|
|||||||
|
|
||||||
return unless SiteSetting.ai_discord_search_enabled
|
return unless SiteSetting.ai_discord_search_enabled
|
||||||
|
|
||||||
if SiteSetting.ai_discord_search_mode == "persona"
|
if SiteSetting.ai_discord_search_mode == "agent"
|
||||||
DiscourseAi::Discord::Bot::PersonaReplier.new(interaction).handle_interaction!
|
DiscourseAi::Discord::Bot::AgentReplier.new(interaction).handle_interaction!
|
||||||
else
|
else
|
||||||
DiscourseAi::Discord::Bot::Search.new(interaction).handle_interaction!
|
DiscourseAi::Discord::Bot::Search.new(interaction).handle_interaction!
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,20 +8,20 @@ module Jobs
|
|||||||
return if (user = User.find_by(id: args[:user_id])).nil?
|
return if (user = User.find_by(id: args[:user_id])).nil?
|
||||||
return if (query = args[:query]).blank?
|
return if (query = args[:query]).blank?
|
||||||
|
|
||||||
ai_persona_klass =
|
ai_agent_klass =
|
||||||
AiPersona
|
AiAgent
|
||||||
.all_personas(enabled_only: false)
|
.all_agents(enabled_only: false)
|
||||||
.find { |persona| persona.id == SiteSetting.ai_bot_discover_persona.to_i }
|
.find { |agent| agent.id == SiteSetting.ai_bot_discover_agent.to_i }
|
||||||
|
|
||||||
if ai_persona_klass.nil? || !user.in_any_groups?(ai_persona_klass.allowed_group_ids.to_a)
|
if ai_agent_klass.nil? || !user.in_any_groups?(ai_agent_klass.allowed_group_ids.to_a)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return if (llm_model = LlmModel.find_by(id: ai_persona_klass.default_llm_id)).nil?
|
return if (llm_model = LlmModel.find_by(id: ai_agent_klass.default_llm_id)).nil?
|
||||||
|
|
||||||
bot =
|
bot =
|
||||||
DiscourseAi::Personas::Bot.as(
|
DiscourseAi::Agents::Bot.as(
|
||||||
Discourse.system_user,
|
Discourse.system_user,
|
||||||
persona: ai_persona_klass.new,
|
agent: ai_agent_klass.new,
|
||||||
model: llm_model,
|
model: llm_model,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ module Jobs
|
|||||||
base = { query: query, model_used: llm_model.display_name }
|
base = { query: query, model_used: llm_model.display_name }
|
||||||
|
|
||||||
context =
|
context =
|
||||||
DiscourseAi::Personas::BotContext.new(
|
DiscourseAi::Agents::BotContext.new(
|
||||||
messages: [{ type: :user, content: query }],
|
messages: [{ type: :user, content: query }],
|
||||||
skip_tool_details: true,
|
skip_tool_details: true,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AiPersona < ActiveRecord::Base
|
class AiAgent < ActiveRecord::Base
|
||||||
# TODO remove this line 01-10-2025
|
# TODO remove this line 01-10-2025
|
||||||
self.ignored_columns = %i[default_llm question_consolidator_llm]
|
self.ignored_columns = %i[default_llm question_consolidator_llm]
|
||||||
|
|
||||||
# places a hard limit, so per site we cache a maximum of 500 classes
|
# places a hard limit, so per site we cache a maximum of 500 classes
|
||||||
MAX_PERSONAS_PER_SITE = 500
|
MAX_AGENTS_PER_SITE = 500
|
||||||
|
|
||||||
validates :name, presence: true, uniqueness: true, length: { maximum: 100 }
|
validates :name, presence: true, uniqueness: true, length: { maximum: 100 }
|
||||||
validates :description, presence: true, length: { maximum: 2000 }
|
validates :description, presence: true, length: { maximum: 2000 }
|
||||||
validates :system_prompt, presence: true, length: { maximum: 10_000_000 }
|
validates :system_prompt, presence: true, length: { maximum: 10_000_000 }
|
||||||
validate :system_persona_unchangeable, on: :update, if: :system
|
validate :system_agent_unchangeable, on: :update, if: :system
|
||||||
validate :chat_preconditions
|
validate :chat_preconditions
|
||||||
validate :allowed_seeded_model, if: :default_llm_id
|
validate :allowed_seeded_model, if: :default_llm_id
|
||||||
validate :well_formated_examples
|
validate :well_formated_examples
|
||||||
@@ -41,50 +41,50 @@ class AiPersona < ActiveRecord::Base
|
|||||||
before_destroy :ensure_not_system
|
before_destroy :ensure_not_system
|
||||||
before_update :regenerate_rag_fragments
|
before_update :regenerate_rag_fragments
|
||||||
|
|
||||||
def self.persona_cache
|
def self.agent_cache
|
||||||
@persona_cache ||= ::DiscourseAi::MultisiteHash.new("persona_cache")
|
@agent_cache ||= ::DiscourseAi::MultisiteHash.new("agent_cache")
|
||||||
end
|
end
|
||||||
|
|
||||||
scope :ordered, -> { order("priority DESC, lower(name) ASC") }
|
scope :ordered, -> { order("priority DESC, lower(name) ASC") }
|
||||||
|
|
||||||
def self.all_personas(enabled_only: true)
|
def self.all_agents(enabled_only: true)
|
||||||
persona_cache[:value] ||= AiPersona
|
agent_cache[:value] ||= AiAgent
|
||||||
.ordered
|
.ordered
|
||||||
.all
|
.all
|
||||||
.limit(MAX_PERSONAS_PER_SITE)
|
.limit(MAX_AGENTS_PER_SITE)
|
||||||
.map(&:class_instance)
|
.map(&:class_instance)
|
||||||
|
|
||||||
if enabled_only
|
if enabled_only
|
||||||
persona_cache[:value].select { |p| p.enabled }
|
agent_cache[:value].select { |p| p.enabled }
|
||||||
else
|
else
|
||||||
persona_cache[:value]
|
agent_cache[:value]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.persona_users(user: nil)
|
def self.agent_users(user: nil)
|
||||||
persona_users =
|
agent_users =
|
||||||
persona_cache[:persona_users] ||= AiPersona
|
agent_cache[:agent_users] ||= AiAgent
|
||||||
.where(enabled: true)
|
.where(enabled: true)
|
||||||
.joins(:user)
|
.joins(:user)
|
||||||
.map do |persona|
|
.map do |agent|
|
||||||
{
|
{
|
||||||
id: persona.id,
|
id: agent.id,
|
||||||
user_id: persona.user_id,
|
user_id: agent.user_id,
|
||||||
username: persona.user.username_lower,
|
username: agent.user.username_lower,
|
||||||
allowed_group_ids: persona.allowed_group_ids,
|
allowed_group_ids: agent.allowed_group_ids,
|
||||||
default_llm_id: persona.default_llm_id,
|
default_llm_id: agent.default_llm_id,
|
||||||
force_default_llm: persona.force_default_llm,
|
force_default_llm: agent.force_default_llm,
|
||||||
allow_chat_channel_mentions: persona.allow_chat_channel_mentions,
|
allow_chat_channel_mentions: agent.allow_chat_channel_mentions,
|
||||||
allow_chat_direct_messages: persona.allow_chat_direct_messages,
|
allow_chat_direct_messages: agent.allow_chat_direct_messages,
|
||||||
allow_topic_mentions: persona.allow_topic_mentions,
|
allow_topic_mentions: agent.allow_topic_mentions,
|
||||||
allow_personal_messages: persona.allow_personal_messages,
|
allow_agentl_messages: agent.allow_agentl_messages,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
if user
|
if user
|
||||||
persona_users.select { |persona_user| user.in_any_groups?(persona_user[:allowed_group_ids]) }
|
agent_users.select { |agent_user| user.in_any_groups?(agent_user[:allowed_group_ids]) }
|
||||||
else
|
else
|
||||||
persona_users
|
agent_users
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -93,31 +93,31 @@ class AiPersona < ActiveRecord::Base
|
|||||||
allow_chat_channel_mentions: false,
|
allow_chat_channel_mentions: false,
|
||||||
allow_chat_direct_messages: false,
|
allow_chat_direct_messages: false,
|
||||||
allow_topic_mentions: false,
|
allow_topic_mentions: false,
|
||||||
allow_personal_messages: false
|
allow_agentl_messages: false
|
||||||
)
|
)
|
||||||
index =
|
index =
|
||||||
"modality-#{allow_chat_channel_mentions}-#{allow_chat_direct_messages}-#{allow_topic_mentions}-#{allow_personal_messages}"
|
"modality-#{allow_chat_channel_mentions}-#{allow_chat_direct_messages}-#{allow_topic_mentions}-#{allow_agentl_messages}"
|
||||||
|
|
||||||
personas =
|
agents =
|
||||||
persona_cache[index.to_sym] ||= persona_users.select do |persona|
|
agent_cache[index.to_sym] ||= agent_users.select do |agent|
|
||||||
next true if allow_chat_channel_mentions && persona[:allow_chat_channel_mentions]
|
next true if allow_chat_channel_mentions && agent[:allow_chat_channel_mentions]
|
||||||
next true if allow_chat_direct_messages && persona[:allow_chat_direct_messages]
|
next true if allow_chat_direct_messages && agent[:allow_chat_direct_messages]
|
||||||
next true if allow_topic_mentions && persona[:allow_topic_mentions]
|
next true if allow_topic_mentions && agent[:allow_topic_mentions]
|
||||||
next true if allow_personal_messages && persona[:allow_personal_messages]
|
next true if allow_agentl_messages && agent[:allow_agentl_messages]
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
if user
|
if user
|
||||||
personas.select { |u| user.in_any_groups?(u[:allowed_group_ids]) }
|
agents.select { |u| user.in_any_groups?(u[:allowed_group_ids]) }
|
||||||
else
|
else
|
||||||
personas
|
agents
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
after_commit :bump_cache
|
after_commit :bump_cache
|
||||||
|
|
||||||
def bump_cache
|
def bump_cache
|
||||||
self.class.persona_cache.flush!
|
self.class.agent_cache.flush!
|
||||||
end
|
end
|
||||||
|
|
||||||
def tools_can_not_be_duplicated
|
def tools_can_not_be_duplicated
|
||||||
@@ -138,7 +138,7 @@ class AiPersona < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
if seen_tools.include?(inner_name)
|
if seen_tools.include?(inner_name)
|
||||||
errors.add(:tools, I18n.t("discourse_ai.ai_bot.personas.cannot_have_duplicate_tools"))
|
errors.add(:tools, I18n.t("discourse_ai.ai_bot.agents.cannot_have_duplicate_tools"))
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
seen_tools.add(inner_name)
|
seen_tools.add(inner_name)
|
||||||
@@ -154,7 +154,7 @@ class AiPersona < ActiveRecord::Base
|
|||||||
.pluck(:tool_name)
|
.pluck(:tool_name)
|
||||||
.each do |tool_name|
|
.each do |tool_name|
|
||||||
if builtin_tool_names.include?(tool_name.downcase)
|
if builtin_tool_names.include?(tool_name.downcase)
|
||||||
errors.add(:tools, I18n.t("discourse_ai.ai_bot.personas.cannot_have_duplicate_tools"))
|
errors.add(:tools, I18n.t("discourse_ai.ai_bot.agents.cannot_have_duplicate_tools"))
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -176,7 +176,7 @@ class AiPersona < ActiveRecord::Base
|
|||||||
allow_chat_channel_mentions
|
allow_chat_channel_mentions
|
||||||
allow_chat_direct_messages
|
allow_chat_direct_messages
|
||||||
allow_topic_mentions
|
allow_topic_mentions
|
||||||
allow_personal_messages
|
allow_agentl_messages
|
||||||
force_default_llm
|
force_default_llm
|
||||||
name
|
name
|
||||||
description
|
description
|
||||||
@@ -208,14 +208,14 @@ class AiPersona < ActiveRecord::Base
|
|||||||
if inner_name.start_with?("custom-")
|
if inner_name.start_with?("custom-")
|
||||||
custom_tool_id = inner_name.split("-", 2).last.to_i
|
custom_tool_id = inner_name.split("-", 2).last.to_i
|
||||||
if AiTool.exists?(id: custom_tool_id, enabled: true)
|
if AiTool.exists?(id: custom_tool_id, enabled: true)
|
||||||
klass = DiscourseAi::Personas::Tools::Custom.class_instance(custom_tool_id)
|
klass = DiscourseAi::Agents::Tools::Custom.class_instance(custom_tool_id)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
inner_name = inner_name.gsub("Tool", "")
|
inner_name = inner_name.gsub("Tool", "")
|
||||||
inner_name = "List#{inner_name}" if %w[Categories Tags].include?(inner_name)
|
inner_name = "List#{inner_name}" if %w[Categories Tags].include?(inner_name)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
klass = "DiscourseAi::Personas::Tools::#{inner_name}".constantize
|
klass = "DiscourseAi::Agents::Tools::#{inner_name}".constantize
|
||||||
options[klass] = current_options if current_options
|
options[klass] = current_options if current_options
|
||||||
rescue StandardError
|
rescue StandardError
|
||||||
end
|
end
|
||||||
@@ -225,14 +225,14 @@ class AiPersona < ActiveRecord::Base
|
|||||||
klass
|
klass
|
||||||
end
|
end
|
||||||
|
|
||||||
persona_class = DiscourseAi::Personas::Persona.system_personas_by_id[self.id]
|
agent_class = DiscourseAi::Agents::Agent.system_agents_by_id[self.id]
|
||||||
if persona_class
|
if agent_class
|
||||||
return(
|
return(
|
||||||
# we need a new copy so we don't leak information
|
# we need a new copy so we don't leak information
|
||||||
# across sites
|
# across sites
|
||||||
Class.new(persona_class) do
|
Class.new(agent_class) do
|
||||||
# required for localization
|
# required for localization
|
||||||
define_singleton_method(:to_s) { persona_class.to_s }
|
define_singleton_method(:to_s) { agent_class.to_s }
|
||||||
instance_attributes.each do |key, value|
|
instance_attributes.each do |key, value|
|
||||||
# description/name are localized
|
# description/name are localized
|
||||||
define_singleton_method(key) { value } if key != :description && key != :name
|
define_singleton_method(key) { value } if key != :description && key != :name
|
||||||
@@ -242,9 +242,9 @@ class AiPersona < ActiveRecord::Base
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
ai_persona_id = self.id
|
ai_agent_id = self.id
|
||||||
|
|
||||||
Class.new(DiscourseAi::Personas::Persona) do
|
Class.new(DiscourseAi::Agents::Agent) do
|
||||||
instance_attributes.each { |key, value| define_singleton_method(key) { value } }
|
instance_attributes.each { |key, value| define_singleton_method(key) { value } }
|
||||||
|
|
||||||
define_singleton_method(:to_s) do
|
define_singleton_method(:to_s) do
|
||||||
@@ -254,24 +254,24 @@ class AiPersona < ActiveRecord::Base
|
|||||||
define_singleton_method(:inspect) { to_s }
|
define_singleton_method(:inspect) { to_s }
|
||||||
|
|
||||||
define_method(:initialize) do |*args, **kwargs|
|
define_method(:initialize) do |*args, **kwargs|
|
||||||
@ai_persona = AiPersona.find_by(id: ai_persona_id)
|
@ai_agent = AiAgent.find_by(id: ai_agent_id)
|
||||||
super(*args, **kwargs)
|
super(*args, **kwargs)
|
||||||
end
|
end
|
||||||
|
|
||||||
define_method(:tools) { tools }
|
define_method(:tools) { tools }
|
||||||
define_method(:force_tool_use) { force_tool_use }
|
define_method(:force_tool_use) { force_tool_use }
|
||||||
define_method(:forced_tool_count) { @ai_persona&.forced_tool_count }
|
define_method(:forced_tool_count) { @ai_agent&.forced_tool_count }
|
||||||
define_method(:options) { options }
|
define_method(:options) { options }
|
||||||
define_method(:temperature) { @ai_persona&.temperature }
|
define_method(:temperature) { @ai_agent&.temperature }
|
||||||
define_method(:top_p) { @ai_persona&.top_p }
|
define_method(:top_p) { @ai_agent&.top_p }
|
||||||
define_method(:system_prompt) { @ai_persona&.system_prompt || "You are a helpful bot." }
|
define_method(:system_prompt) { @ai_agent&.system_prompt || "You are a helpful bot." }
|
||||||
define_method(:uploads) { @ai_persona&.uploads }
|
define_method(:uploads) { @ai_agent&.uploads }
|
||||||
define_method(:response_format) { @ai_persona&.response_format }
|
define_method(:response_format) { @ai_agent&.response_format }
|
||||||
define_method(:examples) { @ai_persona&.examples }
|
define_method(:examples) { @ai_agent&.examples }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
FIRST_PERSONA_USER_ID = -1200
|
FIRST_AGENT_USER_ID = -1200
|
||||||
|
|
||||||
def create_user!
|
def create_user!
|
||||||
raise "User already exists" if user_id && User.exists?(user_id)
|
raise "User already exists" if user_id && User.exists?(user_id)
|
||||||
@@ -279,7 +279,7 @@ class AiPersona < ActiveRecord::Base
|
|||||||
# find the first id smaller than FIRST_USER_ID that is not taken
|
# find the first id smaller than FIRST_USER_ID that is not taken
|
||||||
id = nil
|
id = nil
|
||||||
|
|
||||||
id = DB.query_single(<<~SQL, FIRST_PERSONA_USER_ID, FIRST_PERSONA_USER_ID - 200).first
|
id = DB.query_single(<<~SQL, FIRST_AGENT_USER_ID, FIRST_AGENT_USER_ID - 200).first
|
||||||
WITH seq AS (
|
WITH seq AS (
|
||||||
SELECT generate_series(?, ?, -1) AS id
|
SELECT generate_series(?, ?, -1) AS id
|
||||||
)
|
)
|
||||||
@@ -323,12 +323,12 @@ class AiPersona < ActiveRecord::Base
|
|||||||
allow_chat_channel_mentions || allow_chat_direct_messages || allow_topic_mentions ||
|
allow_chat_channel_mentions || allow_chat_direct_messages || allow_topic_mentions ||
|
||||||
force_default_llm
|
force_default_llm
|
||||||
) && !default_llm_id
|
) && !default_llm_id
|
||||||
errors.add(:default_llm, I18n.t("discourse_ai.ai_bot.personas.default_llm_required"))
|
errors.add(:default_llm, I18n.t("discourse_ai.ai_bot.agents.default_llm_required"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def system_persona_unchangeable
|
def system_agent_unchangeable
|
||||||
error_msg = I18n.t("discourse_ai.ai_bot.personas.cannot_edit_system_persona")
|
error_msg = I18n.t("discourse_ai.ai_bot.agents.cannot_edit_system_agent")
|
||||||
|
|
||||||
if top_p_changed? || temperature_changed? || system_prompt_changed? || name_changed? ||
|
if top_p_changed? || temperature_changed? || system_prompt_changed? || name_changed? ||
|
||||||
description_changed?
|
description_changed?
|
||||||
@@ -356,7 +356,7 @@ class AiPersona < ActiveRecord::Base
|
|||||||
|
|
||||||
def ensure_not_system
|
def ensure_not_system
|
||||||
if system
|
if system
|
||||||
errors.add(:base, I18n.t("discourse_ai.ai_bot.personas.cannot_delete_system_persona"))
|
errors.add(:base, I18n.t("discourse_ai.ai_bot.agents.cannot_delete_system_agent"))
|
||||||
throw :abort
|
throw :abort
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -380,13 +380,13 @@ class AiPersona < ActiveRecord::Base
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
errors.add(:examples, I18n.t("discourse_ai.personas.malformed_examples"))
|
errors.add(:examples, I18n.t("discourse_ai.agents.malformed_examples"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
#
|
#
|
||||||
# Table name: ai_personas
|
# Table name: ai_agents
|
||||||
#
|
#
|
||||||
# id :bigint not null, primary key
|
# id :bigint not null, primary key
|
||||||
# name :string(100) not null
|
# name :string(100) not null
|
||||||
@@ -414,7 +414,7 @@ end
|
|||||||
# allow_chat_channel_mentions :boolean default(FALSE), not null
|
# allow_chat_channel_mentions :boolean default(FALSE), not null
|
||||||
# allow_chat_direct_messages :boolean default(FALSE), not null
|
# allow_chat_direct_messages :boolean default(FALSE), not null
|
||||||
# allow_topic_mentions :boolean default(FALSE), not null
|
# allow_topic_mentions :boolean default(FALSE), not null
|
||||||
# allow_personal_messages :boolean default(TRUE), not null
|
# allow_agentl_messages :boolean default(TRUE), not null
|
||||||
# force_default_llm :boolean default(FALSE), not null
|
# force_default_llm :boolean default(FALSE), not null
|
||||||
# rag_llm_model_id :bigint
|
# rag_llm_model_id :bigint
|
||||||
# default_llm_id :bigint
|
# default_llm_id :bigint
|
||||||
@@ -424,5 +424,5 @@ end
|
|||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
# index_ai_personas_on_name (name) UNIQUE
|
# index_ai_agents_on_name (name) UNIQUE
|
||||||
#
|
#
|
||||||
@@ -36,7 +36,7 @@ class AiTool < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def runner(parameters, llm:, bot_user:, context: nil)
|
def runner(parameters, llm:, bot_user:, context: nil)
|
||||||
DiscourseAi::Personas::ToolRunner.new(
|
DiscourseAi::Agents::ToolRunner.new(
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
llm: llm,
|
llm: llm,
|
||||||
bot_user: bot_user,
|
bot_user: bot_user,
|
||||||
@@ -45,10 +45,10 @@ class AiTool < ActiveRecord::Base
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
after_commit :bump_persona_cache
|
after_commit :bump_agent_cache
|
||||||
|
|
||||||
def bump_persona_cache
|
def bump_agent_cache
|
||||||
AiPersona.persona_cache.flush!
|
AiAgent.agent_cache.flush!
|
||||||
end
|
end
|
||||||
|
|
||||||
def regenerate_rag_fragments
|
def regenerate_rag_fragments
|
||||||
@@ -176,11 +176,11 @@ class AiTool < ActiveRecord::Base
|
|||||||
* user_id_or_username (number | string): The ID or username of the user.
|
* user_id_or_username (number | string): The ID or username of the user.
|
||||||
* Returns: Object (User details using UserSerializer structure) or null if not found.
|
* Returns: Object (User details using UserSerializer structure) or null if not found.
|
||||||
*
|
*
|
||||||
* discourse.getPersona(name): Gets an object representing another AI Persona configured on the site.
|
* discourse.getAgent(name): Gets an object representing another AI Agent configured on the site.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* name (string): The name of the target persona.
|
* name (string): The name of the target agent.
|
||||||
* Returns: Object { respondTo: function(params) } or null if persona not found.
|
* Returns: Object { respondTo: function(params) } or null if agent not found.
|
||||||
* respondTo(params): Instructs the target persona to generate a response within the current context (e.g., replying to the same post or chat message).
|
* respondTo(params): Instructs the target agent to generate a response within the current context (e.g., replying to the same post or chat message).
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* params (Object, optional): { instructions: string, whisper: boolean }
|
* params (Object, optional): { instructions: string, whisper: boolean }
|
||||||
* Returns: { success: boolean, post_id?: number, post_number?: number, message_id?: number } or { error: string }
|
* Returns: { success: boolean, post_id?: number, post_number?: number, message_id?: number } or { error: string }
|
||||||
@@ -201,7 +201,7 @@ class AiTool < ActiveRecord::Base
|
|||||||
* private_message (boolean): Whether the context is a private message (in Post context).
|
* private_message (boolean): Whether the context is a private message (in Post context).
|
||||||
* message_id (number): ID of the chat message triggering the tool (if in Chat context).
|
* message_id (number): ID of the chat message triggering the tool (if in Chat context).
|
||||||
* channel_id (number): ID of the chat channel (if in Chat context).
|
* channel_id (number): ID of the chat channel (if in Chat context).
|
||||||
* user (Object): Details of the user invoking the tool/persona (structure may vary, often null or SystemUser details unless explicitly passed).
|
* user (Object): Details of the user invoking the tool/agent (structure may vary, often null or SystemUser details unless explicitly passed).
|
||||||
* participants (string): Comma-separated list of usernames in a PM (if applicable).
|
* participants (string): Comma-separated list of usernames in a PM (if applicable).
|
||||||
* // ... other potential context-specific properties added by the calling environment.
|
* // ... other potential context-specific properties added by the calling environment.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
class RagDocumentFragment < ActiveRecord::Base
|
class RagDocumentFragment < ActiveRecord::Base
|
||||||
# TODO Jan 2025 - remove
|
# TODO Jan 2025 - remove
|
||||||
self.ignored_columns = %i[ai_persona_id]
|
self.ignored_columns = %i[ai_agent_id]
|
||||||
|
|
||||||
belongs_to :upload
|
belongs_to :upload
|
||||||
belongs_to :target, polymorphic: true
|
belongs_to :target, polymorphic: true
|
||||||
@@ -38,7 +38,7 @@ class RagDocumentFragment < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def indexing_status(persona, uploads)
|
def indexing_status(agent, uploads)
|
||||||
embeddings_table = DiscourseAi::Embeddings::Schema.for(self).table
|
embeddings_table = DiscourseAi::Embeddings::Schema.for(self).table
|
||||||
|
|
||||||
results =
|
results =
|
||||||
@@ -56,8 +56,8 @@ class RagDocumentFragment < ActiveRecord::Base
|
|||||||
WHERE uploads.id IN (:upload_ids)
|
WHERE uploads.id IN (:upload_ids)
|
||||||
GROUP BY uploads.id
|
GROUP BY uploads.id
|
||||||
SQL
|
SQL
|
||||||
target_id: persona.id,
|
target_id: agent.id,
|
||||||
target_type: persona.class.to_s,
|
target_type: agent.class.to_s,
|
||||||
upload_ids: uploads.map(&:id),
|
upload_ids: uploads.map(&:id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ class SharedAiConversation < ActiveRecord::Base
|
|||||||
# but this name works
|
# but this name works
|
||||||
class SharedPost
|
class SharedPost
|
||||||
attr_accessor :user
|
attr_accessor :user
|
||||||
attr_reader :id, :user_id, :created_at, :cooked, :persona
|
attr_reader :id, :user_id, :created_at, :cooked, :agent
|
||||||
def initialize(post)
|
def initialize(post)
|
||||||
@id = post[:id]
|
@id = post[:id]
|
||||||
@user_id = post[:user_id]
|
@user_id = post[:user_id]
|
||||||
@created_at = DateTime.parse(post[:created_at])
|
@created_at = DateTime.parse(post[:created_at])
|
||||||
@cooked = post[:cooked]
|
@cooked = post[:cooked]
|
||||||
@persona = post[:persona]
|
@agent = post[:agent]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -140,9 +140,9 @@ class SharedAiConversation < ActiveRecord::Base
|
|||||||
llm_name = ActiveSupport::Inflector.humanize(llm_name) if llm_name
|
llm_name = ActiveSupport::Inflector.humanize(llm_name) if llm_name
|
||||||
llm_name ||= I18n.t("discourse_ai.unknown_model")
|
llm_name ||= I18n.t("discourse_ai.unknown_model")
|
||||||
|
|
||||||
persona = nil
|
agent = nil
|
||||||
if persona_id = topic.custom_fields["ai_persona_id"]
|
if agent_id = topic.custom_fields["ai_agent_id"]
|
||||||
persona = AiPersona.find_by(id: persona_id.to_i)&.name
|
agent = AiAgent.find_by(id: agent_id.to_i)&.name
|
||||||
end
|
end
|
||||||
|
|
||||||
posts =
|
posts =
|
||||||
@@ -167,7 +167,7 @@ class SharedAiConversation < ActiveRecord::Base
|
|||||||
cooked: cook_artifacts(post),
|
cooked: cook_artifacts(post),
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped[:persona] = persona if ai_bot_participant&.id == post.user_id
|
mapped[:agent] = agent if ai_bot_participant&.id == post.user_id
|
||||||
mapped[:username] = post.user&.username if include_usernames
|
mapped[:username] = post.user&.username if include_usernames
|
||||||
mapped
|
mapped
|
||||||
end,
|
end,
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AiFeaturesPersonaSerializer < ApplicationSerializer
|
class AiFeaturesAgentSerializer < ApplicationSerializer
|
||||||
attributes :id, :name, :system_prompt, :allowed_groups, :enabled
|
attributes :id, :name, :system_prompt, :allowed_groups, :enabled
|
||||||
|
|
||||||
def allowed_groups
|
def allowed_groups
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
class LlmModelSerializer < ApplicationSerializer
|
class LlmModelSerializer < ApplicationSerializer
|
||||||
# TODO: we probably should rename the table LlmModel to AiLlm
|
# TODO: we probably should rename the table LlmModel to AiLlm
|
||||||
# it is consistent with AiPersona and AiTool
|
# it is consistent with AiAgent and AiTool
|
||||||
# LLM model is a bit confusing given that large langauge model model is a confusing
|
# LLM model is a bit confusing given that large langauge model model is a confusing
|
||||||
# name
|
# name
|
||||||
root "ai_llm"
|
root "ai_llm"
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class LocalizedAiPersonaSerializer < ApplicationSerializer
|
class LocalizedAiAgentSerializer < ApplicationSerializer
|
||||||
root "ai_persona"
|
root "ai_agent"
|
||||||
|
|
||||||
attributes :id,
|
attributes :id,
|
||||||
:name,
|
:name,
|
||||||
@@ -29,7 +29,7 @@ class LocalizedAiPersonaSerializer < ApplicationSerializer
|
|||||||
:allow_chat_channel_mentions,
|
:allow_chat_channel_mentions,
|
||||||
:allow_chat_direct_messages,
|
:allow_chat_direct_messages,
|
||||||
:allow_topic_mentions,
|
:allow_topic_mentions,
|
||||||
:allow_personal_messages,
|
:allow_agentl_messages,
|
||||||
:force_default_llm,
|
:force_default_llm,
|
||||||
:response_format,
|
:response_format,
|
||||||
:examples
|
:examples
|
||||||
@@ -43,8 +43,8 @@
|
|||||||
<article class="post">
|
<article class="post">
|
||||||
<header class="post__header">
|
<header class="post__header">
|
||||||
<span class="post__user"><%= post.user.username %></span>
|
<span class="post__user"><%= post.user.username %></span>
|
||||||
<%if post.persona.present? %>
|
<%if post.agent.present? %>
|
||||||
<span class="post__persona"><%= post.persona %></span>
|
<span class="post__agent"><%= post.agent %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
<time class="post__date" datetime="<%= post.created_at.iso8601 %>"><%= post.created_at.strftime('%Y-%m-%d') %></time>
|
<time class="post__date" datetime="<%= post.created_at.iso8601 %>"><%= post.created_at.strftime('%Y-%m-%d') %></time>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user