FEATURE: Improve admin plugin UI and use new plugins show route (#512)
This commit changes Discourse AI's admin plugin page to use the new plugin show route. The UI for persona editing has also been improved for consistency, and other plugin UIs will follow suit: Settings for the plugin are now listed in the plugin UI and can be changed from there directly after core PR discourse/discourse#26154 is merged. See also: * https://github.com/discourse/discourse/pull/26024 * https://github.com/discourse/discourse/pull/26154 * https://github.com/discourse/discourse/pull/26254
This commit is contained in:
parent
5cac47a30a
commit
fb0d56324f
|
@ -1,10 +1,10 @@
|
|||
export default {
|
||||
resource: "admin.adminPlugins",
|
||||
resource: "admin.adminPlugins.show",
|
||||
|
||||
path: "/plugins",
|
||||
|
||||
map() {
|
||||
this.route("discourse-ai", function () {
|
||||
this.route("discourse-ai", { path: "/" }, function () {
|
||||
this.route("ai-personas", function () {
|
||||
this.route("new");
|
||||
this.route("show", { path: "/:id" });
|
|
@ -8,6 +8,7 @@ import didUpdate from "@ember/render-modifiers/modifiers/did-update";
|
|||
import { LinkTo } from "@ember/routing";
|
||||
import { later } from "@ember/runloop";
|
||||
import { inject as service } from "@ember/service";
|
||||
import BackButton from "discourse/components/back-button";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import Textarea from "discourse/components/d-textarea";
|
||||
import DToggleSwitch from "discourse/components/d-toggle-switch";
|
||||
|
@ -58,7 +59,7 @@ export default class PersonaEditor extends Component {
|
|||
if (isNew) {
|
||||
this.args.personas.addObject(this.args.model);
|
||||
this.router.transitionTo(
|
||||
"adminPlugins.discourse-ai.ai-personas.show",
|
||||
"adminPlugins.show.discourse-ai.ai-personas.show",
|
||||
this.args.model
|
||||
);
|
||||
} else {
|
||||
|
@ -109,7 +110,7 @@ export default class PersonaEditor extends Component {
|
|||
return this.args.model.destroyRecord().then(() => {
|
||||
this.args.personas.removeObject(this.args.model);
|
||||
this.router.transitionTo(
|
||||
"adminPlugins.discourse-ai.ai-personas.index"
|
||||
"adminPlugins.show.discourse-ai.ai-personas.index"
|
||||
);
|
||||
});
|
||||
},
|
||||
|
@ -180,6 +181,10 @@ export default class PersonaEditor extends Component {
|
|||
}
|
||||
|
||||
<template>
|
||||
<BackButton
|
||||
@route="adminPlugins.show.discourse-ai.ai-personas"
|
||||
@label="discourse_ai.ai_persona.back"
|
||||
/>
|
||||
<form
|
||||
class="form-horizontal ai-persona-editor"
|
||||
{{didUpdate this.updateModel @model.id}}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { fn } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import DToggleSwitch from "discourse/components/d-toggle-switch";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { cook } from "discourse/lib/text";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import I18n from "discourse-i18n";
|
||||
import AiPersonaEditor from "./ai-persona-editor";
|
||||
|
||||
|
@ -21,45 +27,93 @@ export default class AiPersonaListEditor extends Component {
|
|||
return this._noPersonaText;
|
||||
}
|
||||
|
||||
@action
|
||||
async toggleEnabled(persona) {
|
||||
const oldValue = persona.enabled;
|
||||
const newValue = !oldValue;
|
||||
|
||||
try {
|
||||
persona.set("enabled", newValue);
|
||||
await persona.save();
|
||||
} catch (err) {
|
||||
persona.set("enabled", oldValue);
|
||||
popupAjaxError(err);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="ai-persona-list-editor__header">
|
||||
<h3>{{I18n.t "discourse_ai.ai_persona.title"}}</h3>
|
||||
{{#unless @currentPersona.isNew}}
|
||||
<LinkTo
|
||||
@route="adminPlugins.discourse-ai.ai-personas.new"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
{{icon "plus"}}
|
||||
<span>{{I18n.t "discourse_ai.ai_persona.new"}}</span>
|
||||
</LinkTo>
|
||||
{{/unless}}
|
||||
</div>
|
||||
<div class="content-list ai-persona-list-editor">
|
||||
<ul>
|
||||
{{#each @personas as |persona|}}
|
||||
<li
|
||||
class={{concatClass
|
||||
(if persona.enabled "" "diabled")
|
||||
(if persona.priority "priority")
|
||||
}}
|
||||
>
|
||||
<LinkTo
|
||||
@route="adminPlugins.discourse-ai.ai-personas.show"
|
||||
current-when="true"
|
||||
@model={{persona}}
|
||||
>{{persona.name}}
|
||||
</LinkTo>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</div>
|
||||
<section class="ai-persona-list-editor__current content-body">
|
||||
<section class="ai-persona-list-editor__current admin-detail pull-left">
|
||||
{{#if @currentPersona}}
|
||||
<AiPersonaEditor @model={{@currentPersona}} @personas={{@personas}} />
|
||||
{{else}}
|
||||
<div class="ai-persona-list-editor__empty">
|
||||
{{this.noPersonaText}}
|
||||
<div class="ai-persona-list-editor__header">
|
||||
<h3>{{i18n "discourse_ai.ai_persona.short_title"}}</h3>
|
||||
{{#unless @currentPersona.isNew}}
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai.ai-personas.new"
|
||||
class="btn btn-small btn-primary"
|
||||
>
|
||||
{{icon "plus"}}
|
||||
<span>{{I18n.t "discourse_ai.ai_persona.new"}}</span>
|
||||
</LinkTo>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
<div class="ai-persona-list-editor__empty">
|
||||
<details class="details__boxed">
|
||||
<summary>{{i18n
|
||||
"discourse_ai.ai_persona.what_are_personas"
|
||||
}}</summary>
|
||||
{{this.noPersonaText}}
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<table class="content-list ai-persona-list-editor">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{i18n "discourse_ai.ai_persona.name"}}</th>
|
||||
<th>{{i18n "discourse_ai.ai_persona.enabled"}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each @personas as |persona|}}
|
||||
<tr
|
||||
data-persona-id={{persona.id}}
|
||||
class={{concatClass
|
||||
"ai-persona-list__row"
|
||||
(if persona.priority "priority")
|
||||
}}
|
||||
>
|
||||
<td>
|
||||
<div class="ai-persona-list__name-with-description">
|
||||
<div class="ai-persona-list__name">
|
||||
<strong>
|
||||
{{persona.name}}
|
||||
</strong>
|
||||
</div>
|
||||
<div class="ai-persona-list__description">
|
||||
{{persona.description}}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<DToggleSwitch
|
||||
@state={{persona.enabled}}
|
||||
{{on "click" (fn this.toggleEnabled persona)}}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<LinkTo
|
||||
@route="adminPlugins.show.discourse-ai.ai-personas.show"
|
||||
@model={{persona}}
|
||||
class="btn btn-text btn-small"
|
||||
>{{i18n "discourse_ai.ai_persona.edit"}} </LinkTo>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{/if}}
|
||||
</section>
|
||||
</template>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import { inject as service } from "@ember/service";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
router: service(),
|
||||
beforeModel() {
|
||||
this.router.transitionTo("adminPlugins.discourse-ai.ai-personas");
|
||||
},
|
||||
});
|
|
@ -2,6 +2,6 @@ import DiscourseRoute from "discourse/routes/discourse";
|
|||
|
||||
export default DiscourseRoute.extend({
|
||||
model() {
|
||||
return this.modelFor("adminPlugins.discourse-ai.ai-personas");
|
||||
return this.modelFor("adminPlugins.show.discourse-ai.ai-personas");
|
||||
},
|
||||
});
|
|
@ -1,10 +1,10 @@
|
|||
import { AUTO_GROUPS } from "discourse/lib/constants";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
async model() {
|
||||
const record = this.store.createRecord("ai-persona");
|
||||
// TL0
|
||||
record.set("allowed_group_ids", [10]);
|
||||
record.set("allowed_group_ids", [AUTO_GROUPS.trust_level_0.id]);
|
||||
return record;
|
||||
},
|
||||
|
||||
|
@ -12,7 +12,7 @@ export default DiscourseRoute.extend({
|
|||
this._super(controller, model);
|
||||
controller.set(
|
||||
"allPersonas",
|
||||
this.modelFor("adminPlugins.discourse-ai.ai-personas")
|
||||
this.modelFor("adminPlugins.show.discourse-ai.ai-personas")
|
||||
);
|
||||
},
|
||||
});
|
|
@ -2,7 +2,9 @@ import DiscourseRoute from "discourse/routes/discourse";
|
|||
|
||||
export default DiscourseRoute.extend({
|
||||
async model(params) {
|
||||
const allPersonas = this.modelFor("adminPlugins.discourse-ai.ai-personas");
|
||||
const allPersonas = this.modelFor(
|
||||
"adminPlugins.show.discourse-ai.ai-personas"
|
||||
);
|
||||
const id = parseInt(params.id, 10);
|
||||
return allPersonas.findBy("id", id);
|
||||
},
|
||||
|
@ -11,7 +13,7 @@ export default DiscourseRoute.extend({
|
|||
this._super(controller, model);
|
||||
controller.set(
|
||||
"allPersonas",
|
||||
this.modelFor("adminPlugins.discourse-ai.ai-personas")
|
||||
this.modelFor("adminPlugins.show.discourse-ai.ai-personas")
|
||||
);
|
||||
},
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default DiscourseRoute.extend({
|
||||
async model() {
|
||||
export default class DiscourseAiAiPersonasRoute extends DiscourseRoute {
|
||||
model() {
|
||||
return this.store.findAll("ai-persona");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { inject as service } from "@ember/service";
|
||||
import DiscourseRoute from "discourse/routes/discourse";
|
||||
|
||||
export default class DiscourseAiIndexRoute extends DiscourseRoute {
|
||||
@service router;
|
||||
|
||||
beforeModel() {
|
||||
return this.router.transitionTo(
|
||||
"adminPlugins.show.discourse-ai.ai-personas.index"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<AiPersonaListEditor @personas={{this.model}} />
|
|
@ -0,0 +1 @@
|
|||
<AiPersonaListEditor @personas={{this.model}} />
|
|
@ -0,0 +1,26 @@
|
|||
import { PLUGIN_NAV_MODE_TOP } from "discourse/lib/admin-plugin-config-nav";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
|
||||
export default {
|
||||
name: "discourse-ai-admin-plugin-configuration-nav",
|
||||
|
||||
initialize(container) {
|
||||
const currentUser = container.lookup("service:current-user");
|
||||
if (!currentUser || !currentUser.admin) {
|
||||
return;
|
||||
}
|
||||
|
||||
withPluginApi("1.1.0", (api) => {
|
||||
api.addAdminPluginConfigurationNav("discourse-ai", PLUGIN_NAV_MODE_TOP, [
|
||||
{
|
||||
label: "admin.plugins.change_settings_short",
|
||||
route: "adminPlugins.show.settings",
|
||||
},
|
||||
{
|
||||
label: "discourse_ai.ai_persona.short_title",
|
||||
route: "adminPlugins.show.discourse-ai.ai-personas",
|
||||
},
|
||||
]);
|
||||
});
|
||||
},
|
||||
};
|
|
@ -1,10 +1,19 @@
|
|||
.admin-contents .ai-persona-list-editor {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ai-persona-list-editor {
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
margin: 0 0 1em 0;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__current {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.ai-persona-editor {
|
||||
&__system_prompt,
|
||||
&__description,
|
||||
.select-kit.multi-select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
|
@ -114,7 +114,9 @@ en:
|
|||
select_option: "Select an option..."
|
||||
|
||||
ai_persona:
|
||||
back: Back
|
||||
name: Name
|
||||
edit: Edit
|
||||
description: Description
|
||||
no_llm_selected: "No language model selected"
|
||||
max_context_posts: "Max Context Posts"
|
||||
|
@ -129,12 +131,13 @@ en:
|
|||
system_prompt: System Prompt
|
||||
save: Save
|
||||
saved: AI Persona Saved
|
||||
enabled: Enabled
|
||||
enabled: "Enabled?"
|
||||
commands: Enabled Commands
|
||||
allowed_groups: Allowed Groups
|
||||
confirm_delete: Are you sure you want to delete this persona?
|
||||
new: New
|
||||
new: "New Persona"
|
||||
title: "AI Personas"
|
||||
short_title: "Personas"
|
||||
delete: Delete
|
||||
temperature: Temperature
|
||||
temperature_help: Temperature to use for the LLM, increase to increase creativity (leave empty to use model default, generally a value from 0.0 to 2.0)
|
||||
|
@ -143,16 +146,15 @@ en:
|
|||
priority: Priority
|
||||
priority_help: Priority personas are displayed to users at the top of the persona list. If multiple personas have priority, they will be sorted alphabetically.
|
||||
command_options: "Command Options"
|
||||
what_are_personas: "What are AI Personas?"
|
||||
no_persona_selected: |
|
||||
## What are AI Personas?
|
||||
|
||||
AI Personas are a powerful feature that allows you to customize the behavior of the AI engine in your Discourse forum. They act as a 'system message' that guides the AI's responses and interactions, helping to create a more personalized and engaging user experience.
|
||||
|
||||
## Why use AI Personas?
|
||||
#### Why use AI Personas?
|
||||
|
||||
With AI Personas, you can tailor the AI's behavior to better fit the context and tone of your forum. Whether you want the AI to be more formal for a professional setting, more casual for a community forum, or even embody a specific character for a role-playing game, AI Personas give you the flexibility to do so.
|
||||
|
||||
## Group-Specific Access to AI Personas
|
||||
#### Group-Specific Access to AI Personas
|
||||
|
||||
Moreover, you can set it up so that certain user groups have access to specific personas. This means you can have different AI behaviors for different sections of your forum, further enhancing the diversity and richness of your community's interactions.
|
||||
|
||||
|
|
|
@ -35,8 +35,6 @@ Discourse::Application.routes.draw do
|
|||
:constraints => StaffConstraint.new
|
||||
|
||||
scope "/admin/plugins/discourse-ai", constraints: AdminConstraint.new do
|
||||
get "/", to: redirect("/admin/plugins/discourse-ai/ai-personas")
|
||||
|
||||
resources :ai_personas,
|
||||
only: %i[index create show update destroy],
|
||||
path: "ai-personas",
|
||||
|
|
|
@ -17,6 +17,7 @@ register_asset "stylesheets/modules/ai-helper/common/ai-helper.scss"
|
|||
|
||||
register_asset "stylesheets/modules/ai-bot/common/bot-replies.scss"
|
||||
register_asset "stylesheets/modules/ai-bot/common/ai-persona.scss"
|
||||
register_asset "stylesheets/modules/ai-bot/mobile/ai-persona.scss", :mobile
|
||||
|
||||
register_asset "stylesheets/modules/embeddings/common/semantic-related-topics.scss"
|
||||
register_asset "stylesheets/modules/embeddings/common/semantic-search.scss"
|
||||
|
@ -38,7 +39,7 @@ after_initialize do
|
|||
require_relative "discourse_automation/llm_triage"
|
||||
require_relative "discourse_automation/llm_report"
|
||||
|
||||
add_admin_route "discourse_ai.title", "discourse-ai"
|
||||
add_admin_route("discourse_ai.title", "discourse-ai", { use_new_show_route: true })
|
||||
|
||||
[
|
||||
DiscourseAi::Embeddings::EntryPoint.new,
|
||||
|
|
Loading…
Reference in New Issue