WIP: add features to persona list, and other style updates

This commit is contained in:
awesomerobot 2025-06-04 19:54:41 -04:00 committed by Roman Rizzi
parent f7e0ea888d
commit 411da2ee88
No known key found for this signature in database
GPG Key ID: 64024A71CE7330D3
4 changed files with 68 additions and 17 deletions

View File

@ -8,16 +8,25 @@ module DiscourseAi
before_action :find_ai_persona, only: %i[edit update destroy create_user] before_action :find_ai_persona, only: %i[edit update destroy create_user]
def index def index
features_by_persona_id = DiscourseAi::Features.features.group_by { |f| f[:persona]&.id }
ai_personas = ai_personas =
AiPersona.ordered.map do |persona| AiPersona
# we use a special serializer here cause names and descriptions are .ordered
# localized for system personas .includes(:user, :uploads)
LocalizedAiPersonaSerializer.new(persona, root: false) .map do |persona|
end LocalizedAiPersonaSerializer.new(
persona,
root: false,
features_by_persona_id: features_by_persona_id,
)
end
tools = tools =
DiscourseAi::Personas::Persona.all_available_tools.map do |tool| DiscourseAi::Personas::Persona.all_available_tools.map do |tool|
AiToolSerializer.new(tool, root: false) AiToolSerializer.new(tool, root: false)
end end
AiTool AiTool
.where(enabled: true) .where(enabled: true)
.each do |tool| .each do |tool|
@ -31,10 +40,12 @@ module DiscourseAi
), ),
} }
end end
llms = llms =
DiscourseAi::Configuration::LlmEnumerator.values_for_serialization( DiscourseAi::Configuration::LlmEnumerator.values_for_serialization(
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_personas: ai_personas,
meta: { meta: {

View File

@ -3,6 +3,11 @@
class LocalizedAiPersonaSerializer < ApplicationSerializer class LocalizedAiPersonaSerializer < ApplicationSerializer
root "ai_persona" root "ai_persona"
def initialize(object, options = {})
@features_by_persona_id = options.delete(:features_by_persona_id)
super(object, options)
end
attributes :id, attributes :id,
:name, :name,
:description, :description,
@ -32,7 +37,8 @@ class LocalizedAiPersonaSerializer < ApplicationSerializer
:allow_personal_messages, :allow_personal_messages,
:force_default_llm, :force_default_llm,
:response_format, :response_format,
:examples :examples,
:features
has_one :user, serializer: BasicUserSerializer, embed: :object has_one :user, serializer: BasicUserSerializer, embed: :object
has_many :rag_uploads, serializer: UploadSerializer, embed: :object has_many :rag_uploads, serializer: UploadSerializer, embed: :object

View File

@ -1,13 +1,12 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { fn } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { LinkTo } from "@ember/routing"; import { LinkTo } from "@ember/routing";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item"; import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
import DButton from "discourse/components/d-button";
import DPageSubheader from "discourse/components/d-page-subheader"; import DPageSubheader from "discourse/components/d-page-subheader";
import DToggleSwitch from "discourse/components/d-toggle-switch";
import concatClass from "discourse/helpers/concat-class"; import concatClass from "discourse/helpers/concat-class";
import icon from "discourse/helpers/d-icon";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list"; import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
@ -61,17 +60,18 @@ export default class AiPersonaListEditor extends Component {
<thead> <thead>
<tr> <tr>
<th>{{i18n "discourse_ai.ai_persona.name"}}</th> <th>{{i18n "discourse_ai.ai_persona.name"}}</th>
<th>{{i18n "discourse_ai.ai_persona.list.enabled"}}</th> <th>{{i18n "discourse_ai.features.short_title"}}</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{{#each @personas as |persona|}} {{#each @personas as |persona|}}
{{log persona}}
<tr <tr
data-persona-id={{persona.id}} data-persona-id={{persona.id}}
class={{concatClass class={{concatClass
"ai-persona-list__row d-admin-row__content" "ai-persona-list__row d-admin-row__content"
(if persona.priority "priority") (if persona.priority "--priority")
(if persona.enabled "--enabled")
}} }}
> >
<td class="d-admin-row__overview"> <td class="d-admin-row__overview">
@ -79,6 +79,7 @@ export default class AiPersonaListEditor extends Component {
<div class="ai-persona-list__name"> <div class="ai-persona-list__name">
<strong> <strong>
{{persona.name}} {{persona.name}}
{{#if persona.enabled}}{{icon "check"}}{{/if}}
</strong> </strong>
</div> </div>
<div class="ai-persona-list__description"> <div class="ai-persona-list__description">
@ -86,12 +87,20 @@ export default class AiPersonaListEditor extends Component {
</div> </div>
</div> </div>
</td> </td>
<td class="d-admin-row__detail">
<DToggleSwitch <td class="d-admin-row__features">
@state={{persona.enabled}} {{#each persona.features as |feature|}}
{{on "click" (fn this.toggleEnabled persona)}} {{log persona}}
/> <DButton
class="btn-flat btn-small ai-persona-list__row-item-feature"
@translatedLabel={{feature.name}}
@route="adminPlugins.show.discourse-ai-features.edit"
@routeModels={{feature.id}}
/>
{{/each}}
</td> </td>
<td class="d-admin-row__controls"> <td class="d-admin-row__controls">
<LinkTo <LinkTo
@route="adminPlugins.show.discourse-ai-personas.edit" @route="adminPlugins.show.discourse-ai-personas.edit"

View File

@ -21,6 +21,31 @@
li.disabled { li.disabled {
opacity: 0.5; opacity: 0.5;
} }
.ai-persona-list {
&__row-item-feature {
padding: 0;
margin-right: 0.5em;
text-align: left;
}
&__name {
.d-icon {
color: var(--success);
font-size: var(--font-down-1);
}
}
&__row {
&:hover {
background: transparent;
}
&__overview {
padding-left: var(--space-2);
}
}
}
} }
.ai-persona-tool-option-editor { .ai-persona-tool-option-editor {