FEATURE: Examples support for personas. (#1334)

Examples simulate previous interactions with an LLM and come
right after the system prompt. This helps grounding the model and
producing better responses.
This commit is contained in:
Roman Rizzi
2025-05-13 10:06:16 -03:00
committed by GitHub
parent acd1986a5c
commit aef84bc5bb
16 changed files with 295 additions and 90 deletions
@@ -34,6 +34,7 @@ const CREATE_ATTRIBUTES = [
"allow_chat_channel_mentions",
"allow_chat_direct_messages",
"response_format",
"examples",
];
const SYSTEM_ATTRIBUTES = [
@@ -61,7 +62,6 @@ const SYSTEM_ATTRIBUTES = [
"allow_topic_mentions",
"allow_chat_channel_mentions",
"allow_chat_direct_messages",
"response_format",
];
export default class AiPersona extends RestModel {
@@ -154,6 +154,7 @@ export default class AiPersona extends RestModel {
this.populateTools(attrs);
attrs.forced_tool_count = this.forced_tool_count || -1;
attrs.response_format = attrs.response_format || [];
attrs.examples = attrs.examples || [];
return attrs;
}
@@ -17,6 +17,7 @@ import AdminUser from "admin/models/admin-user";
import GroupChooser from "select-kit/components/group-chooser";
import AiPersonaResponseFormatEditor from "../components/modal/ai-persona-response-format-editor";
import AiLlmSelector from "./ai-llm-selector";
import AiPersonaCollapsableExample from "./ai-persona-example";
import AiPersonaToolOptions from "./ai-persona-tool-options";
import AiToolSelector from "./ai-tool-selector";
import RagOptionsFk from "./rag-options-fk";
@@ -230,6 +231,12 @@ export default class PersonaEditor extends Component {
return this.allTools.filter((tool) => tools.includes(tool.id));
}
@action
addExamplesPair(form, data) {
const newExamples = [...data.examples, ["", ""]];
form.set("examples", newExamples);
}
mapToolOptions(currentOptions, toolNames) {
const updatedOptions = Object.assign({}, currentOptions);
@@ -422,6 +429,32 @@ export default class PersonaEditor extends Component {
</form.Field>
{{/unless}}
<form.Section
@title={{i18n "discourse_ai.ai_persona.examples.title"}}
@subtitle={{i18n "discourse_ai.ai_persona.examples.examples_help"}}
>
{{#unless data.system}}
<form.Container>
<form.Button
@action={{fn this.addExamplesPair form data}}
@label="discourse_ai.ai_persona.examples.new"
class="ai-persona-editor__new_example"
/>
</form.Container>
{{/unless}}
{{#if (gt data.examples.length 0)}}
<form.Collection @name="examples" as |exCollection exCollectionIdx|>
<AiPersonaCollapsableExample
@examplesCollection={{exCollection}}
@exampleNumber={{exCollectionIdx}}
@system={{data.system}}
@form={{form}}
/>
</form.Collection>
{{/if}}
</form.Section>
<form.Section @title={{i18n "discourse_ai.ai_persona.ai_tools"}}>
<form.Field
@name="tools"
@@ -0,0 +1,67 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { concat } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
import { eq } from "truth-helpers";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
export default class AiPersonaCollapsableExample extends Component {
@tracked collapsed = true;
get caretIcon() {
return this.collapsed ? "angle-right" : "angle-down";
}
@action
toggleExample() {
this.collapsed = !this.collapsed;
}
@action
deletePair() {
this.collapsed = true;
this.args.examplesCollection.remove(this.args.exampleNumber);
}
get exampleTitle() {
return i18n("discourse_ai.ai_persona.examples.collapsable_title", {
number: this.args.exampleNumber + 1,
});
}
<template>
<div role="button" {{on "click" this.toggleExample}}>
<span>{{icon this.caretIcon}}</span>
{{this.exampleTitle}}
</div>
{{#unless this.collapsed}}
<@examplesCollection.Collection as |exPair pairIdx|>
<exPair.Field
@title={{i18n
(concat
"discourse_ai.ai_persona.examples."
(if (eq pairIdx 0) "user" "model")
)
}}
@validation="required|length:1,100"
@disabled={{@system}}
as |field|
>
<field.Textarea />
</exPair.Field>
</@examplesCollection.Collection>
{{#unless @system}}
<@form.Container>
<@form.Button
@action={{this.deletePair}}
@label="discourse_ai.ai_persona.examples.remove"
class="ai-persona-editor__delete_example btn-danger"
/>
</@form.Container>
{{/unless}}
{{/unless}}
</template>
}