+
-
+
@@ -369,8 +369,8 @@ export default class PersonaEditor extends Component {
@@ -380,7 +380,7 @@ export default class PersonaEditor extends Component {
{{#if data.vision_enabled}}
@@ -408,8 +408,8 @@ export default class PersonaEditor extends Component {
{{#unless data.system}}
{{#unless data.system}}
{{/unless}}
{{#if (gt data.examples.length 0)}}
-
-
+
@@ -467,7 +467,7 @@ export default class PersonaEditor extends Component {
@value={{field.value}}
@disabled={{data.system}}
@onChange={{fn this.updateToolNames form data}}
- @content={{@personas.resultSetMeta.tools}}
+ @content={{@agents.resultSetMeta.tools}}
/>
@@ -475,7 +475,7 @@ export default class PersonaEditor extends Component {
{{#if (gt data.tools.length 0)}}
@@ -493,7 +493,7 @@ export default class PersonaEditor extends Component {
{{#if (gt data.forcedTools.length 0)}}
@@ -508,19 +508,19 @@ export default class PersonaEditor extends Component {
{{#if (gt data.tools.length 0)}}
-
{{/if}}
@@ -535,10 +535,10 @@ export default class PersonaEditor extends Component {
@@ -546,16 +546,16 @@ export default class PersonaEditor extends Component {
@@ -587,10 +587,10 @@ export default class PersonaEditor extends Component {
{{/if}}
-
+
@@ -599,21 +599,21 @@ export default class PersonaEditor extends Component {
{{#if @model.isNew}}
-
@@ -622,12 +622,12 @@ export default class PersonaEditor extends Component {
{{/if}}
{{#if data.user}}
{{/if}}
{{#if data.user}}
{{/unless}}
diff --git a/assets/javascripts/discourse/components/ai-persona-example.gjs b/assets/javascripts/discourse/components/ai-agent-example.gjs
similarity index 82%
rename from assets/javascripts/discourse/components/ai-persona-example.gjs
rename to assets/javascripts/discourse/components/ai-agent-example.gjs
index 3ee03114..7e44b008 100644
--- a/assets/javascripts/discourse/components/ai-persona-example.gjs
+++ b/assets/javascripts/discourse/components/ai-agent-example.gjs
@@ -7,7 +7,7 @@ import { eq } from "truth-helpers";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
-export default class AiPersonaCollapsableExample extends Component {
+export default class AiAgentCollapsableExample extends Component {
@tracked collapsed = true;
get caretIcon() {
@@ -26,7 +26,7 @@ export default class AiPersonaCollapsableExample extends Component {
}
get exampleTitle() {
- return i18n("discourse_ai.ai_persona.examples.collapsable_title", {
+ return i18n("discourse_ai.ai_agent.examples.collapsable_title", {
number: this.args.exampleNumber + 1,
});
}
@@ -41,7 +41,7 @@ export default class AiPersonaCollapsableExample extends Component {
<@form.Button
@action={{this.deletePair}}
- @label="discourse_ai.ai_persona.examples.remove"
- class="ai-persona-editor__delete_example btn-danger"
+ @label="discourse_ai.ai_agent.examples.remove"
+ class="ai-agent-editor__delete_example btn-danger"
/>
@form.Container>
{{/unless}}
diff --git a/assets/javascripts/discourse/components/ai-agent-list-editor.gjs b/assets/javascripts/discourse/components/ai-agent-list-editor.gjs
new file mode 100644
index 00000000..e0ba1c1b
--- /dev/null
+++ b/assets/javascripts/discourse/components/ai-agent-list-editor.gjs
@@ -0,0 +1,117 @@
+import Component from "@glimmer/component";
+import { fn } from "@ember/helper";
+import { on } from "@ember/modifier";
+import { action } from "@ember/object";
+import { LinkTo } from "@ember/routing";
+import { service } from "@ember/service";
+import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
+import DPageSubheader from "discourse/components/d-page-subheader";
+import DToggleSwitch from "discourse/components/d-toggle-switch";
+import concatClass from "discourse/helpers/concat-class";
+import { popupAjaxError } from "discourse/lib/ajax-error";
+import { i18n } from "discourse-i18n";
+import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
+import AiAgentEditor from "./ai-agent-editor";
+
+export default class AiAgentListEditor extends Component {
+ @service adminPluginNavManager;
+
+ @action
+ async toggleEnabled(agent) {
+ const oldValue = agent.enabled;
+ const newValue = !oldValue;
+
+ try {
+ agent.set("enabled", newValue);
+ await agent.save();
+ } catch (err) {
+ agent.set("enabled", oldValue);
+ popupAjaxError(err);
+ }
+ }
+
+
+
+
+ {{#if @currentAgent}}
+
+ {{else}}
+
+ <:actions as |actions|>
+
+
+
+
+ {{#if @agents}}
+
+
+
+ {{else}}
+
+ {{/if}}
+ {{/if}}
+
+
+}
diff --git a/assets/javascripts/discourse/components/ai-persona-llm-selector.gjs b/assets/javascripts/discourse/components/ai-agent-llm-selector.gjs
similarity index 67%
rename from assets/javascripts/discourse/components/ai-persona-llm-selector.gjs
rename to assets/javascripts/discourse/components/ai-agent-llm-selector.gjs
index aa50ae60..6f40dd53 100644
--- a/assets/javascripts/discourse/components/ai-persona-llm-selector.gjs
+++ b/assets/javascripts/discourse/components/ai-agent-llm-selector.gjs
@@ -6,10 +6,10 @@ import { service } from "@ember/service";
import { i18n } from "discourse-i18n";
import DropdownSelectBox from "select-kit/components/dropdown-select-box";
-const PERSONA_SELECTOR_KEY = "ai_persona_selector_id";
+const AGENT_SELECTOR_KEY = "ai_agent_selector_id";
const LLM_SELECTOR_KEY = "ai_llm_selector_id";
-export default class AiPersonaLlmSelector extends Component {
+export default class AiAgentLlmSelector extends Component {
@service currentUser;
@service keyValueStore;
@@ -20,7 +20,7 @@ export default class AiPersonaLlmSelector extends Component {
super(...arguments);
if (this.botOptions?.length) {
- this.#loadStoredPersona();
+ this.#loadStoredAgent();
this.#loadStoredLlm();
next(() => {
@@ -34,25 +34,25 @@ export default class AiPersonaLlmSelector extends Component {
}
get hasLlmSelector() {
- return this.currentUser.ai_enabled_chat_bots.any((bot) => !bot.is_persona);
+ return this.currentUser.ai_enabled_chat_bots.any((bot) => !bot.is_agent);
}
get botOptions() {
- if (!this.currentUser.ai_enabled_personas) {
+ if (!this.currentUser.ai_enabled_agents) {
return;
}
- let enabledPersonas = this.currentUser.ai_enabled_personas;
+ let enabledAgents = this.currentUser.ai_enabled_agents;
if (!this.hasLlmSelector) {
- enabledPersonas = enabledPersonas.filter((persona) => persona.username);
+ enabledAgents = enabledAgents.filter((agent) => agent.username);
}
- return enabledPersonas.map((persona) => {
+ return enabledAgents.map((agent) => {
return {
- id: persona.id,
- name: persona.name,
- description: persona.description,
+ id: agent.id,
+ name: agent.name,
+ description: agent.description,
};
});
}
@@ -67,8 +67,8 @@ export default class AiPersonaLlmSelector extends Component {
set value(newValue) {
this._value = newValue;
- this.keyValueStore.setItem(PERSONA_SELECTOR_KEY, newValue);
- this.args.setPersonaId(newValue);
+ this.keyValueStore.setItem(AGENT_SELECTOR_KEY, newValue);
+ this.args.setAgentId(newValue);
this.setAllowLLMSelector();
this.resetTargetRecipients();
}
@@ -79,11 +79,11 @@ export default class AiPersonaLlmSelector extends Component {
return;
}
- const persona = this.currentUser.ai_enabled_personas.find(
- (innerPersona) => innerPersona.id === this._value
+ const agent = this.currentUser.ai_enabled_agents.find(
+ (innerAgent) => innerAgent.id === this._value
);
- this.allowLLMSelector = !persona?.force_default_llm;
+ this.allowLLMSelector = !agent?.force_default_llm;
}
get currentLlm() {
@@ -104,16 +104,16 @@ export default class AiPersonaLlmSelector extends Component {
).username;
this.args.setTargetRecipient(botUsername);
} else {
- const persona = this.currentUser.ai_enabled_personas.find(
- (innerPersona) => innerPersona.id === this._value
+ const agent = this.currentUser.ai_enabled_agents.find(
+ (innerAgent) => innerAgent.id === this._value
);
- this.args.setTargetRecipient(persona.username || "");
+ this.args.setTargetRecipient(agent.username || "");
}
}
get llmOptions() {
const availableBots = this.currentUser.ai_enabled_chat_bots
- .filter((bot) => !bot.is_persona)
+ .filter((bot) => !bot.is_agent)
.filter(Boolean);
return availableBots
@@ -130,18 +130,18 @@ export default class AiPersonaLlmSelector extends Component {
return this.allowLLMSelector && this.llmOptions.length > 1;
}
- #loadStoredPersona() {
- let personaId = this.keyValueStore.getItem(PERSONA_SELECTOR_KEY);
+ #loadStoredAgent() {
+ let agentId = this.keyValueStore.getItem(AGENT_SELECTOR_KEY);
this._value = this.botOptions[0].id;
- if (personaId) {
- personaId = parseInt(personaId, 10);
- if (this.botOptions.any((bot) => bot.id === personaId)) {
- this._value = personaId;
+ if (agentId) {
+ agentId = parseInt(agentId, 10);
+ if (this.botOptions.any((bot) => bot.id === agentId)) {
+ this._value = agentId;
}
}
- this.args.setPersonaId(this._value);
+ this.args.setAgentId(this._value);
}
#loadStoredLlm() {
@@ -172,13 +172,13 @@ export default class AiPersonaLlmSelector extends Component {
}
-
{{i18n "discourse_ai.ai_persona.ai_bot.save_first"}}
+ {{i18n "discourse_ai.ai_agent.ai_bot.save_first"}}
{{else}}
{{#if data.default_llm_id}}
{{i18n "discourse_ai.ai_agent.name"}} | +{{i18n "discourse_ai.ai_agent.list.enabled"}} | ++ |
---|---|---|
+
+
+
+
+ {{agent.name}}
+
+
+
+ {{agent.description}}
+
+ |
+
+ |
+
+ |
+
-
+
+
{{#if @showLabels}}
-
+
{{/if}}
{{#if this.showLLMSelector}}
-
+
{{#if @showLabels}}
{{/if}}
{{#if this.showToolOptions}}
<@form.Container
- @title={{i18n "discourse_ai.ai_persona.tool_options"}}
+ @title={{i18n "discourse_ai.ai_agent.tool_options"}}
@direction="column"
@format="full"
>
<@form.Object
@name="toolOptions"
- @title={{i18n "discourse_ai.ai_persona.tool_options"}}
+ @title={{i18n "discourse_ai.ai_agent.tool_options"}}
as |toolObj optsPerTool|
>
{{#each (this.formObjectKeys optsPerTool) as |toolId|}}
-
+
{{#let (get this.toolsMetadata toolId) as |toolMeta|}}
-
+
@@ -73,7 +73,7 @@ export default class AiPersonaToolOptions extends Component {
@value={{field.value}}
@llms={{@llms}}
@onChange={{field.set}}
- @class="ai-persona-tool-option-editor__llms"
+ @class="ai-agent-tool-option-editor__llms"
/>
{{else if (eq optionMeta.type "boolean")}}
diff --git a/assets/javascripts/discourse/components/ai-bot-conversations.gjs b/assets/javascripts/discourse/components/ai-bot-conversations.gjs
index 9e86d3f0..a17fed96 100644
--- a/assets/javascripts/discourse/components/ai-bot-conversations.gjs
+++ b/assets/javascripts/discourse/components/ai-bot-conversations.gjs
@@ -23,7 +23,7 @@ import {
} from "discourse/lib/user-status-on-autocomplete";
import { clipboardHelpers } from "discourse/lib/utilities";
import { i18n } from "discourse-i18n";
-import AiPersonaLlmSelector from "discourse/plugins/discourse-ai/discourse/components/ai-persona-llm-selector";
+import AiAgentLlmSelector from "discourse/plugins/discourse-ai/discourse/components/ai-agent-llm-selector";
export default class AiBotConversations extends Component {
@service aiBotConversationsHiddenSubmit;
@@ -133,8 +133,8 @@ export default class AiBotConversations extends Component {
}
@action
- setPersonaId(id) {
- this.aiBotConversationsHiddenSubmit.personaId = id;
+ setAgentId(id) {
+ this.aiBotConversationsHiddenSubmit.agentId = id;
}
@action
@@ -279,9 +279,9 @@ export default class AiBotConversations extends Component {
{{toolMeta.name}}
-
diff --git a/assets/javascripts/discourse/components/ai-llm-editor-form.gjs b/assets/javascripts/discourse/components/ai-llm-editor-form.gjs
index 38cf1b50..68276365 100644
--- a/assets/javascripts/discourse/components/ai-llm-editor-form.gjs
+++ b/assets/javascripts/discourse/components/ai-llm-editor-form.gjs
@@ -116,7 +116,7 @@ export default class AiLlmEditorForm extends Component {
const localized = usedBy.map((m) => {
return i18n(`discourse_ai.llms.usage.${m.type}`, {
- persona: m.name,
+ agent: m.name,
});
});
diff --git a/assets/javascripts/discourse/components/ai-llm-selector.gjs b/assets/javascripts/discourse/components/ai-llm-selector.gjs
index f5d9d95e..e5feef9a 100644
--- a/assets/javascripts/discourse/components/ai-llm-selector.gjs
+++ b/assets/javascripts/discourse/components/ai-llm-selector.gjs
@@ -8,7 +8,7 @@ const AiLlmSelector =
@onChange={{@onChange}}
@options={{hash
filterable=true
- none="discourse_ai.ai_persona.no_llm_selected"
+ none="discourse_ai.ai_agent.no_llm_selected"
}}
class={{@class}}
/>
diff --git a/assets/javascripts/discourse/components/ai-llms-list-editor.gjs b/assets/javascripts/discourse/components/ai-llms-list-editor.gjs
index 5413a3a3..1f168eb7 100644
--- a/assets/javascripts/discourse/components/ai-llms-list-editor.gjs
+++ b/assets/javascripts/discourse/components/ai-llms-list-editor.gjs
@@ -112,9 +112,9 @@ export default class AiLlmsListEditor extends Component {
}
localizeUsage(usage) {
- if (usage.type === "ai_persona") {
- return i18n("discourse_ai.llms.usage.ai_persona", {
- persona: usage.name,
+ if (usage.type === "ai_agent") {
+ return i18n("discourse_ai.llms.usage.ai_agent", {
+ agent: usage.name,
});
} else if (usage.type === "automation") {
return i18n("discourse_ai.llms.usage.automation", {
diff --git a/assets/javascripts/discourse/components/ai-persona-list-editor.gjs b/assets/javascripts/discourse/components/ai-persona-list-editor.gjs
deleted file mode 100644
index 69cadec1..00000000
--- a/assets/javascripts/discourse/components/ai-persona-list-editor.gjs
+++ /dev/null
@@ -1,117 +0,0 @@
-import Component from "@glimmer/component";
-import { fn } from "@ember/helper";
-import { on } from "@ember/modifier";
-import { action } from "@ember/object";
-import { LinkTo } from "@ember/routing";
-import { service } from "@ember/service";
-import DBreadcrumbsItem from "discourse/components/d-breadcrumbs-item";
-import DPageSubheader from "discourse/components/d-page-subheader";
-import DToggleSwitch from "discourse/components/d-toggle-switch";
-import concatClass from "discourse/helpers/concat-class";
-import { popupAjaxError } from "discourse/lib/ajax-error";
-import { i18n } from "discourse-i18n";
-import AdminConfigAreaEmptyList from "admin/components/admin-config-area-empty-list";
-import AiPersonaEditor from "./ai-persona-editor";
-
-export default class AiPersonaListEditor extends Component {
- @service adminPluginNavManager;
-
- @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);
- }
- }
-
-
-
-
- {{#if @currentPersona}}
-
- {{else}}
-
- <:actions as |actions|>
-
-
-
-
- {{#if @personas}}
-
-
-
- {{else}}
-
- {{/if}}
- {{/if}}
-
-
-}
diff --git a/assets/javascripts/discourse/components/ai-search-discoveries.gjs b/assets/javascripts/discourse/components/ai-search-discoveries.gjs
index e3e2dcbd..99f0c546 100644
--- a/assets/javascripts/discourse/components/ai-search-discoveries.gjs
+++ b/assets/javascripts/discourse/components/ai-search-discoveries.gjs
@@ -154,8 +154,8 @@ export default class AiSearchDiscoveries extends Component {
}
get canContinueConversation() {
- const personas = this.currentUser?.ai_enabled_personas;
- if (!personas) {
+ const agents = this.currentUser?.ai_enabled_agents;
+ if (!agents) {
return false;
}
@@ -163,16 +163,16 @@ export default class AiSearchDiscoveries extends Component {
return false;
}
- const discoverPersona = personas.find(
- (persona) =>
- persona.id === parseInt(this.siteSettings?.ai_bot_discover_persona, 10)
+ const discoverAgent = agents.find(
+ (agent) =>
+ agent.id === parseInt(this.siteSettings?.ai_bot_discover_agent, 10)
);
- const discoverPersonaHasBot = discoverPersona?.username;
+ const discoverAgentHasBot = discoverAgent?.username;
return (
this.discobotDiscoveries.discovery?.length > 0 &&
!this.smoothStreamer.isStreaming &&
- discoverPersonaHasBot
+ discoverAgentHasBot
);
}
diff --git a/assets/javascripts/discourse/components/modal/ai-persona-response-format-editor.gjs b/assets/javascripts/discourse/components/modal/ai-agent-response-format-editor.gjs
similarity index 76%
rename from assets/javascripts/discourse/components/modal/ai-persona-response-format-editor.gjs
rename to assets/javascripts/discourse/components/modal/ai-agent-response-format-editor.gjs
index 3f987698..96028481 100644
--- a/assets/javascripts/discourse/components/modal/ai-persona-response-format-editor.gjs
+++ b/assets/javascripts/discourse/components/modal/ai-agent-response-format-editor.gjs
@@ -7,16 +7,16 @@ import ModalJsonSchemaEditor from "discourse/components/modal/json-schema-editor
import { prettyJSON } from "discourse/lib/formatter";
import { i18n } from "discourse-i18n";
-export default class AiPersonaResponseFormatEditor extends Component {
+export default class AiAgentResponseFormatEditor extends Component {
@tracked showJsonEditorModal = false;
jsonSchema = {
type: "array",
uniqueItems: true,
- title: i18n("discourse_ai.ai_persona.response_format.modal.root_title"),
+ title: i18n("discourse_ai.ai_agent.response_format.modal.root_title"),
items: {
type: "object",
- title: i18n("discourse_ai.ai_persona.response_format.modal.key_title"),
+ title: i18n("discourse_ai.ai_agent.response_format.modal.key_title"),
properties: {
key: {
type: "string",
@@ -30,7 +30,7 @@ export default class AiPersonaResponseFormatEditor extends Component {
};
get editorTitle() {
- return i18n("discourse_ai.ai_persona.response_format.title");
+ return i18n("discourse_ai.ai_agent.response_format.title");
}
get responseFormatAsJSON() {
@@ -64,21 +64,21 @@ export default class AiPersonaResponseFormatEditor extends Component {
<@form.Container @title={{this.editorTitle}} @format="large">
-
{{i18n "discourse_ai.ai_persona.name"}} | -{{i18n "discourse_ai.ai_persona.list.enabled"}} | -- |
---|---|---|
-
-
-
-
- {{persona.name}}
-
-
-
- {{persona.description}}
-
- |
-
- |
-
- |
-
+
{{#if (gt @data.response_format.length 0)}}
-
+{{else}} -{{this.displayJSON}}
- {{i18n "discourse_ai.ai_persona.response_format.no_format"}} +diff --git a/assets/javascripts/discourse/components/post/ai-persona-flair.gjs b/assets/javascripts/discourse/components/post/ai-agent-flair.gjs similarity index 60% rename from assets/javascripts/discourse/components/post/ai-persona-flair.gjs rename to assets/javascripts/discourse/components/post/ai-agent-flair.gjs index af637159..44a596c6 100644 --- a/assets/javascripts/discourse/components/post/ai-persona-flair.gjs +++ b/assets/javascripts/discourse/components/post/ai-agent-flair.gjs @@ -1,14 +1,14 @@ import Component from "@glimmer/component"; import { isGPTBot } from "../../lib/ai-bot-helper"; -export default class AiPersonaFlair extends Component { +export default class AiAgentFlair extends Component { static shouldRender(args) { return isGPTBot(args.post.user); } - - {{@outletArgs.post.topic.ai_persona_name}} + + {{@outletArgs.post.topic.ai_agent_name}} } diff --git a/assets/javascripts/discourse/components/rag-options-fk.gjs b/assets/javascripts/discourse/components/rag-options-fk.gjs index e540f744..0117d1a2 100644 --- a/assets/javascripts/discourse/components/rag-options-fk.gjs +++ b/assets/javascripts/discourse/components/rag-options-fk.gjs @@ -70,7 +70,7 @@ export default class RagOptionsFk extends Component { @value={{field.value}} @llms={{this.visionLlms}} @onChange={{field.set}} - @class="ai-persona-editor__llms" + @class="ai-agent-editor__llms" /> @form.Field> diff --git a/assets/javascripts/discourse/connectors/composer-fields/persona-llm-selector.gjs b/assets/javascripts/discourse/connectors/composer-fields/agent-llm-selector.gjs similarity index 73% rename from assets/javascripts/discourse/connectors/composer-fields/persona-llm-selector.gjs rename to assets/javascripts/discourse/connectors/composer-fields/agent-llm-selector.gjs index 3eaaaf71..d3190aa7 100644 --- a/assets/javascripts/discourse/connectors/composer-fields/persona-llm-selector.gjs +++ b/assets/javascripts/discourse/connectors/composer-fields/agent-llm-selector.gjs @@ -1,7 +1,7 @@ import Component from "@glimmer/component"; import { action } from "@ember/object"; import { service } from "@ember/service"; -import AiPersonaLlmSelector from "discourse/plugins/discourse-ai/discourse/components/ai-persona-llm-selector"; +import AiAgentLlmSelector from "discourse/plugins/discourse-ai/discourse/components/ai-agent-llm-selector"; function isBotMessage(composer, currentUser) { if ( @@ -21,7 +21,7 @@ function isBotMessage(composer, currentUser) { export default class BotSelector extends Component { static shouldRender(args, container) { return ( - container?.currentUser?.ai_enabled_personas && + container?.currentUser?.ai_enabled_agents && isBotMessage(args.model, container.currentUser) ); } @@ -29,8 +29,8 @@ export default class BotSelector extends Component { @service currentUser; @action - setPersonaIdOnComposer(id) { - this.args.outletArgs.model.metaData = { ai_persona_id: id }; + setAgentIdOnComposer(id) { + this.args.outletArgs.model.metaData = { ai_agent_id: id }; } @action @@ -39,8 +39,8 @@ export default class BotSelector extends Component { } -+ {{i18n "discourse_ai.ai_agent.response_format.no_format"}}{{/if}} <@form.Button @action={{this.openModal}} - @label="discourse_ai.ai_persona.response_format.open_modal" + @label="discourse_ai.ai_agent.response_format.open_modal" @disabled={{@data.system}} />diff --git a/assets/javascripts/discourse/connectors/full-page-search-below-search-header/ai-full-page-discobot-discoveries.gjs b/assets/javascripts/discourse/connectors/full-page-search-below-search-header/ai-full-page-discobot-discoveries.gjs index 6662a628..1506de9d 100644 --- a/assets/javascripts/discourse/connectors/full-page-search-below-search-header/ai-full-page-discobot-discoveries.gjs +++ b/assets/javascripts/discourse/connectors/full-page-search-below-search-header/ai-full-page-discobot-discoveries.gjs @@ -9,8 +9,8 @@ import AiSearchDiscoveriesTooltip from "../../components/ai-search-discoveries-t export default class AiFullPageDiscobotDiscoveries extends Component { static shouldRender(_args, { siteSettings, currentUser }) { return ( - siteSettings.ai_bot_discover_persona && - currentUser?.can_use_ai_bot_discover_persona && + siteSettings.ai_bot_discover_agent && + currentUser?.can_use_ai_bot_discover_agent && currentUser?.user_option?.ai_search_discoveries ); } diff --git a/assets/javascripts/discourse/connectors/search-menu-results-top/ai-discobot-discoveries.gjs b/assets/javascripts/discourse/connectors/search-menu-results-top/ai-discobot-discoveries.gjs index 6256656f..05631abf 100644 --- a/assets/javascripts/discourse/connectors/search-menu-results-top/ai-discobot-discoveries.gjs +++ b/assets/javascripts/discourse/connectors/search-menu-results-top/ai-discobot-discoveries.gjs @@ -8,8 +8,8 @@ import AiSearchDiscoveriesTooltip from "../../components/ai-search-discoveries-t export default class AiDiscobotDiscoveries extends Component { static shouldRender(args, { siteSettings, currentUser }) { return ( - siteSettings.ai_bot_discover_persona && - currentUser?.can_use_ai_bot_discover_persona && + siteSettings.ai_bot_discover_agent && + currentUser?.can_use_ai_bot_discover_agent && currentUser?.user_option?.ai_search_discoveries ); } diff --git a/assets/javascripts/discourse/controllers/preferences-ai.js b/assets/javascripts/discourse/controllers/preferences-ai.js index adab3654..e1bf6a01 100644 --- a/assets/javascripts/discourse/controllers/preferences-ai.js +++ b/assets/javascripts/discourse/controllers/preferences-ai.js @@ -35,8 +35,8 @@ export default class PreferencesAiController extends Controller { checked: this.model.user_option.ai_search_discoveries, isIncluded: (() => { return ( - this.siteSettings.ai_bot_discover_persona && - this.model?.can_use_ai_bot_discover_persona && + this.siteSettings.ai_bot_discover_agent && + this.model?.can_use_ai_bot_discover_agent && this.siteSettings.ai_bot_enabled ); })(), diff --git a/assets/javascripts/discourse/lib/ai-bot-helper.js b/assets/javascripts/discourse/lib/ai-bot-helper.js index 913f0bf9..91fce0f2 100644 --- a/assets/javascripts/discourse/lib/ai-bot-helper.js +++ b/assets/javascripts/discourse/lib/ai-bot-helper.js @@ -5,7 +5,7 @@ import Composer from "discourse/models/composer"; import { i18n } from "discourse-i18n"; import ShareFullTopicModal from "../components/modal/share-full-topic-modal"; -const MAX_PERSONA_USER_ID = -1200; +const MAX_AGENT_USER_ID = -1200; let enabledChatBotMap = null; @@ -40,12 +40,12 @@ export function getBotType(user) { if (!bot) { return; } - return bot.is_persona ? "persona" : "llm"; + return bot.is_agent ? "agent" : "llm"; } export function isPostFromAiBot(post, currentUser) { return ( - post.user_id <= MAX_PERSONA_USER_ID || + post.user_id <= MAX_AGENT_USER_ID || !!currentUser?.ai_enabled_chat_bots?.any( (bot) => post.username === bot.username ) @@ -66,7 +66,7 @@ export async function composeAiBotMessage( options = { skipFocus: false, topicBody: "", - personaUsername: null, + agentUsername: null, } ) { const currentUser = composer.currentUser; @@ -77,8 +77,8 @@ export async function composeAiBotMessage( botUsername = currentUser.ai_enabled_chat_bots.find( (bot) => bot.model_name === targetBot )?.username; - } else if (options.personaUsername) { - botUsername = options.personaUsername; + } else if (options.agentUsername) { + botUsername = options.agentUsername; } else { botUsername = currentUser.ai_enabled_chat_bots[0].username; } diff --git a/assets/javascripts/discourse/services/ai-bot-conversations-hidden-submit.js b/assets/javascripts/discourse/services/ai-bot-conversations-hidden-submit.js index 1749ba98..0dd99ad7 100644 --- a/assets/javascripts/discourse/services/ai-bot-conversations-hidden-submit.js +++ b/assets/javascripts/discourse/services/ai-bot-conversations-hidden-submit.js @@ -16,7 +16,7 @@ export default class AiBotConversationsHiddenSubmit extends Service { @tracked loading = false; - personaId; + agentId; targetUsername; uploads = []; @@ -35,12 +35,12 @@ export default class AiBotConversationsHiddenSubmit extends Service { async submitToBot() { if ( this.inputValue.length < - this.siteSettings.min_personal_message_post_length + this.siteSettings.min_agentl_message_post_length ) { return this.dialog.alert({ message: i18n( "discourse_ai.ai_bot.conversations.min_input_length_message", - { count: this.siteSettings.min_personal_message_post_length } + { count: this.siteSettings.min_agentl_message_post_length } ), didConfirm: () => this.focusInput(), didCancel: () => this.focusInput(), @@ -78,7 +78,7 @@ export default class AiBotConversationsHiddenSubmit extends Service { title, archetype: "private_message", target_recipients: this.targetUsername, - meta_data: { ai_persona_id: this.personaId }, + meta_data: { ai_agent_id: this.agentId }, }, }); diff --git a/assets/javascripts/initializers/admin-plugin-configuration-nav.js b/assets/javascripts/initializers/admin-plugin-configuration-nav.js index 391f5cbc..c94c0966 100644 --- a/assets/javascripts/initializers/admin-plugin-configuration-nav.js +++ b/assets/javascripts/initializers/admin-plugin-configuration-nav.js @@ -22,9 +22,9 @@ export default { description: "discourse_ai.llms.preconfigured.description", }, { - label: "discourse_ai.ai_persona.short_title", - route: "adminPlugins.show.discourse-ai-personas", - description: "discourse_ai.ai_persona.persona_description", + label: "discourse_ai.ai_agent.short_title", + route: "adminPlugins.show.discourse-ai-agents", + description: "discourse_ai.ai_agent.agent_description", }, { label: "discourse_ai.embeddings.short_title", diff --git a/assets/javascripts/initializers/ai-bot-replies.js b/assets/javascripts/initializers/ai-bot-replies.js index 822e6ffd..92ccf54d 100644 --- a/assets/javascripts/initializers/ai-bot-replies.js +++ b/assets/javascripts/initializers/ai-bot-replies.js @@ -3,7 +3,7 @@ import { withSilencedDeprecations } from "discourse/lib/deprecated"; import { withPluginApi } from "discourse/lib/plugin-api"; import { registerWidgetShim } from "discourse/widgets/render-glimmer"; import AiBotHeaderIcon from "../discourse/components/ai-bot-header-icon"; -import AiPersonaFlair from "../discourse/components/post/ai-persona-flair"; +import AiAgentFlair from "../discourse/components/post/ai-agent-flair"; import AiCancelStreamingButton from "../discourse/components/post-menu/ai-cancel-streaming-button"; import AiDebugButton from "../discourse/components/post-menu/ai-debug-button"; import AiShareButton from "../discourse/components/post-menu/ai-share-button"; @@ -53,35 +53,35 @@ function initializeAIBotReplies(api) { }); } -function initializePersonaDecorator(api) { - api.renderAfterWrapperOutlet("post-meta-data-poster-name", AiPersonaFlair); +function initializeAgentDecorator(api) { + api.renderAfterWrapperOutlet("post-meta-data-poster-name", AiAgentFlair); withSilencedDeprecations("discourse.post-stream-widget-overrides", () => - initializeWidgetPersonaDecorator(api) + initializeWidgetAgentDecorator(api) ); } -function initializeWidgetPersonaDecorator(api) { +function initializeWidgetAgentDecorator(api) { api.decorateWidget(`poster-name:after`, (dec) => { const botType = getBotType(dec.attrs.user); // we have 2 ways of decorating - // 1. if a bot is a LLM we decorate with persona name - // 2. if bot is a persona we decorate with LLM name + // 1. if a bot is a LLM we decorate with agent name + // 2. if bot is a agent we decorate with LLM name if (botType === "llm") { - return dec.widget.attach("persona-flair", { - personaName: dec.model?.topic?.ai_persona_name, + return dec.widget.attach("agent-flair", { + agentName: dec.model?.topic?.ai_agent_name, }); - } else if (botType === "persona") { - return dec.widget.attach("persona-flair", { - personaName: dec.model?.llm_name, + } else if (botType === "agent") { + return dec.widget.attach("agent-flair", { + agentName: dec.model?.llm_name, }); } }); registerWidgetShim( - "persona-flair", - "span.persona-flair", - hbs`{{@data.personaName}}` + "agent-flair", + "span.agent-flair", + hbs`{{@data.agentName}}` ); } @@ -149,11 +149,11 @@ function initializeShareTopicButton(api) { showShareConversationModal(modal, this.topic.id); }, classNames: ["share-ai-conversation-button"], - dependentKeys: ["topic.ai_persona_name"], + dependentKeys: ["topic.ai_agent_name"], displayed() { return ( currentUser?.can_share_ai_bot_conversations && - this.topic.ai_persona_name + this.topic.ai_agent_name ); }, }); @@ -171,7 +171,7 @@ export default { withPluginApi((api) => { attachHeaderIcon(api); initializeAIBotReplies(api); - initializePersonaDecorator(api); + initializeAgentDecorator(api); initializeDebugButton(api, container); initializeShareButton(api, container); initializeShareTopicButton(api, container); diff --git a/assets/javascripts/initializers/ai-search-discoveries.js b/assets/javascripts/initializers/ai-search-discoveries.js index 339cb944..d5f9434b 100644 --- a/assets/javascripts/initializers/ai-search-discoveries.js +++ b/assets/javascripts/initializers/ai-search-discoveries.js @@ -6,7 +6,7 @@ export default apiInitializer((api) => { if ( !settings.ai_bot_enabled || - !currentUser?.can_use_ai_bot_discover_persona + !currentUser?.can_use_ai_bot_discover_agent ) { return; } diff --git a/assets/stylesheets/common/ai-features.scss b/assets/stylesheets/common/ai-features.scss index 6a29541c..05ccd103 100644 --- a/assets/stylesheets/common/ai-features.scss +++ b/assets/stylesheets/common/ai-features.scss @@ -8,7 +8,7 @@ display: block; } - &__row-item-persona { + &__row-item-agent { padding: 0; text-align: left; diff --git a/assets/stylesheets/modules/ai-bot-conversations/common.scss b/assets/stylesheets/modules/ai-bot-conversations/common.scss index c6f0a02d..c94320a5 100644 --- a/assets/stylesheets/modules/ai-bot-conversations/common.scss +++ b/assets/stylesheets/modules/ai-bot-conversations/common.scss @@ -157,7 +157,7 @@ body.has-ai-conversations-sidebar { flex-direction: column; height: calc(100dvh - var(--header-offset) - 5em); - .persona-llm-selector { + .agent-llm-selector { display: flex; gap: 0.5em; justify-content: flex-start; diff --git a/assets/stylesheets/modules/ai-bot/common/ai-persona.scss b/assets/stylesheets/modules/ai-bot/common/ai-agent.scss similarity index 93% rename from assets/stylesheets/modules/ai-bot/common/ai-persona.scss rename to assets/stylesheets/modules/ai-bot/common/ai-agent.scss index a5fdfe5b..ac93b558 100644 --- a/assets/stylesheets/modules/ai-bot/common/ai-persona.scss +++ b/assets/stylesheets/modules/ai-bot/common/ai-agent.scss @@ -1,8 +1,8 @@ -.admin-contents .ai-persona-list-editor { +.admin-contents .ai-agent-list-editor { margin-top: 0; } -.ai-persona-list-editor { +.ai-agent-list-editor { &__header { display: flex; justify-content: space-between; @@ -23,7 +23,7 @@ } } -.ai-persona-tool-option-editor { +.ai-agent-tool-option-editor { &__instructions { color: var(--primary-medium); font-size: var(--font-down-1); @@ -31,7 +31,7 @@ } } -.ai-personas__container { +.ai-agents__container { display: flex; flex-direction: row; align-items: center; @@ -39,7 +39,7 @@ width: 100%; } -.ai-persona-editor { +.ai-agent-editor { padding-left: 0.5em; &__tool-options { diff --git a/assets/stylesheets/modules/ai-bot/common/bot-replies.scss b/assets/stylesheets/modules/ai-bot/common/bot-replies.scss index 07d0cc2c..04f03ec3 100644 --- a/assets/stylesheets/modules/ai-bot/common/bot-replies.scss +++ b/assets/stylesheets/modules/ai-bot/common/bot-replies.scss @@ -11,7 +11,7 @@ nav.post-controls .actions button.cancel-streaming { } } - .persona-llm-selector { + .agent-llm-selector { display: flex; justify-content: space-between; align-items: center; @@ -24,7 +24,7 @@ nav.post-controls .actions button.cancel-streaming { } .ai-bot-pm { - .gpt-persona { + .gpt-agent { margin-bottom: 5px; } @@ -75,7 +75,7 @@ article.streaming nav.post-controls .actions button.cancel-streaming { } } -.topic-body .persona-flair { +.topic-body .agent-flair { order: 2; font-size: var(--font-down-1); } diff --git a/assets/stylesheets/modules/ai-bot/mobile/ai-persona.scss b/assets/stylesheets/modules/ai-bot/mobile/ai-agent.scss similarity index 81% rename from assets/stylesheets/modules/ai-bot/mobile/ai-persona.scss rename to assets/stylesheets/modules/ai-bot/mobile/ai-agent.scss index 6353f526..94e322d1 100644 --- a/assets/stylesheets/modules/ai-bot/mobile/ai-persona.scss +++ b/assets/stylesheets/modules/ai-bot/mobile/ai-agent.scss @@ -1,4 +1,4 @@ -.ai-persona-editor { +.ai-agent-editor { &__system_prompt, &__description, .select-kit.multi-select { diff --git a/assets/stylesheets/modules/llms/common/ai-llms-editor.scss b/assets/stylesheets/modules/llms/common/ai-llms-editor.scss index ac7d8669..b957c511 100644 --- a/assets/stylesheets/modules/llms/common/ai-llms-editor.scss +++ b/assets/stylesheets/modules/llms/common/ai-llms-editor.scss @@ -45,7 +45,7 @@ } .ai-tool-list-editor__current, -.ai-persona-list-editor__current, +.ai-agent-list-editor__current, .ai-llms-list-editor__configured { .d-admin-table { tr:hover { diff --git a/config/locales/client.ar.yml b/config/locales/client.ar.yml index 7b5a8d30..b26cef9d 100644 --- a/config/locales/client.ar.yml +++ b/config/locales/client.ar.yml @@ -128,7 +128,7 @@ ar: flag_post: label: "الإبلاغ عن المنشور" description: "يبلغ عن المنشور (سواءً كان عشوائيًا أو للمراجعة)" - include_personal_messages: + include_agentl_messages: label: "تضمين الرسائل الشخصية" description: "افحص الرسائل الشخصية وافرزها أيضًا" model: @@ -219,7 +219,7 @@ ar: last_week: "الأسبوع الماضي" last_month: "الشهر الماضي" custom: "مُخصَّص..." - ai_persona: + ai_agent: ai_tools: "الأدوات" tool_strategies: all: "التطبيق على كل الردود" @@ -261,8 +261,8 @@ ar: allow_chat_direct_messages_help: "إذا تم التفعيل، يمكن للمستخدمين في المجموعات المسموح بها إرسال رسالة مباشرة إلى هذه الشخصية." allow_chat_channel_mentions: "السماح بالإشارات في قناة الدردشة" allow_chat_channel_mentions_help: "إذا تم التفعيل، يمكن للمستخدمين في المجموعات المسموح بها الإشارة إلى هذه الشخصية في قنوات الدردشة." - allow_personal_messages: "السماح بالرسائل الشخصية" - allow_personal_messages_help: "إذا تم التفعيل، يمكن للمستخدمين في المجموعات المسموح بها إرسال رسائل شخصية إلى هذه الشخصية." + allow_agentl_messages: "السماح بالرسائل الشخصية" + allow_agentl_messages_help: "إذا تم التفعيل، يمكن للمستخدمين في المجموعات المسموح بها إرسال رسائل شخصية إلى هذه الشخصية." allow_topic_mentions: "السماح بالإشارات في الموضوعات" allow_topic_mentions_help: "إذا تم التفعيل، يمكن للمستخدمين في المجموعات المسموح بها الإشارة إلى هذه الشخصية في الموضوعات." force_default_llm: "استخدام نموذج اللغة الافتراضي دائمًا" @@ -274,7 +274,7 @@ ar: allowed_groups: "المجموعات المسموح بها" confirm_delete: "هل تريد بالتأكيد حذف هذه الشخصية؟" new: "شخصية جديدة" - no_personas: "لم تُنشئ أي شخصيات بعد" + no_agents: "لم تُنشئ أي شخصيات بعد" title: "الشخصيات" short_title: "الشخصيات" delete: "حذف" @@ -287,7 +287,7 @@ ar: tool_options: "خيارات الأداة" rag_conversation_chunks: "البحث في أجزاء المحادثة" rag_conversation_chunks_help: "عدد الأجزاء التي سيتم استخدامها لإي عمليات البحث ضمن نموذج RAG. يزداد مقدار السياق الذي يمكن للذكاء الاصطناعي استخدامه بزيادة القيمة." - persona_description: "تُعد الشخصيات ميزة قوية تتيح لك تخصيص سلوك محرك الذكاء الاصطناعي في منتدى Discourse الخاص بك. إنها تعمل بمثابة \"رسالة نظام\" توجِّه ردود الذكاء الاصطناعي وتفاعلاته، مما يساعد في إنشاء تجربة مستخدم أكثر تخصيصًا وتفاعليةً." + agent_description: "تُعد الشخصيات ميزة قوية تتيح لك تخصيص سلوك محرك الذكاء الاصطناعي في منتدى Discourse الخاص بك. إنها تعمل بمثابة \"رسالة نظام\" توجِّه ردود الذكاء الاصطناعي وتفاعلاته، مما يساعد في إنشاء تجربة مستخدم أكثر تخصيصًا وتفاعليةً." response_format: open_modal: "تعديل" modal: @@ -380,7 +380,7 @@ ar: usage: ai_bot: "روبوت الذكاء الاصطناعي" ai_helper: "المساعد" - ai_persona: "الشخصية (%{persona})" + ai_agent: "الشخصية (%{agent})" ai_summarization: "تلخيص" ai_embeddings_semantic_search: "البحث باستخدام الذكاء الاصطناعي" ai_spam: "عشوائي" diff --git a/config/locales/client.be.yml b/config/locales/client.be.yml index f947e630..2d346599 100644 --- a/config/locales/client.be.yml +++ b/config/locales/client.be.yml @@ -50,7 +50,7 @@ be: summary: "вынік" username: "Імя карыстальніка" total_requests: "Усяго запытаў" - ai_persona: + ai_agent: back: "Назад" name: "імя" edit: "рэдагаваць" diff --git a/config/locales/client.bg.yml b/config/locales/client.bg.yml index 1b2aada9..d08c0515 100644 --- a/config/locales/client.bg.yml +++ b/config/locales/client.bg.yml @@ -56,7 +56,7 @@ bg: periods: last_day: "Последните 24 часа" custom: "Ръчно задаване..." - ai_persona: + ai_agent: back: "Назад" name: "Име" edit: "Редактирай" diff --git a/config/locales/client.bs_BA.yml b/config/locales/client.bs_BA.yml index 87e098a6..2b0d514e 100644 --- a/config/locales/client.bs_BA.yml +++ b/config/locales/client.bs_BA.yml @@ -51,7 +51,7 @@ bs_BA: usage: summary: "Sažetak" username: "Nadimak" - ai_persona: + ai_agent: back: "Prethodno" name: "Ime" edit: "Edit" diff --git a/config/locales/client.ca.yml b/config/locales/client.ca.yml index 31e2c9df..14357fc5 100644 --- a/config/locales/client.ca.yml +++ b/config/locales/client.ca.yml @@ -55,7 +55,7 @@ ca: total_requests: "Total de peticions" periods: last_day: "Últimes 24 hores" - ai_persona: + ai_agent: back: "Enrere" name: "Nom" edit: "Edita" @@ -100,7 +100,7 @@ ca: success: "Èxit!" providers: google: "Google" - fake: "Personalitzat" + fake: "Agentlitzat" ai_helper: context_menu: cancel: "Cancel·la" @@ -126,7 +126,7 @@ ca: display_name: "Nom" providers: google: "Google" - fake: "Personalitzat" + fake: "Agentlitzat" ai_bot: debug_ai_modal: request: "Sol·licita" diff --git a/config/locales/client.cs.yml b/config/locales/client.cs.yml index 937dc755..c1c3b618 100644 --- a/config/locales/client.cs.yml +++ b/config/locales/client.cs.yml @@ -58,7 +58,7 @@ cs: periods: last_day: "Posledních 24 hodin" custom: "Vlastní…" - ai_persona: + ai_agent: back: "Zpět" name: "Jméno" edit: "Upravit" diff --git a/config/locales/client.da.yml b/config/locales/client.da.yml index a4f37fb1..43a24336 100644 --- a/config/locales/client.da.yml +++ b/config/locales/client.da.yml @@ -59,7 +59,7 @@ da: periods: last_day: "Seneste 24 timer" custom: "Tilpasset..." - ai_persona: + ai_agent: back: "Tilbage" name: "Navn" edit: "Rediger" diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml index b8e3ca03..e6b9b565 100644 --- a/config/locales/client.de.yml +++ b/config/locales/client.de.yml @@ -12,8 +12,8 @@ de: descriptions: discourse_ai: search: "Ermöglicht KI-Suche" - stream_completion: "Ermöglicht das Streamen von KI-Persona-Vervollständigungen" - update_personas: "Ermöglicht die Aktualisierung von KI-Personas" + stream_completion: "Ermöglicht das Streamen von KI-Agent-Vervollständigungen" + update_agents: "Ermöglicht die Aktualisierung von KI-Agents" site_settings: categories: discourse_ai: "Discourse-KI" @@ -102,17 +102,17 @@ de: tool: label: "Werkzeug" description: "Werkzeug für die Triage (das Werkzeug darf keine Parameter definiert haben)" - llm_persona_triage: + llm_agent_triage: fields: - persona: - label: "Persona" - description: "KI-Persona, die für die Triage verwendet werden soll (Standard-LLM und -Benutzer müssen eingestellt sein)" + agent: + label: "Agent" + description: "KI-Agent, die für die Triage verwendet werden soll (Standard-LLM und -Benutzer müssen eingestellt sein)" whisper: label: "Als Flüstern antworten" - description: "Ob die Antwort der Persona ein Flüstern sein soll" + description: "Ob die Antwort der Agent ein Flüstern sein soll" silent_mode: label: "Stiller Modus" - description: "Im stillen Modus empfängt die Persona den Inhalt, schreibt aber nichts in das Forum - nützlich bei der Triage mit Tools" + description: "Im stillen Modus empfängt die Agent den Inhalt, schreibt aber nichts in das Forum - nützlich bei der Triage mit Tools" llm_triage: fields: system_prompt: @@ -148,15 +148,15 @@ de: flag_post: label: "Beitrag melden" description: "Meldet den Beitrag (entweder als Spam oder zur Überprüfung)" - include_personal_messages: + include_agentl_messages: label: "Persönliche Nachrichten einbeziehen" description: "Auch persönliche Nachrichten scannen und sortieren" whisper: label: "Als Flüstern antworten" description: "Ob die Antwort der KI ein Flüstern sein soll" - reply_persona: - label: "Antwort Persona" - description: "KI-Persona, die für Antworten verwendet werden soll (muss Standard-LLM haben), wird gegenüber vorgefertigten Antworten bevorzugt" + reply_agent: + label: "Antwort Agent" + description: "KI-Agent, die für Antworten verwendet werden soll (muss Standard-LLM haben), wird gegenüber vorgefertigten Antworten bevorzugt" model: label: "Modell" description: "Für die Triage verwendetes Sprachmodell" @@ -167,12 +167,12 @@ de: title: "KI" features: short_title: "Funktionen" - description: "Dies sind die KI-Funktionen, die den Besuchern deiner Website zur Verfügung stehen. Sie können so konfiguriert werden, dass sie bestimmte Personas und LLM verwenden, und der Zugriff kann mithilfe von Gruppen gesteuert werden." + description: "Dies sind die KI-Funktionen, die den Besuchern deiner Website zur Verfügung stehen. Sie können so konfiguriert werden, dass sie bestimmte Agents und LLM verwenden, und der Zugriff kann mithilfe von Gruppen gesteuert werden." back: "Zurück" list: header: name: "Name" - persona: "Persona" + agent: "Agent" groups: "Gruppen" edit: "Bearbeiten" set_up: "Einrichten" @@ -253,7 +253,7 @@ de: last_week: "Letzte Woche" last_month: "Letzter Monat" custom: "Benutzerdefiniert …" - ai_persona: + ai_agent: ai_tools: "Tools" tool_strategies: all: "Auf alle Antworten anwenden" @@ -265,7 +265,7 @@ de: edit: "Bearbeiten" description: "Beschreibung" no_llm_selected: "Kein Sprachmodell ausgewählt" - use_parent_llm: "Verwende das Personas Sprachmodell" + use_parent_llm: "Verwende das Agents Sprachmodell" max_context_posts: "Max. Kontext-Beiträge" max_context_posts_help: "Die maximale Anzahl von Beiträgen, die die KI als Kontext für die Antwort auf einen Nutzer verwenden soll (leer für Standardwert)." vision_enabled: Sehen aktiviert @@ -278,12 +278,12 @@ de: tool_details: Tool-Details anzeigen tool_details_help: Zeigt den Endnutzern Details darüber, welche Tools das Sprachmodell ausgelöst hat. mentionable: Erwähnungen zulassen - mentionable_help: Wenn diese Funktion aktiviert ist, können Nutzer in erlaubten Gruppen diesen Nutzer in Beiträgen erwähnen und die KI wird als diese Persona antworten. + mentionable_help: Wenn diese Funktion aktiviert ist, können Nutzer in erlaubten Gruppen diesen Nutzer in Beiträgen erwähnen und die KI wird als diese Agent antworten. user: Nutzer create_user: Benutzer erstellen - create_user_help: Du kannst dieser Persona optional einen Nutzer zuordnen. Wenn du das tust, wird die KI diesen Nutzer verwenden, um auf Anfragen zu antworten. + create_user_help: Du kannst dieser Agent optional einen Nutzer zuordnen. Wenn du das tust, wird die KI diesen Nutzer verwenden, um auf Anfragen zu antworten. default_llm: Standard-Sprachmodell - default_llm_help: Das Standard-Sprachmodell, das für diese Persona verwendet werden soll. Erforderlich, wenn du die Persona in öffentlichen Beiträgen erwähnen möchtest. + default_llm_help: Das Standard-Sprachmodell, das für diese Agent verwendet werden soll. Erforderlich, wenn du die Agent in öffentlichen Beiträgen erwähnen möchtest. question_consolidator_llm: Sprachmodell für Fragenkonsolidierer question_consolidator_llm_help: Das Sprachmodell, das für den Fragenkonsolidierer verwendet werden soll. Du kannst ein weniger leistungsfähiges Modell wählen, um Kosten zu sparen. system_prompt: System-Eingabeaufforderung @@ -291,34 +291,34 @@ de: allow_chat_direct_messages: "Chat-Direktnachrichten zulassen" allow_chat_direct_messages_help: "Wenn aktiviert, können Benutzer in zulässigen Gruppen Direktnachrichten an diese Person senden." allow_chat_channel_mentions: "Chat-Kanal-Erwähnungen zulassen" - allow_chat_channel_mentions_help: "Wenn aktiviert, können Benutzer in zulässigen Gruppen diese Persona in Chatkanälen erwähnen." - allow_personal_messages: "Persönliche Nachrichten zulassen" - allow_personal_messages_help: "Wenn aktiviert, können Benutzer in zulässigen Gruppen persönliche Nachrichten an diese Persona senden." + allow_chat_channel_mentions_help: "Wenn aktiviert, können Benutzer in zulässigen Gruppen diese Agent in Chatkanälen erwähnen." + allow_agentl_messages: "Persönliche Nachrichten zulassen" + allow_agentl_messages_help: "Wenn aktiviert, können Benutzer in zulässigen Gruppen persönliche Nachrichten an diese Agent senden." allow_topic_mentions: "Themenerwähnungen zulassen" - allow_topic_mentions_help: "Wenn aktiviert, können Benutzer in zulässigen Gruppen diese Persona in Themen erwähnen." + allow_topic_mentions_help: "Wenn aktiviert, können Benutzer in zulässigen Gruppen diese Agent in Themen erwähnen." force_default_llm: "Immer das Standard-Sprachmodell verwenden" save: "Speichern" - saved: "Persona gespeichert" + saved: "Agent gespeichert" enabled: "Aktiviert?" tools: "Aktivierte Tools" forced_tools: "Erzwungene Tools" allowed_groups: "Zulässige Gruppen" - confirm_delete: "Bist du sicher, dass du diese Persona löschen willst?" - new: "Neue Persona" - no_personas: "Du hast noch keine Personas erstellt" - title: "Personas" - short_title: "Personas" + confirm_delete: "Bist du sicher, dass du diese Agent löschen willst?" + new: "Neue Agent" + no_agents: "Du hast noch keine Agents erstellt" + title: "Agents" + short_title: "Agents" delete: "Löschen" temperature: "Temperatur" temperature_help: "Temperatur, die für das LLM verwendet werden soll. Erhöhen, um die Kreativität zu steigern (leer lassen, um den Standardwert des Modells zu verwenden, im Allgemeinen ein Wert zwischen 0,0 und 2,0)" top_p: "Top P" top_p_help: "Top P für das LLM. Erhöhen, um die Zufälligkeit zu steigern (leer lassen, um den Standardwert des Modells zu verwenden, in der Regel ein Wert zwischen 0,0 und 1,0)" priority: "Priorität" - priority_help: "Personas mit Priorität werden den Benutzern am Anfang der Persona-Liste angezeigt. Wenn mehrere Personas Priorität haben, werden sie alphabetisch sortiert." + priority_help: "Agents mit Priorität werden den Benutzern am Anfang der Agent-Liste angezeigt. Wenn mehrere Agents Priorität haben, werden sie alphabetisch sortiert." tool_options: "Tool-Optionen" rag_conversation_chunks: "Unterhaltungs-Chunks durchsuchen" rag_conversation_chunks_help: "Die Anzahl der Chunks, die für die RAG-Modell-Suche verwendet werden. Erhöhen, um die Menge des Kontexts zu steigern, den die KI verwenden kann." - persona_description: "Personas sind eine leistungsstarke Funktion, mit der du das Verhalten der KI-Engine in deinem Discourse-Forum anpassen kannst. Sie fungieren als „Systemnachricht“, welche die Antworten und Interaktionen der KI steuert und dazu beiträgt, ein persönlicheres und ansprechenderes Erlebnis für Benutzer zu schaffen." + agent_description: "Agents sind eine leistungsstarke Funktion, mit der du das Verhalten der KI-Engine in deinem Discourse-Forum anpassen kannst. Sie fungieren als „Systemnachricht“, welche die Antworten und Interaktionen der KI steuert und dazu beiträgt, ein persönlicheres und ansprechenderes Erlebnis für Benutzer zu schaffen." response_format: title: "JSON-Antwortformat" no_format: "Kein JSON-Format angegeben" @@ -338,7 +338,7 @@ de: enabled: "KI-Bot?" ai_bot: title: "KI-Bot-Optionen" - save_first: "Weitere KI-Bot-Optionen werden verfügbar, sobald du die Persona gespeichert hast." + save_first: "Weitere KI-Bot-Optionen werden verfügbar, sobald du die Agent gespeichert hast." rag: title: "RAG" options: @@ -369,7 +369,7 @@ de: name_help: "Der Name wird in der Discourse-Benutzeroberfläche angezeigt und ist die Kurzkennung, die Du verwendest, um das Werkzeug in verschiedenen Einstellungen zu finden. Er sollte eindeutig sein (er ist erforderlich)." new: "Neues Tool" tool_name: "Werkzeugname" - tool_name_help: "Der Werkzeugname wird dem großen Sprachmodell präsentiert. Es ist nicht eindeutig, aber es ist von Person zu Person unterschiedlich. (Persona wird beim Speichern validiert)" + tool_name_help: "Der Werkzeugname wird dem großen Sprachmodell präsentiert. Es ist nicht eindeutig, aber es ist von Person zu Person unterschiedlich. (Agent wird beim Speichern validiert)" description: "Beschreibung" description_help: "Eine klare Beschreibung des Zwecks des Tools für das Sprachmodell" subheader_description: "Tools erweitern die Fähigkeiten von KI-Bots mit benutzerdefinierten JavaScript-Funktionen." @@ -445,7 +445,7 @@ de: ai_bot: "KI-Bot" ai_helper: "Helfer" ai_helper_image_caption: "Bildbeschriftungen" - ai_persona: "Persona (%{persona})" + ai_agent: "Agent (%{agent})" ai_summarization: "Zusammenfassen" ai_embeddings_semantic_search: "KI-Suche" ai_spam: "Spam" @@ -658,7 +658,7 @@ de: collapse_view_label: "Vollbild verlassen (ESC- oder Zurück-Taste)" click_to_run_label: "Artefakt ausführen" ai_bot: - persona: "Persona" + agent: "Agent" llm: "Modell" pm_warning: "KI-Chatbot-Nachrichten werden regelmäßig von Moderatoren überwacht." cancel_streaming: "Antwort abbrechen" diff --git a/config/locales/client.el.yml b/config/locales/client.el.yml index b2c37f36..ea94e347 100644 --- a/config/locales/client.el.yml +++ b/config/locales/client.el.yml @@ -57,7 +57,7 @@ el: periods: last_day: "Τελευταίες 24 ώρες" custom: "Προσαρμοσμένο..." - ai_persona: + ai_agent: back: "Πίσω" name: "Όνομα" edit: "Επεξεργασία" diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 88214c5f..24edecde 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -6,8 +6,8 @@ en: descriptions: discourse_ai: search: "Allows AI search" - stream_completion: "Allows streaming AI persona completions" - update_personas: "Allows updating AI personas" + stream_completion: "Allows streaming AI agent completions" + update_agents: "Allows updating AI agents" site_settings: categories: @@ -100,17 +100,17 @@ en: label: "Tool" description: "Tool to use for triage (tool must have no parameters defined)" - llm_persona_triage: + llm_agent_triage: fields: - persona: - label: "Persona" - description: "AI Persona to use for triage (must have default LLM and User set)" + agent: + label: "Agent" + description: "AI Agent to use for triage (must have default LLM and User set)" whisper: label: "Reply as Whisper" - description: "Whether the persona's response should be a whisper" + description: "Whether the agent's response should be a whisper" silent_mode: label: "Silent Mode" - description: "In silent mode persona will receive the content but will not post anything on the forum - useful when performing triage using tools" + description: "In silent mode agent will receive the content but will not post anything on the forum - useful when performing triage using tools" llm_triage: fields: system_prompt: @@ -146,15 +146,15 @@ en: flag_post: label: "Flag post" description: "Flags post (either as spam or for review)" - include_personal_messages: - label: "Include personal messages" - description: "Also scan and triage personal messages" + include_agentl_messages: + label: "Include agentl messages" + description: "Also scan and triage agentl messages" whisper: label: "Reply as Whisper" description: "Whether the AI's response should be a whisper" - reply_persona: - label: "Reply Persona" - description: "AI Persona to use for replies (must have default LLM), will be prioritized over canned reply" + reply_agent: + label: "Reply Agent" + description: "AI Agent to use for replies (must have default LLM), will be prioritized over canned reply" model: label: "Model" description: "Language model used for triage" @@ -167,12 +167,12 @@ en: features: short_title: "Features" - description: "These are the AI features available to visitors on your site. These can be configured to use specific personas and LLMs, and can be access controlled by groups." + description: "These are the AI features available to visitors on your site. These can be configured to use specific agents and LLMs, and can be access controlled by groups." back: "Back" list: header: name: "Name" - persona: "Persona" + agent: "Agent" groups: "Groups" edit: "Edit" set_up: "Set up" @@ -257,7 +257,7 @@ en: last_month: "Last month" custom: "Custom..." - ai_persona: + ai_agent: ai_tools: "Tools" tool_strategies: all: "Apply to all replies" @@ -269,7 +269,7 @@ en: edit: "Edit" description: "Description" no_llm_selected: "No language model selected" - use_parent_llm: "Use personas language model" + use_parent_llm: "Use agents language model" max_context_posts: "Max context posts" max_context_posts_help: "The maximum number of posts to use as context for the AI when responding to a user. (empty for default)" vision_enabled: Vision enabled @@ -282,47 +282,47 @@ en: tool_details: Show tool details tool_details_help: Will show end users details on which tools the language model has triggered. mentionable: Allow mentions - mentionable_help: If enabled, users in allowed groups can mention this user in posts, the AI will respond as this persona. + mentionable_help: If enabled, users in allowed groups can mention this user in posts, the AI will respond as this agent. user: User create_user: Create user - create_user_help: You can optionally attach a user to this persona. If you do, the AI will use this user to respond to requests. + create_user_help: You can optionally attach a user to this agent. If you do, the AI will use this user to respond to requests. default_llm: Default language model - default_llm_help: The default language model to use for this persona. Required if you wish to mention persona on public posts. + default_llm_help: The default language model to use for this agent. Required if you wish to mention agent on public posts. question_consolidator_llm: Language Model for Question Consolidator question_consolidator_llm_help: The language model to use for the question consolidator, you may choose a less powerful model to save costs. system_prompt: System prompt forced_tool_strategy: Forced tool strategy allow_chat_direct_messages: "Allow chat direct messages" - allow_chat_direct_messages_help: "If enabled, users in allowed groups can send direct messages to this persona." + allow_chat_direct_messages_help: "If enabled, users in allowed groups can send direct messages to this agent." allow_chat_channel_mentions: "Allow chat channel mentions" - allow_chat_channel_mentions_help: "If enabled, users in allowed groups can mention this persona in chat channels." - allow_personal_messages: "Allow personal messages" - allow_personal_messages_help: "If enabled, users in allowed groups can send personal messages to this persona." + allow_chat_channel_mentions_help: "If enabled, users in allowed groups can mention this agent in chat channels." + allow_agentl_messages: "Allow agentl messages" + allow_agentl_messages_help: "If enabled, users in allowed groups can send agentl messages to this agent." allow_topic_mentions: "Allow topic mentions" - allow_topic_mentions_help: "If enabled, users in allowed groups can mention this persona in topics." + allow_topic_mentions_help: "If enabled, users in allowed groups can mention this agent in topics." force_default_llm: "Always use default language model" save: "Save" - saved: "Persona saved" + saved: "Agent saved" enabled: "Enabled?" tools: "Enabled tools" forced_tools: "Forced tools" allowed_groups: "Allowed groups" - confirm_delete: "Are you sure you want to delete this persona?" - new: "New persona" - no_personas: "You have not created any personas yet" - title: "Personas" - short_title: "Personas" + confirm_delete: "Are you sure you want to delete this agent?" + new: "New agent" + no_agents: "You have not created any agents yet" + title: "Agents" + short_title: "Agents" 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)" top_p: "Top P" top_p_help: "Top P to use for the LLM, increase to increase randomness (leave empty to use model default, generally a value from 0.0 to 1.0)" 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." + priority_help: "Priority agents are displayed to users at the top of the agent list. If multiple agents have priority, they will be sorted alphabetically." tool_options: "Tool options" rag_conversation_chunks: "Search conversation chunks" rag_conversation_chunks_help: "The number of chunks to use for the RAG model searches. Increase to increase the amount of context the AI can use." - persona_description: "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." + agent_description: "Agents 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 agentlized and engaging user experience." response_format: title: "JSON response format" no_format: "No JSON format specified" @@ -344,7 +344,7 @@ en: ai_bot: title: "AI bot options" - save_first: "More AI bot options will become available once you save the persona." + save_first: "More AI bot options will become available once you save the agent." rag: title: "RAG" @@ -377,7 +377,7 @@ en: name_help: "Name will show up in the Discourse UI and is the short identifier you will use to find the tool in various settings, it should be distinct (it is required)" new: "New tool" tool_name: "Tool Name" - tool_name_help: "Tool Name is presented to the large language model. It is not distinct, but it is distinct per persona. (persona validates on save)" + tool_name_help: "Tool Name is presented to the large language model. It is not distinct, but it is distinct per agent. (agent validates on save)" description: "Description" description_help: "A clear description of the tool's purpose for the language model" subheader_description: "Tools extend the capabilities of AI bots with user-defined JavaScript functions." @@ -455,7 +455,7 @@ en: ai_bot: "AI bot" ai_helper: "Helper" ai_helper_image_caption: "Image caption" - ai_persona: "Persona (%{persona})" + ai_agent: "Agent (%{agent})" ai_summarization: "Summarize" ai_embeddings_semantic_search: "AI search" ai_spam: "Spam" @@ -681,7 +681,7 @@ en: click_to_run_label: "Run Artifact" ai_bot: - persona: "Persona" + agent: "Agent" llm: "Model" pm_warning: "AI chatbot messages are monitored regularly by moderators." cancel_streaming: "Stop reply" diff --git a/config/locales/client.en_GB.yml b/config/locales/client.en_GB.yml index c2a0d3d6..89627760 100644 --- a/config/locales/client.en_GB.yml +++ b/config/locales/client.en_GB.yml @@ -15,7 +15,7 @@ en_GB: discourse_ai: usage: summary: "Summary" - ai_persona: + ai_agent: description: "Description" tools: description: "Description" diff --git a/config/locales/client.es.yml b/config/locales/client.es.yml index 373f6a5e..fd576947 100644 --- a/config/locales/client.es.yml +++ b/config/locales/client.es.yml @@ -12,7 +12,7 @@ es: descriptions: discourse_ai: search: "Permite la búsqueda de IA" - stream_completion: "Permite la transmisión de realización de personas de IA" + stream_completion: "Permite la transmisión de realización de agents de IA" site_settings: categories: discourse_ai: "Discourse AI" @@ -128,9 +128,9 @@ es: flag_post: label: "Denunciar publicación" description: "Denuncia la publicación (como spam o para revisión)" - include_personal_messages: - label: "Incluir mensajes personales" - description: "También escanea y clasifica los mensajes personales" + include_agentl_messages: + label: "Incluir mensajes agentles" + description: "También escanea y clasifica los mensajes agentles" model: label: "Modelo" description: "Modelo lingüístico utilizado para el triaje" @@ -152,8 +152,8 @@ es: short_title: "Spam" title: "Configurar el manejo de correo no deseado" select_llm: "Seleccionar LLM" - custom_instructions: "Instrucciones personalizadas" - custom_instructions_help: "Instrucciones personalizadas específicas de tu sitio para ayudar a guiar a la IA en la identificación del correo no deseado, por ejemplo: «Sé más agresivo a la hora de escanear los mensajes que no estén en inglés»." + custom_instructions: "Instrucciones agentlizadas" + custom_instructions_help: "Instrucciones agentlizadas específicas de tu sitio para ayudar a guiar a la IA en la identificación del correo no deseado, por ejemplo: «Sé más agresivo a la hora de escanear los mensajes que no estén en inglés»." last_seven_days: "Últimos 7 días" scanned_count: "Publicaciones escaneadas" false_positives: "Denunciado incorrectamente" @@ -218,8 +218,8 @@ es: last_day: "Últimas 24 horas" last_week: "Última semana" last_month: "Último mes" - custom: "Personalizado..." - ai_persona: + custom: "Agentlizado..." + ai_agent: ai_tools: "Herramientas" tool_strategies: all: "Aplicar a todas las respuestas" @@ -243,47 +243,47 @@ es: tool_details: Mostrar detalles de la herramienta tool_details_help: Mostrará a los usuarios finales detalles sobre qué herramientas ha activado el modelo de lenguaje. mentionable: Permitir menciones - mentionable_help: Si está activada, los usuarios de los grupos permitidos pueden mencionar a este usuario en sus mensajes, y la IA responderá como esta persona. + mentionable_help: Si está activada, los usuarios de los grupos permitidos pueden mencionar a este usuario en sus mensajes, y la IA responderá como esta agent. user: Usuario create_user: Crear usuario - create_user_help: Opcionalmente, puedes adjuntar un usuario a esta persona. Si lo haces, la IA utilizará a este usuario para responder a las solicitudes. + create_user_help: Opcionalmente, puedes adjuntar un usuario a esta agent. Si lo haces, la IA utilizará a este usuario para responder a las solicitudes. default_llm: Modelo lingüístico por defecto - default_llm_help: El modelo de idioma por defecto que se utilizará para esta persona. Obligatorio si deseas mencionar a la persona en publicaciones públicas. + default_llm_help: El modelo de idioma por defecto que se utilizará para esta agent. Obligatorio si deseas mencionar a la agent en publicaciones públicas. question_consolidator_llm: Modelo lingüístico para el consolidador de preguntas question_consolidator_llm_help: El modelo lingüístico a utilizar para el consolidador de preguntas, puedes elegir un modelo menos potente para ahorrar costes. system_prompt: Aviso del sistema forced_tool_strategy: Estrategia de herramienta forzada allow_chat_direct_messages: "Permitir mensajes directos de chat" - allow_chat_direct_messages_help: "Si se activa, los usuarios de los grupos permitidos pueden enviar mensajes directos a esta persona." + allow_chat_direct_messages_help: "Si se activa, los usuarios de los grupos permitidos pueden enviar mensajes directos a esta agent." allow_chat_channel_mentions: "Permitir menciones en el canal de chat" - allow_chat_channel_mentions_help: "Si se activa, los usuarios de los grupos permitidos pueden mencionar a esta persona en los canales de chat." - allow_personal_messages: "Permitir mensajes personales" - allow_personal_messages_help: "Si se activa, los usuarios de los grupos permitidos pueden enviar mensajes personales a esta persona." + allow_chat_channel_mentions_help: "Si se activa, los usuarios de los grupos permitidos pueden mencionar a esta agent en los canales de chat." + allow_agentl_messages: "Permitir mensajes agentles" + allow_agentl_messages_help: "Si se activa, los usuarios de los grupos permitidos pueden enviar mensajes agentles a esta agent." allow_topic_mentions: "Permitir menciones en temas" - allow_topic_mentions_help: "Si se activa, los usuarios de los grupos permitidos pueden mencionar a esta persona en los temas." + allow_topic_mentions_help: "Si se activa, los usuarios de los grupos permitidos pueden mencionar a esta agent en los temas." force_default_llm: "Utilizar siempre el modelo de idioma por defecto" save: "Guardar" - saved: "Persona guardada" + saved: "Agent guardada" enabled: "¿Activado?" tools: "Herramientas habilitadas" forced_tools: "Herramientas forzadas" allowed_groups: "Grupos permitidos" - confirm_delete: "¿Seguro que quieres eliminar esta persona?" - new: "Nueva persona" - no_personas: "Aún no has creado ninguna persona" - title: "Personas" - short_title: "Personas" + confirm_delete: "¿Seguro que quieres eliminar esta agent?" + new: "Nueva agent" + no_agents: "Aún no has creado ninguna agent" + title: "Agents" + short_title: "Agents" delete: "Eliminar" temperature: "Temperatura" temperature_help: "Temperatura que se utilizará para el LLM. Aumentar para aumentar la creatividad (dejar vacío para utilizar el valor por defecto del modelo, generalmente un valor de 0,0 a 2,0)" top_p: "Top P" top_p_help: "Top P a utilizar para el LLM, aumentar para aumentar la aleatoriedad (dejar vacío para utilizar el modelo por defecto, generalmente un valor de 0,0 a 1,0)" priority: "Prioridad" - priority_help: "Las personas prioritarias se muestran a los usuarios en la parte superior de la lista de personas. Si varias personas tienen prioridad, se ordenarán alfabéticamente." + priority_help: "Las agents prioritarias se muestran a los usuarios en la parte superior de la lista de agents. Si varias agents tienen prioridad, se ordenarán alfabéticamente." tool_options: "Opciones de herramientas" rag_conversation_chunks: "Buscar fragmentos de conversación" rag_conversation_chunks_help: "El número de fragmentos a utilizar para las búsquedas del modelo RAG. Aumentar para incrementar la cantidad de contexto que puede utilizar la IA." - persona_description: "Las personas son una potente característica que te permite personalizar el comportamiento del motor de IA en tu foro de Discourse. Actúan como un «mensaje del sistema» que guía las respuestas e interacciones de la IA, ayudando a crear una experiencia de usuario más personalizada y atractiva." + agent_description: "Las agents son una potente característica que te permite agentlizar el comportamiento del motor de IA en tu foro de Discourse. Actúan como un «mensaje del sistema» que guía las respuestas e interacciones de la IA, ayudando a crear una experiencia de usuario más agentlizada y atractiva." response_format: open_modal: "Editar" modal: @@ -369,14 +369,14 @@ es: six_hours: "6 horas" day: "24 horas" week: "7 días" - custom: "Personalizado..." + custom: "Agentlizado..." hours: "horas" max_tokens_help: "Número máximo de tokens (palabras y caracteres) que cada usuario de este grupo puede utilizar dentro de la duración especificada. Los tokens son las unidades que utilizan los modelos de IA para procesar texto: aproximadamente 1 token = 4 caracteres o 3/4 de una palabra." max_usages_help: "Número máximo de veces que cada usuario de este grupo puede utilizar el modelo de IA dentro de la duración especificada. Esta cuota se controla por usuario individual, no se comparte con todo el grupo." usage: ai_bot: "Bot de IA" ai_helper: "Ayudante" - ai_persona: "Persona (%{persona})" + ai_agent: "Agent (%{agent})" ai_summarization: "Resumir" ai_embeddings_semantic_search: "Búsqueda de IA" ai_spam: "Spam" @@ -429,7 +429,7 @@ es: samba_nova: "SambaNova" mistral: "Mistral" open_router: "OpenRouter" - fake: "Personalizado" + fake: "Agentlizado" provider_fields: access_key_id: "ID de la clave de acceso a AWS Bedrock" region: "Región de AWS Bedrock" @@ -463,8 +463,8 @@ es: discard: "Descartar" changes: "Ediciones sugeridas" custom_prompt: - title: "Instruccón personalizada" - placeholder: "Introduzca un aviso personalizado..." + title: "Instruccón agentlizada" + placeholder: "Introduzca un aviso agentlizado..." submit: "Enviar instrucción" translate_prompt: "Traducir a %{language}" post_options_menu: @@ -542,7 +542,7 @@ es: google: "Google" cloudflare: "Cloudflare" CDCK: "CDCK" - fake: "Personalizado" + fake: "Agentlizado" provider_fields: model_name: "Nombre del modelo" semantic_search: "Temas (semánticos)" diff --git a/config/locales/client.et.yml b/config/locales/client.et.yml index de2652c5..4a034ad8 100644 --- a/config/locales/client.et.yml +++ b/config/locales/client.et.yml @@ -52,7 +52,7 @@ et: summary: "Kokkuvõte" username: "Kasutajanimi" total_requests: "Kokku päringuid" - ai_persona: + ai_agent: back: "Tagasi" name: "Nimi" edit: "Muuda" diff --git a/config/locales/client.fa_IR.yml b/config/locales/client.fa_IR.yml index 239acd5b..de6297bd 100644 --- a/config/locales/client.fa_IR.yml +++ b/config/locales/client.fa_IR.yml @@ -83,7 +83,7 @@ fa_IR: periods: last_day: "۲۴ ساعت گذشته" custom: "سفارشی..." - ai_persona: + ai_agent: back: "بازگشت" name: "نام" edit: "ویرایش" diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml index a55ec157..05150b6b 100644 --- a/config/locales/client.fi.yml +++ b/config/locales/client.fi.yml @@ -128,7 +128,7 @@ fi: flag_post: label: "Liputa viesti" description: "Liputtaa viestin (joko roskapostiksi tai käsiteltäväksi)" - include_personal_messages: + include_agentl_messages: label: "Sisällytä yksityisviestit" description: "Skannaa ja luokittele myös yksityisviestit" model: @@ -219,7 +219,7 @@ fi: last_week: "Viime viikko" last_month: "Viime kuukausi" custom: "Mukautettu..." - ai_persona: + ai_agent: ai_tools: "Työkalut" tool_strategies: all: "Käytä kaikkiin vastauksiin" @@ -257,8 +257,8 @@ fi: allow_chat_direct_messages_help: "Jos tämä on käytössä, sallittujen ryhmien käyttäjät voivat lähettää yksityisviestejä tälle persoonalle." allow_chat_channel_mentions: "Salli chat-kanavamaininnat" allow_chat_channel_mentions_help: "Jos tämä on käytössä, sallittujen ryhmien käyttäjät voivat mainita tämän persoonan chat-kanavilla." - allow_personal_messages: "Salli yksityisviestit" - allow_personal_messages_help: "Jos tämä on käytössä, sallittujen ryhmien käyttäjät voivat lähettää yksityisviestejä tälle persoonalle." + allow_agentl_messages: "Salli yksityisviestit" + allow_agentl_messages_help: "Jos tämä on käytössä, sallittujen ryhmien käyttäjät voivat lähettää yksityisviestejä tälle persoonalle." allow_topic_mentions: "Salli ketjumaininnat" allow_topic_mentions_help: "Jos tämä on käytössä, sallittujen ryhmien käyttäjät voivat mainita tämän persoonan ketjuissa." force_default_llm: "Käytä aina oletuskielimallia" @@ -270,7 +270,7 @@ fi: allowed_groups: "Sallitut ryhmät" confirm_delete: "Oletko varma, että haluat poistaa tämän persoonan?" new: "Uusi persoona" - no_personas: "Et ole vielä luonut persoonia" + no_agents: "Et ole vielä luonut persoonia" title: "Persoonat" short_title: "Persoonat" delete: "Poista" @@ -283,7 +283,7 @@ fi: tool_options: "Työkaluasetukset" rag_conversation_chunks: "Hakukeskustelulohkot" rag_conversation_chunks_help: "RAG-mallin hauissa käytettävien lohkojen määrä. Lisää kontekstin määrää, jota tekoäly voi käyttää, kasvattamalla arvoa." - persona_description: "Personat ovat tehokas ominaisuus, jonka avulla voit mukauttaa tekoälymoduulin toimintaa Discourse-foorumillasi. Ne toimivat \"järjestelmäviestinä\", joka ohjaa tekoälyn vastauksia ja vuorovaikutusta ja auttaa luomaan personoidumman ja kiinnostavamman käyttökokemuksen." + agent_description: "Agentt ovat tehokas ominaisuus, jonka avulla voit mukauttaa tekoälymoduulin toimintaa Discourse-foorumillasi. Ne toimivat \"järjestelmäviestinä\", joka ohjaa tekoälyn vastauksia ja vuorovaikutusta ja auttaa luomaan personoidumman ja kiinnostavamman käyttökokemuksen." response_format: open_modal: "Muokkaa" modal: @@ -376,7 +376,7 @@ fi: usage: ai_bot: "Tekoälyrobotti" ai_helper: "Apuri" - ai_persona: "Persoona (%{persona})" + ai_agent: "Persoona (%{agent})" ai_summarization: "Tee yhteenveto" ai_embeddings_semantic_search: "Tekoälyhaku" ai_spam: "Roskaposti" diff --git a/config/locales/client.fr.yml b/config/locales/client.fr.yml index 0ae1c534..78246e21 100644 --- a/config/locales/client.fr.yml +++ b/config/locales/client.fr.yml @@ -128,7 +128,7 @@ fr: flag_post: label: "Signaler cette publication" description: "Signale la publication (soit comme spam ou pour examen)" - include_personal_messages: + include_agentl_messages: label: "Inclure les messages personnels" description: "Analysez et triez également les messages personnels" model: @@ -219,7 +219,7 @@ fr: last_week: "La semaine dernière" last_month: "Le mois dernier" custom: "Personnalisé…" - ai_persona: + ai_agent: ai_tools: "Outils" tool_strategies: all: "Appliquer à toutes les réponses" @@ -257,8 +257,8 @@ fr: allow_chat_direct_messages_help: "Si cette option est activée, les utilisateurs des groupes autorisés peuvent envoyer des messages directs à ce personnage." allow_chat_channel_mentions: "Autoriser les mentions du canal de discussion" allow_chat_channel_mentions_help: "Si cette option est activée, les utilisateurs des groupes autorisés peuvent mentionner ce personnage dans les canaux de discussion." - allow_personal_messages: "Autoriser les messages personnels" - allow_personal_messages_help: "Si cette option est activée, les utilisateurs des groupes autorisés peuvent envoyer des messages personnels à ce personnage." + allow_agentl_messages: "Autoriser les messages personnels" + allow_agentl_messages_help: "Si cette option est activée, les utilisateurs des groupes autorisés peuvent envoyer des messages personnels à ce personnage." allow_topic_mentions: "Autoriser les mentions de sujets" allow_topic_mentions_help: "Si cette option est activée, les utilisateurs des groupes autorisés peuvent mentionner ce personnage dans les sujets." force_default_llm: "Toujours utiliser le modèle linguistique par défaut" @@ -270,7 +270,7 @@ fr: allowed_groups: "Groupes autorisés" confirm_delete: "Voulez-vous vraiment supprimer ce personnage ?" new: "Nouveau personnage" - no_personas: "Vous n'avez pas encore créé de personnage" + no_agents: "Vous n'avez pas encore créé de personnage" title: "Personnages" short_title: "Personnages" delete: "Supprimer" @@ -283,7 +283,7 @@ fr: tool_options: "Options de l'outil" rag_conversation_chunks: "Rechercher des morceaux de conversation" rag_conversation_chunks_help: "Le nombre de segments à utiliser pour les recherches de modèles RAG. Augmentez pour augmenter la quantité de contexte que l'IA peut utiliser." - persona_description: "Les personnages sont une fonctionnalité puissante qui vous permet de personnaliser le comportement du moteur d'IA dans votre forum Discourse. Ils agissent comme un « message système » qui guide les réponses et les interactions de l'IA, en contribuant ainsi à créer une expérience utilisateur plus personnalisée et plus interactive." + agent_description: "Les personnages sont une fonctionnalité puissante qui vous permet de personnaliser le comportement du moteur d'IA dans votre forum Discourse. Ils agissent comme un « message système » qui guide les réponses et les interactions de l'IA, en contribuant ainsi à créer une expérience utilisateur plus personnalisée et plus interactive." response_format: open_modal: "Modifier" modal: @@ -376,7 +376,7 @@ fr: usage: ai_bot: "Robot IA" ai_helper: "Assistant" - ai_persona: "Personnage (%{persona})" + ai_agent: "Personnage (%{agent})" ai_summarization: "Résumer" ai_embeddings_semantic_search: "Recherche IA" ai_spam: "Spam" diff --git a/config/locales/client.gl.yml b/config/locales/client.gl.yml index 7421331b..7e8535ef 100644 --- a/config/locales/client.gl.yml +++ b/config/locales/client.gl.yml @@ -55,8 +55,8 @@ gl: username: "Nome de usuario" total_requests: "Solicitudes totais" periods: - custom: "Personalizar..." - ai_persona: + custom: "Agentlizar..." + ai_agent: back: "Volver" name: "Nome" edit: "Editar" @@ -97,7 +97,7 @@ gl: hour: "1 hora" six_hours: "6 horas" day: "24 horas" - custom: "Personalizar..." + custom: "Agentlizar..." hours: "horas" usage: ai_spam: "Lixo" @@ -107,7 +107,7 @@ gl: success: "Feito!" providers: google: "Google" - fake: "Personalizado" + fake: "Agentlizado" ai_helper: context_menu: cancel: "Cancelar" @@ -133,7 +133,7 @@ gl: display_name: "Nome" providers: google: "Google" - fake: "Personalizado" + fake: "Agentlizado" ai_bot: debug_ai_modal: request: "Petición" diff --git a/config/locales/client.he.yml b/config/locales/client.he.yml index f3100ce3..47ee2f59 100644 --- a/config/locales/client.he.yml +++ b/config/locales/client.he.yml @@ -13,7 +13,7 @@ he: discourse_ai: search: "מאפשר חיפוש בינה מלאכותית" stream_completion: "מאפשר הזרמת השלמות דמות בינה מלאכותית" - update_personas: "מאפשר לעדכן דמויות בינה מלאכותית" + update_agents: "מאפשר לעדכן דמויות בינה מלאכותית" site_settings: categories: discourse_ai: "בינה מלאכותית ב־Discourse" @@ -100,9 +100,9 @@ he: label: "מודל" tool: label: "כלי" - llm_persona_triage: + llm_agent_triage: fields: - persona: + agent: label: "דמות" silent_mode: label: "מצב שקט" @@ -141,10 +141,10 @@ he: flag_post: label: "סימון פוסט" description: "סימון פוסט (או כספאם או לסקירה)" - include_personal_messages: + include_agentl_messages: label: "כולל הודעות פרטיות" description: "לסרוק ולאמת הודעות אישיות" - reply_persona: + reply_agent: label: "דמות לתגובה" model: label: "מודל" @@ -161,7 +161,7 @@ he: list: header: name: "שם" - persona: "דמות" + agent: "דמות" groups: "קבוצות" edit: "עריכה" set_up: "הגדרה" @@ -234,7 +234,7 @@ he: last_week: "בשבוע שעבר" last_month: "בחודש שעבר" custom: "התאמה אישית…" - ai_persona: + ai_agent: ai_tools: "כלים" tool_strategies: all: "החלה על כל התגובות" @@ -275,8 +275,8 @@ he: allow_chat_direct_messages_help: "אם האפשרות פעילה, משתמשים בקבוצות המורשות יכולים לשלוח הודעות ישירות לדמות הזאת." allow_chat_channel_mentions: "לאפשר אזכורים בערוצי צ׳אט" allow_chat_channel_mentions_help: "אם האפשרות פעילה, משתמשים בקבוצות המורשות יכולים לאזכר את הדמות הזאת בערוצי צ׳אט." - allow_personal_messages: "לאפשר הודעות פרטיות" - allow_personal_messages_help: "אם האפשרות פעילה, משתמשים בקבוצות המורשות יכולים לשלוח הודעות פרטיות לדמות הזאת." + allow_agentl_messages: "לאפשר הודעות פרטיות" + allow_agentl_messages_help: "אם האפשרות פעילה, משתמשים בקבוצות המורשות יכולים לשלוח הודעות פרטיות לדמות הזאת." allow_topic_mentions: "לאפשר אזכורים בנושאים" allow_topic_mentions_help: "אם האפשרות פעילה, משתמשים בקבוצות המורשות יכולים לאזכר את הדמות הזאת בנושאים." force_default_llm: "תמיד להשתמש במודל השפה כברירת מחדל" @@ -288,7 +288,7 @@ he: allowed_groups: "קבוצות מורשות" confirm_delete: "למחוק את הדמות?" new: "דמות חדשה" - no_personas: "לא יצרת דמויות עדיין" + no_agents: "לא יצרת דמויות עדיין" title: "דמויות" short_title: "דמויות" delete: "מחיקה" @@ -301,7 +301,7 @@ he: tool_options: "אפשרויות כלי" rag_conversation_chunks: "חיפוש בחלקי הדיון" rag_conversation_chunks_help: "מספר הנתחים לשימוש לחיפושים עם מודל ה־RAG. הגדלה תגדיל את כמות ההקשר בו יכולה להשתמש הבינה המלאכותית." - persona_description: "דמויות הן יכולות רבות עוצמה שמאפשר להתאים את התנהגות מנוע הבינה המלאכותית בפורום הדיסקורס שלך. הן מתנהגות כמו ‚הודעות מערכת’ שמנחות את תגובות והתנהלות הבינה המלאכותית, כדי לסייע ליצור חוויית משתמש מותאמת ומקרבת יותר." + agent_description: "דמויות הן יכולות רבות עוצמה שמאפשר להתאים את התנהגות מנוע הבינה המלאכותית בפורום הדיסקורס שלך. הן מתנהגות כמו ‚הודעות מערכת’ שמנחות את תגובות והתנהלות הבינה המלאכותית, כדי לסייע ליצור חוויית משתמש מותאמת ומקרבת יותר." response_format: title: "תבנית תגובת JSON" no_format: "לא צוינה תבנית JSON" @@ -422,7 +422,7 @@ he: ai_bot: "בוט בינה מלאכותית" ai_helper: "מסייע" ai_helper_image_caption: "כותרת תמונה" - ai_persona: "דמות (%{persona})" + ai_agent: "דמות (%{agent})" ai_summarization: "סיכום" ai_embeddings_semantic_search: "חיפוש בינה מלאכותית" ai_spam: "ספאם" @@ -623,7 +623,7 @@ he: collapse_view_label: "יציאה ממסך מלא (כפתורי ESC או Back)" click_to_run_label: "הרצת תוצר" ai_bot: - persona: "דמות" + agent: "דמות" llm: "מודל" pm_warning: "הודעות בוט שיח בינה מלאכותית לא נאכפות דרך קבע על ידי המפקחים." cancel_streaming: "עצירת התגובה" diff --git a/config/locales/client.hr.yml b/config/locales/client.hr.yml index 5e17da5e..86517621 100644 --- a/config/locales/client.hr.yml +++ b/config/locales/client.hr.yml @@ -58,7 +58,7 @@ hr: periods: last_day: "Posljednja 24 sata" custom: "Prilagođeno..." - ai_persona: + ai_agent: back: "Natrag" name: "Ime" edit: "Uredi" diff --git a/config/locales/client.hu.yml b/config/locales/client.hu.yml index bf57a99f..07826d4e 100644 --- a/config/locales/client.hu.yml +++ b/config/locales/client.hu.yml @@ -59,7 +59,7 @@ hu: periods: last_day: "Elmúlt 24 óra" custom: "Egyéni…" - ai_persona: + ai_agent: back: "Vissza" name: "Név" edit: "Szerkesztés" diff --git a/config/locales/client.hy.yml b/config/locales/client.hy.yml index ad8a055d..a7596e1f 100644 --- a/config/locales/client.hy.yml +++ b/config/locales/client.hy.yml @@ -52,7 +52,7 @@ hy: summary: "Ամփոփումը" username: "Օգտանուն" total_requests: "Ընդհանուր հարցումներ" - ai_persona: + ai_agent: back: "Ետ" name: "Անուն" edit: "Խմբագրել" diff --git a/config/locales/client.id.yml b/config/locales/client.id.yml index 7561cf40..ac87d8a7 100644 --- a/config/locales/client.id.yml +++ b/config/locales/client.id.yml @@ -116,12 +116,12 @@ id: username: "Nama Pengguna" periods: last_day: "24 jam terakhir" - ai_persona: + ai_agent: back: "Kembali" name: "Nama" edit: "Ubah" description: "Deskripsi" - mentionable_help: Jika diaktifkan, pengguna di grup yang diizinkan dapat menyebut pengguna ini di posting, AI akan merespons sebagai persona ini. + mentionable_help: Jika diaktifkan, pengguna di grup yang diizinkan dapat menyebut pengguna ini di posting, AI akan merespons sebagai agent ini. user: Pengguna question_consolidator_llm: Model Bahasa untuk Konsolidator Pertanyaan question_consolidator_llm_help: Model bahasa yang digunakan untuk konsolidator pertanyaan, Anda dapat memilih model yang kurang kuat untuk menghemat biaya. diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml index 58ff2ef8..b33f8117 100644 --- a/config/locales/client.it.yml +++ b/config/locales/client.it.yml @@ -12,7 +12,7 @@ it: descriptions: discourse_ai: search: "Consente la ricerca IA" - stream_completion: "Consente lo streaming di completamenti di personaggi IA" + stream_completion: "Consente lo streaming di completamenti di agentggi IA" site_settings: categories: discourse_ai: "Discourse AI" @@ -128,9 +128,9 @@ it: flag_post: label: "Segnala messaggio" description: "Segnala il post (come spam o per la revisione)" - include_personal_messages: - label: "Includi messaggi personali" - description: "Esegui anche la scansione e la selezione dei messaggi personali" + include_agentl_messages: + label: "Includi messaggi agentli" + description: "Esegui anche la scansione e la selezione dei messaggi agentli" model: label: "Modello" description: "Modello linguistico utilizzato per il triage" @@ -152,8 +152,8 @@ it: short_title: "Spam" title: "Configura la gestione dello spam" select_llm: "Seleziona LLM" - custom_instructions: "Istruzioni personalizzate" - custom_instructions_help: "Istruzioni personalizzate specifiche per il tuo sito per aiutare l'intelligenza artificiale a identificare lo spam, ad esempio \"Sii più aggressivo nell'analizzare i post in una lingua diversa dall'italiano\"." + custom_instructions: "Istruzioni agentlizzate" + custom_instructions_help: "Istruzioni agentlizzate specifiche per il tuo sito per aiutare l'intelligenza artificiale a identificare lo spam, ad esempio \"Sii più aggressivo nell'analizzare i post in una lingua diversa dall'italiano\"." last_seven_days: "Ultimi 7 giorni" scanned_count: "Messaggi scansionati" false_positives: "Segnalato in modo errato" @@ -218,8 +218,8 @@ it: last_day: "Ultime 24 ore" last_week: "Ultima settimana" last_month: "Ultimo mese" - custom: "Personalizza..." - ai_persona: + custom: "Agentlizza..." + ai_agent: ai_tools: "Strumenti" tool_strategies: all: "Applica a tutte le risposte" @@ -243,47 +243,47 @@ it: tool_details: Mostra i dettagli dello strumento tool_details_help: Mostrerà agli utenti finali i dettagli su quali strumenti ha attivato il modello linguistico. mentionable: Consenti menzioni - mentionable_help: Se l'opzione è abilitata, gli utenti nei gruppi consentiti possono menzionare questo utente nei post, l'IA risponderà come questa persona. + mentionable_help: Se l'opzione è abilitata, gli utenti nei gruppi consentiti possono menzionare questo utente nei post, l'IA risponderà come questa agent. user: Utente create_user: Crea utente - create_user_help: Facoltativamente, è possibile associare un utente a questa persona. In tal caso, l'IA utilizzerà questo utente per rispondere alle richieste. + create_user_help: Facoltativamente, è possibile associare un utente a questa agent. In tal caso, l'IA utilizzerà questo utente per rispondere alle richieste. default_llm: Modello linguistico predefinito - default_llm_help: Il modello linguistico predefinito da utilizzare per questa persona. Obbligatorio se desideri menzionare la persona nei post pubblici. + default_llm_help: Il modello linguistico predefinito da utilizzare per questa agent. Obbligatorio se desideri menzionare la agent nei post pubblici. question_consolidator_llm: Modello linguistico per il consolidatore di domande question_consolidator_llm_help: Il modello linguistico da utilizzare per il consolidatore di domande. È possibile scegliere un modello meno potente per risparmiare sui costi. system_prompt: Comando di sistema forced_tool_strategy: Strategia degli strumenti forzati allow_chat_direct_messages: "Consenti messaggi diretti in chat" - allow_chat_direct_messages_help: "Se l'opzione è abilitata, gli utenti nei gruppi consentiti possono inviare messaggi diretti a questa persona." + allow_chat_direct_messages_help: "Se l'opzione è abilitata, gli utenti nei gruppi consentiti possono inviare messaggi diretti a questa agent." allow_chat_channel_mentions: "Consenti menzioni nei canali di chat" - allow_chat_channel_mentions_help: "Se abilitato, gli utenti nei gruppi consentiti possono menzionare questo personaggio nei canali di chat." - allow_personal_messages: "Consenti messaggi personali" - allow_personal_messages_help: "Se l'opzione è abilitata, gli utenti nei gruppi consentiti possono inviare messaggi personali a questo personaggio." + allow_chat_channel_mentions_help: "Se abilitato, gli utenti nei gruppi consentiti possono menzionare questo agentggio nei canali di chat." + allow_agentl_messages: "Consenti messaggi agentli" + allow_agentl_messages_help: "Se l'opzione è abilitata, gli utenti nei gruppi consentiti possono inviare messaggi agentli a questo agentggio." allow_topic_mentions: "Consenti menzioni nell'argomento" - allow_topic_mentions_help: "Se abilitato, gli utenti nei gruppi consentiti possono menzionare questa persona negli argomenti." + allow_topic_mentions_help: "Se abilitato, gli utenti nei gruppi consentiti possono menzionare questa agent negli argomenti." force_default_llm: "Usa sempre il modello linguistico predefinito" save: "Salva" - saved: "Persona salvata" + saved: "Agent salvata" enabled: "Abilitato?" tools: "Strumenti abilitati" forced_tools: "Strumenti forzati" allowed_groups: "Gruppi ammessi" - confirm_delete: "Vuoi davvero eliminare questo personaggio?" - new: "Nuovo personaggio" - no_personas: "Non hai ancora creato nessun personaggio" - title: "Personaggi" - short_title: "Personaggi" + confirm_delete: "Vuoi davvero eliminare questo agentggio?" + new: "Nuovo agentggio" + no_agents: "Non hai ancora creato nessun agentggio" + title: "Agentggi" + short_title: "Agentggi" delete: "Elimina" temperature: "Temperatura" temperature_help: "Temperatura da utilizzare per LLM. Aumenta per aumentare la creatività (lascia vuoto per utilizzare il modello predefinito, generalmente un valore compreso tra 0,0 e 2,0)" top_p: "P superiore" top_p_help: "P superiore da utilizzare per LLM, aumenta per aumentare la casualità (lascia vuoto per utilizzare l'impostazione predefinita del modello, generalmente un valore compreso tra 0,0 e 1,0)" priority: "Priorità" - priority_help: "I personaggi prioritari vengono visualizzati agli utenti nella parte superiore dell'elenco dei personaggi. Se più personaggi hanno la priorità, verranno ordinati in ordine alfabetico." + priority_help: "I agentggi prioritari vengono visualizzati agli utenti nella parte superiore dell'elenco dei agentggi. Se più agentggi hanno la priorità, verranno ordinati in ordine alfabetico." tool_options: "Opzioni dello strumento" rag_conversation_chunks: "Cerca blocchi di conversazione" rag_conversation_chunks_help: "Il numero di blocchi da utilizzare per le ricerche del modello RAG. Aumenta per aumentare la quantità di contesto che l'IA può utilizzare." - persona_description: "I personaggi sono una potente funzionalità che ti consente di personalizzare il comportamento del motore IA nel tuo forum Discourse. Agiscono come un \"messaggio di sistema\" che guida le risposte e le interazioni dell'IA, aiutando a creare un'esperienza utente più personalizzata e coinvolgente." + agent_description: "I agentggi sono una potente funzionalità che ti consente di agentlizzare il comportamento del motore IA nel tuo forum Discourse. Agiscono come un \"messaggio di sistema\" che guida le risposte e le interazioni dell'IA, aiutando a creare un'esperienza utente più agentlizzata e coinvolgente." response_format: open_modal: "Modifica" modal: @@ -369,14 +369,14 @@ it: six_hours: "6 ore" day: "24 ore" week: "7 giorni" - custom: "Personalizza..." + custom: "Agentlizza..." hours: "ore" max_tokens_help: "Numero massimo di token (parole e caratteri) che ogni utente di questo gruppo può utilizzare entro la durata specificata. I token sono le unità utilizzate dai modelli IA per elaborare il testo: circa 1 token = 4 caratteri o 3/4 di parola." max_usages_help: "Numero massimo di volte in cui ogni utente in questo gruppo può usare il modello IA entro la durata specificata. Questa quota viene tracciata per singolo utente, non condivisa tra il gruppo." usage: ai_bot: "Bot IA" ai_helper: "Assistente" - ai_persona: "Personaggio (%{persona})" + ai_agent: "Agentggio (%{agent})" ai_summarization: "Riassumi" ai_embeddings_semantic_search: "Ricerca IA" ai_spam: "Spam" @@ -429,7 +429,7 @@ it: samba_nova: "SambaNova" mistral: "Mistral" open_router: "OpenRouter" - fake: "Personalizzato" + fake: "Agentlizzato" provider_fields: access_key_id: "ID chiave di accesso AWS Bedrock" region: "Regione AWS Bedrock" @@ -463,8 +463,8 @@ it: discard: "Elimina" changes: "Modifiche suggerite" custom_prompt: - title: "Comando personalizzato" - placeholder: "Inserisci un comando personalizzato..." + title: "Comando agentlizzato" + placeholder: "Inserisci un comando agentlizzato..." submit: "Invia comando" translate_prompt: "Traduci in %{language}" post_options_menu: @@ -542,7 +542,7 @@ it: google: "Google" cloudflare: "Cloudflare" CDCK: "CDCK" - fake: "Personalizzato" + fake: "Agentlizzato" provider_fields: model_name: "Nome del modello" semantic_search: "Argomenti (semantici)" diff --git a/config/locales/client.ja.yml b/config/locales/client.ja.yml index 6b156ad7..c161d403 100644 --- a/config/locales/client.ja.yml +++ b/config/locales/client.ja.yml @@ -128,7 +128,7 @@ ja: flag_post: label: "投稿を通報" description: "投稿を通報 (迷惑またはレビュー対象)" - include_personal_messages: + include_agentl_messages: label: "個人メッセージを含める" description: "個人メッセージのスキャンとトリアージも行う" model: @@ -218,7 +218,7 @@ ja: last_week: "先週" last_month: "先月" custom: "カスタム..." - ai_persona: + ai_agent: ai_tools: "ツール" tool_strategies: all: "すべての返信に適用" @@ -255,8 +255,8 @@ ja: allow_chat_direct_messages_help: "有効にすると、許可されているグループのユーザーはこのペルソナにダイレクトメッセージを送信できます。" allow_chat_channel_mentions: "チャットチャンネルのメンションを許可" allow_chat_channel_mentions_help: "有効にすると、許可されたグループ内のユーザーはチャットチャンネルでこのペルソナをメンションできます。" - allow_personal_messages: "個人メッセージを許可" - allow_personal_messages_help: "有効にすると、許可されているグループのユーザーはこのペルソナに個人メッセージを送信できます。" + allow_agentl_messages: "個人メッセージを許可" + allow_agentl_messages_help: "有効にすると、許可されているグループのユーザーはこのペルソナに個人メッセージを送信できます。" allow_topic_mentions: "トピックのメンションを許可" allow_topic_mentions_help: "有効にすると、許可されたグループ内のユーザーはトピックでこのペルソナをメンションできます。" force_default_llm: "常にデフォルトの言語モデルを使用する" @@ -268,7 +268,7 @@ ja: allowed_groups: "許可されたグループ" confirm_delete: "このペルソナを削除してもよろしいですか?" new: "新しいペルソナ" - no_personas: "ペルソナをまだ作成していません" + no_agents: "ペルソナをまだ作成していません" title: "ペルソナ" short_title: "ペルソナ" delete: "削除" @@ -281,7 +281,7 @@ ja: tool_options: "ツールのオプション" rag_conversation_chunks: "会話チャンクを検索" rag_conversation_chunks_help: "RAG モデル検索に使用するチャンクの数。値を増やすと、AI が使用できるコンテキストの量が増えます。" - persona_description: "ペルソナは、Discourse フォーラムの AI エンジンの動作をカスタマイズできる強力な機能です。AI の応答と対話を誘導する「システムメッセージ」として機能し、よりパーソナライズされた魅力的なユーザーエクスペリエンスの作成に役立ちます。" + agent_description: "ペルソナは、Discourse フォーラムの AI エンジンの動作をカスタマイズできる強力な機能です。AI の応答と対話を誘導する「システムメッセージ」として機能し、よりパーソナライズされた魅力的なユーザーエクスペリエンスの作成に役立ちます。" response_format: open_modal: "編集" modal: @@ -374,7 +374,7 @@ ja: usage: ai_bot: "AI ボット" ai_helper: "ヘルパー" - ai_persona: "ペルソナ (%{persona})" + ai_agent: "ペルソナ (%{agent})" ai_summarization: "要約" ai_embeddings_semantic_search: "AI 検索" ai_spam: "迷惑" diff --git a/config/locales/client.ko.yml b/config/locales/client.ko.yml index 620a3adf..f1b68668 100644 --- a/config/locales/client.ko.yml +++ b/config/locales/client.ko.yml @@ -60,7 +60,7 @@ ko: periods: last_day: "지난 24시간" custom: "사용자 지정..." - ai_persona: + ai_agent: back: "뒤로" name: "그룹명" edit: "편집" diff --git a/config/locales/client.lt.yml b/config/locales/client.lt.yml index 6bd3d65e..34b656d5 100644 --- a/config/locales/client.lt.yml +++ b/config/locales/client.lt.yml @@ -95,7 +95,7 @@ lt: periods: last_day: "Paskutinės 24 valandos" custom: "Pasirinktinis..." - ai_persona: + ai_agent: back: "Atgal" name: "Vardas" edit: "Redaguoti" diff --git a/config/locales/client.lv.yml b/config/locales/client.lv.yml index 6ccd5e54..2b9d7086 100644 --- a/config/locales/client.lv.yml +++ b/config/locales/client.lv.yml @@ -54,7 +54,7 @@ lv: username: "Lietotājvārds" periods: last_day: "Pēdējās 24 stundas" - ai_persona: + ai_agent: back: "Atpakaļ" name: "Vārds" edit: "Rediģēt" diff --git a/config/locales/client.nb_NO.yml b/config/locales/client.nb_NO.yml index 79bcb3eb..40dcd2b7 100644 --- a/config/locales/client.nb_NO.yml +++ b/config/locales/client.nb_NO.yml @@ -55,7 +55,7 @@ nb_NO: total_requests: "Totalt forespørsler" periods: last_day: "Siste 24 timer" - ai_persona: + ai_agent: back: "Forrige" name: "Navn" edit: "Endre" diff --git a/config/locales/client.nl.yml b/config/locales/client.nl.yml index 03d38d37..36b9a810 100644 --- a/config/locales/client.nl.yml +++ b/config/locales/client.nl.yml @@ -12,7 +12,7 @@ nl: descriptions: discourse_ai: search: "Staat AI zoeken toe" - stream_completion: "Maakt streamen van voltooiingen van AI persona's mogelijk" + stream_completion: "Maakt streamen van voltooiingen van AI agent's mogelijk" site_settings: categories: discourse_ai: "Discourse AI" @@ -128,7 +128,7 @@ nl: flag_post: label: "Bericht markeren" description: "Markeert berichten (als spam of ter beoordeling)" - include_personal_messages: + include_agentl_messages: label: "Persoonlijke berichten opnemen" description: "Scan en triageer ook persoonlijke berichten" model: @@ -219,7 +219,7 @@ nl: last_week: "Vorige week" last_month: "Vorige maand" custom: "Aangepast..." - ai_persona: + ai_agent: ai_tools: "Tools" tool_strategies: all: "Toepassen op alle antwoorden" @@ -243,47 +243,47 @@ nl: tool_details: Tooldetails weergeven tool_details_help: Toont eindgebruikers informatie over welke tools het taalmodel heeft geactiveerd. mentionable: Vermeldingen toestaan - mentionable_help: Indien ingeschakeld, kunnen gebruikers in toegestane groepen deze gebruiker vermelden in berichten. De AI reageert als deze persona. + mentionable_help: Indien ingeschakeld, kunnen gebruikers in toegestane groepen deze gebruiker vermelden in berichten. De AI reageert als deze agent. user: Gebruiker create_user: Gebruiker maken - create_user_help: Je kunt optioneel een gebruiker aan deze persona koppelen. Als je dat doet, gebruikt de AI deze gebruiker om op verzoeken te antwoorden. + create_user_help: Je kunt optioneel een gebruiker aan deze agent koppelen. Als je dat doet, gebruikt de AI deze gebruiker om op verzoeken te antwoorden. default_llm: Standaard taalmodel - default_llm_help: Het standaard taalmodel dat voor deze persona moet worden gebruikt. Vereist als je de persona wilt vermelden in openbare berichten. + default_llm_help: Het standaard taalmodel dat voor deze agent moet worden gebruikt. Vereist als je de agent wilt vermelden in openbare berichten. question_consolidator_llm: Taalmodel voor Vragenconsolidator question_consolidator_llm_help: Het te gebruiken taalmodel voor de vragenconsolidator. Je kunt een minder krachtig model kiezen om kosten te besparen. system_prompt: Systeemprompt forced_tool_strategy: Gedwongen toolstrategie allow_chat_direct_messages: "Directe chatberichten toestaan" - allow_chat_direct_messages_help: "Indien ingeschakeld, kunnen gebruikers in toegestane groepen directe berichten naar deze persona sturen." + allow_chat_direct_messages_help: "Indien ingeschakeld, kunnen gebruikers in toegestane groepen directe berichten naar deze agent sturen." allow_chat_channel_mentions: "Vermelding in chatkanalen toestaan" - allow_chat_channel_mentions_help: "Indien ingeschakeld, kunnen gebruikers in toegestane groepen deze persona vermelden in chatkanalen." - allow_personal_messages: "Persoonlijke berichten toestaan" - allow_personal_messages_help: "Indien ingeschakeld, kunnen gebruikers in toegestane groepen persoonlijke berichten naar deze persona sturen." + allow_chat_channel_mentions_help: "Indien ingeschakeld, kunnen gebruikers in toegestane groepen deze agent vermelden in chatkanalen." + allow_agentl_messages: "Persoonlijke berichten toestaan" + allow_agentl_messages_help: "Indien ingeschakeld, kunnen gebruikers in toegestane groepen persoonlijke berichten naar deze agent sturen." allow_topic_mentions: "Vermelding in topics toestaan" - allow_topic_mentions_help: "Indien ingeschakeld, kunnen gebruikers in toegestane groepen deze persona vermelden in topics." + allow_topic_mentions_help: "Indien ingeschakeld, kunnen gebruikers in toegestane groepen deze agent vermelden in topics." force_default_llm: "Altijd standaard taalmodel gebruiken" save: "Opslaan" - saved: "Persona opgeslagen" + saved: "Agent opgeslagen" enabled: "Ingeschakeld?" tools: "Ingeschakelde tools" forced_tools: "Geforceerde tools" allowed_groups: "Toegestane groepen" - confirm_delete: "Weet je zeker dat je deze persona wilt verwijderen?" - new: "Nieuwe persona" - no_personas: "Je hebt nog geen persona's gemaakt" - title: "Persona's" - short_title: "Persona's" + confirm_delete: "Weet je zeker dat je deze agent wilt verwijderen?" + new: "Nieuwe agent" + no_agents: "Je hebt nog geen agent's gemaakt" + title: "Agent's" + short_title: "Agent's" delete: "Verwijderen" temperature: "Temperatuur" temperature_help: "Te gebruiken temperatuur voor de LLM. Verhoog deze om de creativiteit te vergroten (laat dit leeg om de standaardinstelling van het model te gebruiken, doorgaans een waarde van 0,0 tot 2,0)" top_p: "Top P" top_p_help: "Te gebruiken Top P voor de LLM. Verhoog deze om de willekeurigheid te vergroten (laat dit leeg om de standaardinstelling van het model te gebruiken, doorgaans een waarde van 0,0 tot 2,0)" priority: "Prioriteit" - priority_help: "Prioritaire persona's worden bovenaan de personalijst weergegeven voor gebruikers. Als meerdere persona's prioriteit hebben, worden deze alfabetisch gesorteerd." + priority_help: "Prioritaire agent's worden bovenaan de agentlijst weergegeven voor gebruikers. Als meerdere agent's prioriteit hebben, worden deze alfabetisch gesorteerd." tool_options: "Toolopties" rag_conversation_chunks: "Conversatiechunks zoeken" rag_conversation_chunks_help: "Het te gebruiken aantal chunks voor zoeken door het RAG-model. Verhoog dit om de hoeveelheid context te vergroten die de AI kan gebruiken." - persona_description: "Persona's zijn een krachtige functie waarmee je het gedrag van de AI-engine kunt aanpassen in je Discourse-forum. Ze fungeren als een 'systeembericht' dat de reacties en interacties van de AI stuurt, waardoor je een persoonlijkere en boeiendere gebruikerservaring creëert." + agent_description: "Agent's zijn een krachtige functie waarmee je het gedrag van de AI-engine kunt aanpassen in je Discourse-forum. Ze fungeren als een 'systeembericht' dat de reacties en interacties van de AI stuurt, waardoor je een persoonlijkere en boeiendere gebruikerservaring creëert." response_format: open_modal: "Bewerken" modal: @@ -376,7 +376,7 @@ nl: usage: ai_bot: "AI-bot" ai_helper: "Helper" - ai_persona: "Persona (%{persona})" + ai_agent: "Agent (%{agent})" ai_summarization: "Samenvatten" ai_embeddings_semantic_search: "AI zoeken" ai_spam: "Spam" diff --git a/config/locales/client.pl_PL.yml b/config/locales/client.pl_PL.yml index f6de01f3..da1f2a5f 100644 --- a/config/locales/client.pl_PL.yml +++ b/config/locales/client.pl_PL.yml @@ -95,13 +95,13 @@ pl_PL: description: "Domyślny model językowy używany do selekcji" tool: label: "Narzędzie" - llm_persona_triage: + llm_agent_triage: fields: - persona: + agent: label: "Osobowość" silent_mode: label: "Tryb cichy" - description: "W trybie cichym persona otrzyma zawartość, ale nie opublikuje niczego na forum - przydatne podczas przeprowadzania selekcji przy użyciu narzędzi." + description: "W trybie cichym agent otrzyma zawartość, ale nie opublikuje niczego na forum - przydatne podczas przeprowadzania selekcji przy użyciu narzędzi." llm_triage: fields: system_prompt: @@ -136,12 +136,12 @@ pl_PL: flag_post: label: "Oflaguj post" description: "Oflaguj post (jako spam lub do sprawdzenia)" - include_personal_messages: + include_agentl_messages: label: "Dołącz osobiste wiadomości" description: "Skanuj i sortuj również wiadomości prywatne" - reply_persona: - label: "Persona odpowiedzi" - description: "AI Persona użyta do odpowiedzi (musi mieć domyślną wartość LLM), będzie traktowana priorytetowo nad gotową odpowiedzią" + reply_agent: + label: "Agent odpowiedzi" + description: "AI Agent użyta do odpowiedzi (musi mieć domyślną wartość LLM), będzie traktowana priorytetowo nad gotową odpowiedzią" model: label: "Model" description: "Model językowy używany do selekcji" @@ -155,7 +155,7 @@ pl_PL: list: header: name: "Nazwa" - persona: "Osobowość" + agent: "Osobowość" groups: "Grupy" edit: "Edytuj" set_up: "Skonfiguruj" @@ -227,7 +227,7 @@ pl_PL: last_week: "Ostatni tydzień" last_month: "Ostatni miesiąc" custom: "Niestandardowy..." - ai_persona: + ai_agent: ai_tools: "Narzędzia" tool_strategies: all: "Zastosuj do wszystkich odpowiedzi" @@ -254,20 +254,20 @@ pl_PL: default_llm: Domyślny model językowy default_llm_help: Domyślny model językowy używany dla tej persony. Wymagane, jeśli chcesz wspomnieć o osobie w postach publicznych. system_prompt: Prompt systemowy - allow_personal_messages: "Zezwalaj na wiadomości osobiste" - allow_personal_messages_help: "Jeśli ta opcja jest włączona, użytkownicy w dozwolonych grupach mogą wysyłać osobiste wiadomości do tej persony." + allow_agentl_messages: "Zezwalaj na wiadomości osobiste" + allow_agentl_messages_help: "Jeśli ta opcja jest włączona, użytkownicy w dozwolonych grupach mogą wysyłać osobiste wiadomości do tej persony." allow_topic_mentions: "Zezwalaj na wzmianki o temacie" allow_topic_mentions_help: "Jeśli ta opcja jest włączona, użytkownicy w dozwolonych grupach mogą wspominać o tej personie w tematach." force_default_llm: "Zawsze używaj domyślnego modelu języka" save: "Zapisz" - saved: "Persona zapisana" + saved: "Agent zapisana" enabled: "Włączona?" tools: "Włączone narzędzia" forced_tools: "Narzędzia wymuszone" allowed_groups: "Dozwolone grupy" confirm_delete: "Czy na pewno chcesz usunąć tę personę?" - new: "Nowa persona" - no_personas: "Nie utworzyłeś jeszcze żadnych person" + new: "Nowa agent" + no_agents: "Nie utworzyłeś jeszcze żadnych person" title: "Persony" short_title: "Persony" delete: "Usuń" @@ -306,7 +306,7 @@ pl_PL: name_help: "Nazwa pojawi się w interfejsie użytkownika Discourse i będzie krótkim identyfikatorem, którego będziesz używać do znajdowania narzędzia w różnych ustawieniach. Powinna być ona unikatowa (jest wymagana)" new: "Nowe narzędzie" tool_name: "Nazwa narzędzia" - tool_name_help: "Nazwa narzędzia jest prezentowana w dużym modelu językowym. Nie jest ona odrębna, ale jest odrębna dla każdej persony. (persona sprawdza poprawność przy zapisywaniu)" + tool_name_help: "Nazwa narzędzia jest prezentowana w dużym modelu językowym. Nie jest ona odrębna, ale jest odrębna dla każdej persony. (agent sprawdza poprawność przy zapisywaniu)" description: "Opis" description_help: "Jasny opis celu narzędzia dla modelu językowego" subheader_description: "Narzędzia rozszerzają możliwości botów AI o zdefiniowane przez użytkownika funkcje JavaScript." @@ -374,7 +374,7 @@ pl_PL: usage: ai_bot: "Bot AI" ai_helper: "Pomocnik" - ai_persona: "Persona (%{persona})" + ai_agent: "Agent (%{agent})" ai_summarization: "Podsumuj" ai_embeddings_semantic_search: "Wyszukiwanie AI" ai_spam: "Spam" @@ -505,7 +505,7 @@ pl_PL: expand_view_label: "Rozszerz widok" collapse_view_label: "Wyjdź z trybu pełnoekranowego (przycisk ESC lub Wstecz)" ai_bot: - persona: "Osobowość" + agent: "Osobowość" llm: "Model" pm_warning: "Wiadomości wysyłane przez chatboty AI są regularnie monitorowane przez moderatorów." cancel_streaming: "Zatrzymaj odpowiedź" diff --git a/config/locales/client.pt.yml b/config/locales/client.pt.yml index fc8157c5..bdfd0e6a 100644 --- a/config/locales/client.pt.yml +++ b/config/locales/client.pt.yml @@ -55,8 +55,8 @@ pt: total_requests: "Total de pedidos" periods: last_day: "Últimas 24 horas" - custom: "Personalizar..." - ai_persona: + custom: "Agentlizar..." + ai_agent: back: "Retroceder" name: "Nome" edit: "Editar" @@ -99,7 +99,7 @@ pt: six_hours: "6 horas" day: "24 horas" week: "7 dias" - custom: "Personalizar..." + custom: "Agentlizar..." hours: "horas" usage: ai_summarization: "Resumir" @@ -108,7 +108,7 @@ pt: title: "Próximo" providers: google: "Google" - fake: "Personalizar" + fake: "Agentlizar" ai_helper: context_menu: cancel: "Cancelar" @@ -133,7 +133,7 @@ pt: display_name: "Nome" providers: google: "Google" - fake: "Personalizar" + fake: "Agentlizar" ai_bot: debug_ai_modal: request: "Pedido" diff --git a/config/locales/client.pt_BR.yml b/config/locales/client.pt_BR.yml index fe7fc3b9..ed8c7a10 100644 --- a/config/locales/client.pt_BR.yml +++ b/config/locales/client.pt_BR.yml @@ -12,7 +12,7 @@ pt_BR: descriptions: discourse_ai: search: "Permite pesquisa com IA" - stream_completion: "Permite transmissão de conclusão com persona de IA" + stream_completion: "Permite transmissão de conclusão com agent de IA" site_settings: categories: discourse_ai: "Discourse IA" @@ -128,7 +128,7 @@ pt_BR: flag_post: label: "Sinalizar postagem" description: "Sinaliza a postagem (como spam ou para revisão)" - include_personal_messages: + include_agentl_messages: label: "Incluir mensagens pessoais" description: "Também verificar e fazer triagem de mensagens pessoais" model: @@ -152,7 +152,7 @@ pt_BR: short_title: "Spam" title: "Configurar tratamento de spam" select_llm: "Selecionar LLM" - custom_instructions: "Instruções personalizadas" + custom_instructions: "Instruções agentlizadas" custom_instructions_help: "Instruções específicas para seu site para ajudar a orientar a IA na identificação de spam, por exemplo: \"Verificar com mais agressividade postagens que não estão em inglês\"." last_seven_days: "Últimos sete dias" scanned_count: "Postagens verificadas" @@ -218,8 +218,8 @@ pt_BR: last_day: "Últimas 24 horas" last_week: "Semana passada" last_month: "Mês passado" - custom: "Personalizado..." - ai_persona: + custom: "Agentlizado..." + ai_agent: ai_tools: "Ferramentas" tool_strategies: all: "Aplicar a todas as respostas" @@ -243,47 +243,47 @@ pt_BR: tool_details: Exibir detalhes da ferramenta tool_details_help: Serão exibidos aos(às) usuários(as) finais as ferramentas nas quais o modelo de linguagem foi acionado. mentionable: Permitir menções - mentionable_help: Ative para que os(as) usuários(as) nos grupos permitidos possam mencionar este(a) usuário(a) nas postagens. A IA responderá como esta persona. + mentionable_help: Ative para que os(as) usuários(as) nos grupos permitidos possam mencionar este(a) usuário(a) nas postagens. A IA responderá como esta agent. user: Usuário(a) create_user: Criar usuário(a) - create_user_help: Como alternativa, você poderá anexar um(a) usuário(a) a esta persona. Se fizer isso, a IA usará este(a) usuário(a) para responder aos pedidos. + create_user_help: Como alternativa, você poderá anexar um(a) usuário(a) a esta agent. Se fizer isso, a IA usará este(a) usuário(a) para responder aos pedidos. default_llm: Modelo de linguagem padrão - default_llm_help: O modelo de linguagem padrão a ser usado para esta persona. É obrigatório se você quiser mencionar a persona em postagens públicas. + default_llm_help: O modelo de linguagem padrão a ser usado para esta agent. É obrigatório se você quiser mencionar a agent em postagens públicas. question_consolidator_llm: Modelo de linguagem para consolidador de pergunta question_consolidator_llm_help: O modelo de linguagem a ser usado para o consolidador de pergunta. Para economizar, você pode escolher um modelo menos robusto. system_prompt: Prompt do sistema forced_tool_strategy: Estratégia de ferramenta forçada allow_chat_direct_messages: "Permitir mensagens diretas do chat" - allow_chat_direct_messages_help: "Ative para que os(às) usuários(as) nos grupos permitidos possam enviar mensagens diretas a esta persona." + allow_chat_direct_messages_help: "Ative para que os(às) usuários(as) nos grupos permitidos possam enviar mensagens diretas a esta agent." allow_chat_channel_mentions: "Permitir menções no canal de chat" - allow_chat_channel_mentions_help: "Ative para os(as) usuários(as) nos grupos permitidos poderem mencionar esta persona nos canais de chat" - allow_personal_messages: "Permitir mensagens pessoais" - allow_personal_messages_help: "Ative para que os(as) usuários(as) nos grupos permitidos possam enviar mensagens pessoais a esta persona." + allow_chat_channel_mentions_help: "Ative para os(as) usuários(as) nos grupos permitidos poderem mencionar esta agent nos canais de chat" + allow_agentl_messages: "Permitir mensagens pessoais" + allow_agentl_messages_help: "Ative para que os(as) usuários(as) nos grupos permitidos possam enviar mensagens pessoais a esta agent." allow_topic_mentions: "Permtir menções de tópicos" - allow_topic_mentions_help: "Ative para os(as) usuários(as) nos grupos permitidos poderem mencionar esta persona nos tópicos." + allow_topic_mentions_help: "Ative para os(as) usuários(as) nos grupos permitidos poderem mencionar esta agent nos tópicos." force_default_llm: "Usar sempre o modelo de linguagem padrão" save: "Salvar" - saved: "Persona salva" + saved: "Agent salva" enabled: "Ativado(a)?" tools: "Ferramentas ativadas" forced_tools: "Ferramentas forçadas" allowed_groups: "Grupos permitidos" - confirm_delete: "Você tem certeza de que deseja excluir esta persona?" - new: "Nova persona" - no_personas: "Você ainda não criou nenhuma persona" - title: "Personas" - short_title: "Personas" + confirm_delete: "Você tem certeza de que deseja excluir esta agent?" + new: "Nova agent" + no_agents: "Você ainda não criou nenhuma agent" + title: "Agents" + short_title: "Agents" delete: "Excluir" temperature: "Temperatura" temperature_help: "A Temperatura a ser usada para o LLM. Aumente para incrementar a criatividade (deixe vazio para usar o padrão do modelo, que geralmente é um valor que varia entre 0.0 e 2.0)" top_p: "P Máximo" top_p_help: "O P Máximo a ser usado para o LLM, aumente para incrementar o fator aleatório (deixe vazio para usar o padrão do modelo, que geralmente é um valor que varia entre 0.0 e 1.0)" priority: "Prioridade" - priority_help: "Personas de prioridade são exibidas aos(às) usuários(as) no topo da lista de personas. Se várias personas tiverem prioridade, serão escolhidas em ordem alfabética." + priority_help: "Agents de prioridade são exibidas aos(às) usuários(as) no topo da lista de agents. Se várias agents tiverem prioridade, serão escolhidas em ordem alfabética." tool_options: "Opções de ferramenta" rag_conversation_chunks: "Pesquisar pedaços de conversa" rag_conversation_chunks_help: "O número de pedaços a serem usados para pesquisas de modelo RAG. Aumente para incrementar a quantidade de contexto que a IA pode usar." - persona_description: "Personas são um recurso poderoso que permite personalizar o comportamento da engine de IA no seu fórum do Discourse. Atuam como uma \"mensagem de sistema\" que orienta as respostas e as interações da IA, ajudando a criar uma experiência mais personzalidada e envolvente para o(a) usuário(a)." + agent_description: "Agents são um recurso poderoso que permite agentlizar o comportamento da engine de IA no seu fórum do Discourse. Atuam como uma \"mensagem de sistema\" que orienta as respostas e as interações da IA, ajudando a criar uma experiência mais personzalidada e envolvente para o(a) usuário(a)." response_format: open_modal: "Editar" modal: @@ -369,14 +369,14 @@ pt_BR: six_hours: "6 horas" day: "24 horas" week: "Sete dias" - custom: "Personalizado..." + custom: "Agentlizado..." hours: "horas" max_tokens_help: "Quantidade máxima de tokens (palavras e caracteres) que podem ser usados por cada usuário(a) neste grupo durante o período especificado. Tokens são unidades usadas para modelos de IA processarem texto: 1 token equivale a aproximadamente 4 caracteres ou 3/4 de uma palavra." max_usages_help: "A quantidade máxima de vezes que cada usuário(a) neste grupo pode usar o modelo de IA durante o período especificado. Essa cota é rastreada por cada usuário(a) individual e não é compartilhada no grupo." usage: ai_bot: "Bot de IA" ai_helper: "Ajudante" - ai_persona: "Persona (%{persona})" + ai_agent: "Agent (%{agent})" ai_summarization: "Resumir" ai_embeddings_semantic_search: "Pesquisa com IA" ai_spam: "Spam" @@ -429,7 +429,7 @@ pt_BR: samba_nova: "SambaNova" mistral: "Mistral" open_router: "OpenRouter" - fake: "Personalizados(as)" + fake: "Agentlizados(as)" provider_fields: access_key_id: "ID da chave de acesso do AWS Bedrock" region: "Região do AWS Bedrock" @@ -463,8 +463,8 @@ pt_BR: discard: "Descartar" changes: "Edições sugeridas" custom_prompt: - title: "Prompt personalizado" - placeholder: "Insira um prompt personalizado..." + title: "Prompt agentlizado" + placeholder: "Insira um prompt agentlizado..." submit: "Enviar prompt" translate_prompt: "Traduzir para %{language}" post_options_menu: @@ -542,7 +542,7 @@ pt_BR: google: "Google" cloudflare: "Cloudflare" CDCK: "CDCK" - fake: "Personalizados(as)" + fake: "Agentlizados(as)" provider_fields: model_name: "Nome do modelo" semantic_search: "Tópicos (semântica)" diff --git a/config/locales/client.ro.yml b/config/locales/client.ro.yml index b15d658c..e1f0cd37 100644 --- a/config/locales/client.ro.yml +++ b/config/locales/client.ro.yml @@ -57,8 +57,8 @@ ro: total_requests: "Total cereri" periods: last_day: "Ultimele 24 de ore" - custom: "Personalizat..." - ai_persona: + custom: "Agentlizat..." + ai_agent: back: "Înapoi" name: "Nume" edit: "Modifică" @@ -96,7 +96,7 @@ ro: hour: "o oră" six_hours: "6 de ore" day: "24 de ore" - custom: "Personalizat..." + custom: "Agentlizat..." hours: "de ore" usage: ai_summarization: "Rezumat" @@ -107,7 +107,7 @@ ro: success: "Succes!" providers: google: "Google" - fake: "Personalizat" + fake: "Agentlizat" ai_helper: context_menu: cancel: "Anulare" @@ -133,7 +133,7 @@ ro: display_name: "Nume" providers: google: "Google" - fake: "Personalizat" + fake: "Agentlizat" ai_bot: debug_ai_modal: request: "Cere" diff --git a/config/locales/client.ru.yml b/config/locales/client.ru.yml index 2f9a31da..c2a0dc29 100644 --- a/config/locales/client.ru.yml +++ b/config/locales/client.ru.yml @@ -128,7 +128,7 @@ ru: flag_post: label: "Пометить публикацию" description: "Пометить публикацию (как спам или для отправки на проверку)" - include_personal_messages: + include_agentl_messages: label: "Включать личные сообщения" description: "Также сканировать и выполнять триаж личных сообщений" model: @@ -219,7 +219,7 @@ ru: last_week: "На прошлой неделе" last_month: "В прошлом месяце" custom: "Другое…" - ai_persona: + ai_agent: ai_tools: "Инструменты" tool_strategies: all: "Применить ко всем ответам" @@ -259,8 +259,8 @@ ru: allow_chat_direct_messages_help: "Если параметр включен, пользователи в разрешенных группах смогут отправлять прямые сообщения этой персоне." allow_chat_channel_mentions: "Разрешить упоминания в каналах чата" allow_chat_channel_mentions_help: "Если параметр включен, пользователи в разрешенных группах смогут упоминать эту персону в каналах чата." - allow_personal_messages: "Разрешить личные сообщения" - allow_personal_messages_help: "Если параметр включен, пользователи в разрешенных группах смогут отправлять личные сообщения этой персоне." + allow_agentl_messages: "Разрешить личные сообщения" + allow_agentl_messages_help: "Если параметр включен, пользователи в разрешенных группах смогут отправлять личные сообщения этой персоне." allow_topic_mentions: "Разрешить упоминания в темах" allow_topic_mentions_help: "Если параметр включен, пользователи в разрешенных группах смогут упоминать эту персону в темах." force_default_llm: "Всегда использовать языковую модель по умолчанию" @@ -272,7 +272,7 @@ ru: allowed_groups: "Разрешённые группы" confirm_delete: "Точно удалить эту персону?" new: "Новая персона" - no_personas: "Вы еще не создали ни одной персоны" + no_agents: "Вы еще не создали ни одной персоны" title: "Персоны" short_title: "Персоны" delete: "Удалить" @@ -285,7 +285,7 @@ ru: tool_options: "Параметры инструмента" rag_conversation_chunks: "Фрагменты разговора для поиска" rag_conversation_chunks_help: "Количество фрагментов для поиска в модели RAG. Увеличьте это значение, чтобы увеличить объем контекста, который может использовать AI." - persona_description: "Персоны — полезная функция, с помощью которой вы можете настроить поведение движка AI на вашем форуме Discourse. Они действуют как «системное сообщение», задающее направление для ответов AI и обеспечивающее более персонализированное взаимодействие с пользователями." + agent_description: "Персоны — полезная функция, с помощью которой вы можете настроить поведение движка AI на вашем форуме Discourse. Они действуют как «системное сообщение», задающее направление для ответов AI и обеспечивающее более персонализированное взаимодействие с пользователями." response_format: open_modal: "Изменить" modal: @@ -378,7 +378,7 @@ ru: usage: ai_bot: "AI-бот" ai_helper: "Помощник" - ai_persona: "Персона (%{persona})" + ai_agent: "Персона (%{agent})" ai_summarization: "Сводка" ai_embeddings_semantic_search: "AI-поиск" ai_spam: "Спам" diff --git a/config/locales/client.sk.yml b/config/locales/client.sk.yml index 35f8bc76..6edb35ba 100644 --- a/config/locales/client.sk.yml +++ b/config/locales/client.sk.yml @@ -61,7 +61,7 @@ sk: periods: last_day: "Posledných 24 hodín" custom: "Vlastné..." - ai_persona: + ai_agent: back: "Späť" name: "Meno" edit: "Upraviť" diff --git a/config/locales/client.sl.yml b/config/locales/client.sl.yml index 5fb6be55..cdeba8e1 100644 --- a/config/locales/client.sl.yml +++ b/config/locales/client.sl.yml @@ -55,7 +55,7 @@ sl: periods: last_day: "Zadnjih 24 ur" custom: "Po meri..." - ai_persona: + ai_agent: back: "Nazaj" name: "Ime" edit: "Uredi" diff --git a/config/locales/client.sq.yml b/config/locales/client.sq.yml index bf1cb5d3..f83aa628 100644 --- a/config/locales/client.sq.yml +++ b/config/locales/client.sq.yml @@ -52,7 +52,7 @@ sq: summary: "Përmbledhja" username: "Emri i përdoruesit" total_requests: "Total requests" - ai_persona: + ai_agent: back: "Kthehu mbrapa" name: "Emri" edit: "Redakto" diff --git a/config/locales/client.sr.yml b/config/locales/client.sr.yml index 34c25906..0a26d85a 100644 --- a/config/locales/client.sr.yml +++ b/config/locales/client.sr.yml @@ -50,7 +50,7 @@ sr: username: "Korisničko Ime" periods: last_day: "Последња 24 сата" - ai_persona: + ai_agent: back: "Nazad" name: "Ime foruma" edit: "Izmeni" diff --git a/config/locales/client.sv.yml b/config/locales/client.sv.yml index 89b8ac36..57addb50 100644 --- a/config/locales/client.sv.yml +++ b/config/locales/client.sv.yml @@ -58,7 +58,7 @@ sv: periods: last_day: "Senaste 24 timmarna" custom: "Anpassad..." - ai_persona: + ai_agent: back: "Tillbaka" name: "Namn" edit: "Redigera" diff --git a/config/locales/client.sw.yml b/config/locales/client.sw.yml index 0ee54e82..ce083b3e 100644 --- a/config/locales/client.sw.yml +++ b/config/locales/client.sw.yml @@ -52,7 +52,7 @@ sw: summary: "Muhtasari" username: "Jina la mtumiaji" total_requests: "Jumla ya Maombi" - ai_persona: + ai_agent: back: "Iliyopita" name: "Jina" edit: "Hariri" diff --git a/config/locales/client.te.yml b/config/locales/client.te.yml index 42afea65..2bf0b7e6 100644 --- a/config/locales/client.te.yml +++ b/config/locales/client.te.yml @@ -52,7 +52,7 @@ te: summary: "సారాంశం" username: "సభ్యనామం" total_requests: "మొత్తం అభ్యర్థనలు" - ai_persona: + ai_agent: back: "వెనుకకు" name: "పేరు" edit: "సవరించండి" diff --git a/config/locales/client.th.yml b/config/locales/client.th.yml index 675b58a9..9f0e6542 100644 --- a/config/locales/client.th.yml +++ b/config/locales/client.th.yml @@ -54,7 +54,7 @@ th: username: "ชื่อผู้ใช้" periods: last_day: "24 ชั่วโมงที่ผ่านมา" - ai_persona: + ai_agent: back: "กลับ" name: "ชื่อ" edit: "แก้ไข" diff --git a/config/locales/client.tr_TR.yml b/config/locales/client.tr_TR.yml index 9f126bd2..08d7b29f 100644 --- a/config/locales/client.tr_TR.yml +++ b/config/locales/client.tr_TR.yml @@ -128,7 +128,7 @@ tr_TR: flag_post: label: "Gönderiye bayrak ekle" description: "Gönderiye bayrak ekler (spam olarak veya inceleme için)" - include_personal_messages: + include_agentl_messages: label: "Kişisel mesaj dâhil edin" description: "Ayrıca kişisel mesajları tarayın ve önceliklendirin" model: @@ -219,7 +219,7 @@ tr_TR: last_week: "Geçen hafta" last_month: "Geçen ay" custom: "Özel..." - ai_persona: + ai_agent: ai_tools: "Araçlar" tool_strategies: all: "Tüm yanıtlara uygula" @@ -257,8 +257,8 @@ tr_TR: allow_chat_direct_messages_help: "Etkinleştirilirse izin verilen gruplardaki kullanıcılar bu kişiliğe doğrudan mesaj gönderebilir." allow_chat_channel_mentions: "Sohbet kanalından bahsetmeye izin ver" allow_chat_channel_mentions_help: "Etkinleştirilirse izin verilen gruplardaki kullanıcılar sohbet kanallarında bu kişilikten bahsedebilir." - allow_personal_messages: "Kişisel mesajlara izin ver" - allow_personal_messages_help: "Etkinleştirilirse izin verilen gruplardaki kullanıcılar bu kişiliğe kişisel mesaj gönderebilir." + allow_agentl_messages: "Kişisel mesajlara izin ver" + allow_agentl_messages_help: "Etkinleştirilirse izin verilen gruplardaki kullanıcılar bu kişiliğe kişisel mesaj gönderebilir." allow_topic_mentions: "Konu bahsetmelerine izin ver" allow_topic_mentions_help: "Etkinleştirilirse izin verilen gruplardaki kullanıcılar konularda bu kişilikten bahsedebilir." force_default_llm: "Her zaman varsayılan dil modelini kullan" @@ -270,7 +270,7 @@ tr_TR: allowed_groups: "İzin verilen gruplar" confirm_delete: "Bu kişiliği silmek istediğinizden emin misiniz?" new: "Yeni kişilik" - no_personas: "Henüz herhangi bir kişilik oluşturmadınız" + no_agents: "Henüz herhangi bir kişilik oluşturmadınız" title: "Kişilikler" short_title: "Kişiler" delete: "Sil" @@ -283,7 +283,7 @@ tr_TR: tool_options: "Araç seçenekleri" rag_conversation_chunks: "Konuşma parçalarını ara" rag_conversation_chunks_help: "RAG modeli aramaları için kullanılacak parça sayısı. YZ'nin kullanabileceği bağlam miktarını artırmak için artırın." - persona_description: "Kişilikler, Discourse forumunuzdaki YZ motorunun davranışını özelleştirmenize olanak tanıyan güçlü bir özelliktir. YZ'nin yanıtlarını ve etkileşimlerini yönlendiren bir \"sistem mesajı\" olarak hareket ederek daha kişiselleştirilmiş ve ilgi çekici bir kullanıcı deneyimi oluşturmaya yardımcı olurlar." + agent_description: "Kişilikler, Discourse forumunuzdaki YZ motorunun davranışını özelleştirmenize olanak tanıyan güçlü bir özelliktir. YZ'nin yanıtlarını ve etkileşimlerini yönlendiren bir \"sistem mesajı\" olarak hareket ederek daha kişiselleştirilmiş ve ilgi çekici bir kullanıcı deneyimi oluşturmaya yardımcı olurlar." response_format: open_modal: "Düzenle" modal: @@ -376,7 +376,7 @@ tr_TR: usage: ai_bot: "YZ botu" ai_helper: "Yardımcı" - ai_persona: "Kişilik (%{persona})" + ai_agent: "Kişilik (%{agent})" ai_summarization: "Özetle" ai_embeddings_semantic_search: "YZ araması" ai_spam: "İstenmeyen içerik" diff --git a/config/locales/client.ug.yml b/config/locales/client.ug.yml index d382de86..061dafb7 100644 --- a/config/locales/client.ug.yml +++ b/config/locales/client.ug.yml @@ -59,7 +59,7 @@ ug: total_requests: "جەمئىي ئىلتىماس" periods: custom: "ئىختىيارى..." - ai_persona: + ai_agent: back: "كەينى" name: "ئىسمى" edit: "تەھرىر" diff --git a/config/locales/client.uk.yml b/config/locales/client.uk.yml index eaa3f5a0..92f8c2a6 100644 --- a/config/locales/client.uk.yml +++ b/config/locales/client.uk.yml @@ -145,7 +145,7 @@ uk: periods: last_day: "Останні 24 години" custom: "Власний…" - ai_persona: + ai_agent: ai_tools: "Інструменти" tool_strategies: all: "Застосувати до всіх відповідей" @@ -172,7 +172,7 @@ uk: question_consolidator_llm_help: Мовна модель для об’єднання запитань, ви можете вибрати менш потужну модель, щоб заощадити кошти. allow_chat_direct_messages_help: "Якщо увімкнено, користувачі з дозволених груп можуть надсилати прямі повідомлення цій персоні." allow_chat_channel_mentions_help: "Якщо увімкнено, користувачі в дозволених групах можуть згадувати цю персону в чаті." - allow_personal_messages_help: "Якщо увімкнено, користувачі з дозволених груп можуть надсилати особисті повідомлення цій персоні." + allow_agentl_messages_help: "Якщо увімкнено, користувачі з дозволених груп можуть надсилати особисті повідомлення цій персоні." allow_topic_mentions_help: "Якщо увімкнено, користувачі в дозволених групах можуть згадувати цю персону в темах." save: "Зберегти" enabled: "Включено?" @@ -253,7 +253,7 @@ uk: custom: "Власний…" hours: "годин" usage: - ai_persona: "Персона (%{persona})" + ai_agent: "Персона (%{agent})" ai_summarization: "Підсумок" ai_spam: "Спам" in_use_warning: diff --git a/config/locales/client.ur.yml b/config/locales/client.ur.yml index 9fa04d90..68c19ad4 100644 --- a/config/locales/client.ur.yml +++ b/config/locales/client.ur.yml @@ -58,7 +58,7 @@ ur: periods: last_day: "پچھلے 24 گھنٹے" custom: "ترمیم..." - ai_persona: + ai_agent: back: "واپس" name: "نام" edit: "ترمیم کریں" diff --git a/config/locales/client.vi.yml b/config/locales/client.vi.yml index 9ab7bee6..3eb7ba00 100644 --- a/config/locales/client.vi.yml +++ b/config/locales/client.vi.yml @@ -56,7 +56,7 @@ vi: periods: last_day: "24 giờ qua" custom: "Tùy chỉnh..." - ai_persona: + ai_agent: back: "Quay lại" name: "Tên" edit: "Sửa" diff --git a/config/locales/client.zh_CN.yml b/config/locales/client.zh_CN.yml index f7481908..502e3115 100644 --- a/config/locales/client.zh_CN.yml +++ b/config/locales/client.zh_CN.yml @@ -13,7 +13,7 @@ zh_CN: discourse_ai: search: "允许 AI 搜索" stream_completion: "允许流式 AI 角色补全" - update_personas: "允许更新 AI 角色模型" + update_agents: "允许更新 AI 角色模型" site_settings: categories: discourse_ai: "Discourse AI" @@ -102,9 +102,9 @@ zh_CN: tool: label: "工具" description: "用于初步分类的工具(工具不得包含已定义的参数)" - llm_persona_triage: + llm_agent_triage: fields: - persona: + agent: label: "角色模型" description: "用于初步分类的 AI 角色模型(必须设置默认 LLM 和用户)" whisper: @@ -144,7 +144,7 @@ zh_CN: flag_post: label: "举报帖子" description: "举报帖子(垃圾内容或请求审核)" - include_personal_messages: + include_agentl_messages: label: "包括私信" description: "同时扫描私信并对其进行分类" model: @@ -234,7 +234,7 @@ zh_CN: last_week: "上周" last_month: "上个月" custom: "自定义…" - ai_persona: + ai_agent: ai_tools: "工具" tool_strategies: all: "应用于所有回复" @@ -271,8 +271,8 @@ zh_CN: allow_chat_direct_messages_help: "如果启用,允许的群组中的用户可以向此角色发送直接消息。" allow_chat_channel_mentions: "允许聊天频道提及" allow_chat_channel_mentions_help: "如果启用,允许的群组中的用户可以在聊天频道中提及此角色。" - allow_personal_messages: "允许私信" - allow_personal_messages_help: "如果启用,允许的群组中的用户可以向此角色发送私信。" + allow_agentl_messages: "允许私信" + allow_agentl_messages_help: "如果启用,允许的群组中的用户可以向此角色发送私信。" allow_topic_mentions: "允许话题提及" allow_topic_mentions_help: "如果启用,允许的群组中的用户可以在话题中提及此角色。" force_default_llm: "始终使用默认语言模型" @@ -284,7 +284,7 @@ zh_CN: allowed_groups: "允许的群组" confirm_delete: "确定要删除此角色吗?" new: "新角色" - no_personas: "您还没有创建任何角色" + no_agents: "您还没有创建任何角色" title: "角色" short_title: "角色" delete: "删除" @@ -297,7 +297,7 @@ zh_CN: tool_options: "工具选项" rag_conversation_chunks: "搜索对话分块" rag_conversation_chunks_help: "为 RAG 模型搜索使用的分块数。增加分块数会增加 AI 可以使用的上下文数量。" - persona_description: "角色是一种强大的功能,可以让您自定义 Discourse 论坛中 AI 引擎的行为。它们充当“系统消息”,指导 AI 的回答和互动,帮助创造更加个性化、引人入胜的用户体验。" + agent_description: "角色是一种强大的功能,可以让您自定义 Discourse 论坛中 AI 引擎的行为。它们充当“系统消息”,指导 AI 的回答和互动,帮助创造更加个性化、引人入胜的用户体验。" response_format: open_modal: "编辑" modal: @@ -394,7 +394,7 @@ zh_CN: usage: ai_bot: "AI 机器人" ai_helper: "助手" - ai_persona: "角色 (%{persona})" + ai_agent: "角色 (%{agent})" ai_summarization: "总结" ai_embeddings_semantic_search: "AI 搜索" ai_spam: "垃圾内容" @@ -580,7 +580,7 @@ zh_CN: collapse_view_label: "退出全屏(按 ESC 或“返回”按钮)" click_to_run_label: "运行工件" ai_bot: - persona: "角色模型" + agent: "角色模型" llm: "模型" pm_warning: "版主会定期监控 AI 聊天机器人消息。" cancel_streaming: "停止回复" diff --git a/config/locales/client.zh_TW.yml b/config/locales/client.zh_TW.yml index bb52aceb..29b2445d 100644 --- a/config/locales/client.zh_TW.yml +++ b/config/locales/client.zh_TW.yml @@ -56,7 +56,7 @@ zh_TW: periods: last_day: "過去 24 小時" custom: "自訂..." - ai_persona: + ai_agent: back: "上一步" name: "名字" edit: "編輯" diff --git a/config/locales/server.ar.yml b/config/locales/server.ar.yml index 61f91d52..9a574168 100644 --- a/config/locales/server.ar.yml +++ b/config/locales/server.ar.yml @@ -242,10 +242,10 @@ ar: ai_bot: reply_error: "عذراً، يبدو أن نظامنا واجه مشكلة غير متوقعة أثناء محاولة الرد.\n\n[details='Error details']\n%{details}\n[/details]" default_pm_prefix: "[رسالة خاصة دون عنوان من روبوت ذكاء اصطناعي]" - personas: + agents: default_llm_required: "نموذج اللغة الكبير الافتراضي مطلوب قبل تفعيل الدردشة" - cannot_delete_system_persona: "لا يمكن حذف شخصيات النظام، يُرجى إيقافها بدلًا من ذلك" - cannot_edit_system_persona: "يمكن إعادة تسمية شخصيات النظام فقط، ولا يجوز لك تعديل الأدوات أو رسائل مطالبة النظام. قم بإيقافها وعمل نسخة منها بدلًا من ذلك." + cannot_delete_system_agent: "لا يمكن حذف شخصيات النظام، يُرجى إيقافها بدلًا من ذلك" + cannot_edit_system_agent: "يمكن إعادة تسمية شخصيات النظام فقط، ولا يجوز لك تعديل الأدوات أو رسائل مطالبة النظام. قم بإيقافها وعمل نسخة منها بدلًا من ذلك." github_helper: name: "مساعد GitHub" description: "روبوت ذكاء اصطناعي متخصص في المساعدة في المهام والأسئلة المتعلقة بمنصة GitHub" @@ -465,11 +465,11 @@ ar: quota_exceeded: "لقد تجاوزت الحصة المُخصَّصة لهذا النموذج. يُرجى إعادة المحاولة بعد %{relative_time}." quota_required: "يجب عليك تحديد الحد الأقصى للرموز أو الاستخدامات لهذا النموذج" no_query_specified: معلمة الاستعلام مطلوبة، يُرجى تحديدها. - no_user_for_persona: لا تملك الشخصية المحدَّدة مستخدمًا مرتبطًا بها. - persona_not_found: الشخصية المحدَّدة غير موجودة. تحقَّق من معلمتَي persona_name أو persona_id. + no_user_for_agent: لا تملك الشخصية المحدَّدة مستخدمًا مرتبطًا بها. + agent_not_found: الشخصية المحدَّدة غير موجودة. تحقَّق من معلمتَي agent_name أو agent_id. no_user_specified: اسم المستخدم أو معلمة user_unique_id مطلوبة، يُرجى تحديدها. user_not_found: المستخدم المحدَّد غير موجود. تحقَّق من معلمة username. - persona_disabled: الشخصية المحدَّدة متوقفة. تحقَّق من معلمتَي persona_name أو persona_id. + agent_disabled: الشخصية المحدَّدة متوقفة. تحقَّق من معلمتَي agent_name أو agent_id. no_default_llm: يجب أن يكون للشخصية معلمة default_llm محدَّدة. user_not_allowed: غير مسموح للمستخدم بالمشاركة في الموضوع. prompt_message_length: تتجاوز الرسالة الحد الأقصى لعدد الحروف، والبالغ 1000 حرف، بمقدار %{idx}. diff --git a/config/locales/server.de.yml b/config/locales/server.de.yml index 6c79ef6b..46502ef5 100644 --- a/config/locales/server.de.yml +++ b/config/locales/server.de.yml @@ -16,9 +16,9 @@ de: llm_tool_triage: title: Triage von Beiträgen mit dem KI-Tool description: "Triage von Beiträgen mit benutzerdefinierter Logik in einem KI-Tool" - llm_persona_triage: - title: Triage von Posts mit KI-Persona - description: "Reagiere auf Beiträge mit einer bestimmten KI-Persona" + llm_agent_triage: + title: Triage von Posts mit KI-Agent + description: "Reagiere auf Beiträge mit einer bestimmten KI-Agent" llm_triage: title: Beiträge mithilfe von KI sortieren description: "Beiträge mithilfe eines großen Sprachmodells sortieren" @@ -86,7 +86,7 @@ de: ai_embeddings_per_post_enabled: Erstelle Einbettungen für jeden Beitrag ai_summarization_enabled: "Aktiviere die Zusammenfassungsfunktion" ai_summarization_model: "Modell, das für die Zusammenfassung verwendet werden soll" - ai_summarization_persona: "Persona, die für die Zusammenfassungsfunktion verwendet werden soll" + ai_summarization_agent: "Agent, die für die Zusammenfassungsfunktion verwendet werden soll" ai_custom_summarization_allowed_groups: "Gruppen, die neue Zusammenfassungen erstellen dürfen." ai_pm_summarization_allowed_groups: "Gruppen, die Zusammenfassungen in PN erstellen und ansehen dürfen." ai_summary_gists_enabled: "Erstelle automatisch kurze Zusammenfassungen der letzten Antworten in Themen" @@ -108,7 +108,7 @@ de: ai_discord_app_id: "Die ID der Discord-Anwendung, mit der du die Discord-Suche verbinden möchtest" ai_discord_app_public_key: "Der öffentliche Schlüssel der Discord-Anwendung, mit der du die Discord-Suche verbinden möchtest" ai_discord_search_mode: "Wähle den Suchmodus für die Discord-Suche aus" - ai_discord_search_persona: "Die Persona, die für die Discord-Suche verwendet wird." + ai_discord_search_agent: "Die Agent, die für die Discord-Suche verwendet wird." ai_discord_allowed_guilds: "Discord-Gilden (Server), auf denen der Bot suchen darf" ai_bot_enable_dedicated_ux: "Erlaube eine Vollbild-Bot-Oberfläche anstelle einer PM" reviewables: @@ -269,10 +269,10 @@ de: reply_error: "Entschuldigung, es sieht so aus, als ob unser System beim Versuch, zu antworten, auf ein unerwartetes Problem gestoßen ist.\n\n[details='Fehlerdetails']\n%{details}\n[/details]" default_pm_prefix: "[KI-Bot-PN ohne Titel]" thinking: "Ich denke..." - personas: + agents: default_llm_required: "Standard-LLM-Modell ist erforderlich, bevor der Chat aktiviert werden kann" - cannot_delete_system_persona: "System-Personas können nicht gelöscht werden, bitte deaktiviere sie stattdessen" - cannot_edit_system_persona: "System-Personas können nur umbenannt werden. Du darfst keine Tools oder die System-Eingabeaufforderung bearbeiten, sondern musst sie deaktivieren und eine Kopie erstellen" + cannot_delete_system_agent: "System-Agents können nicht gelöscht werden, bitte deaktiviere sie stattdessen" + cannot_edit_system_agent: "System-Agents können nur umbenannt werden. Du darfst keine Tools oder die System-Eingabeaufforderung bearbeiten, sondern musst sie deaktivieren und eine Kopie erstellen" cannot_have_duplicate_tools: "Es kann keine doppelten Werkzeuge geben" github_helper: name: "GitHub-Helfer" @@ -312,10 +312,10 @@ de: description: "KI-Bot, spezialisiert auf die Erstellung interaktiver Web-Artefakte" summarizer: name: "Zusammenfasser" - description: "Standard-Persona für die KI-Zusammenfassungen" + description: "Standard-Agent für die KI-Zusammenfassungen" short_summarizer: name: "Zusammenfasser (Kurzform)" - description: "Standard-Persona zur Erstellung von KI-Kurzzusammenfassungen für die Elemente der Themenlisten" + description: "Standard-Agent zur Erstellung von KI-Kurzzusammenfassungen für die Elemente der Themenlisten" topic_not_found: "Zusammenfassung nicht verfügbar, Thema nicht gefunden!" summarizing: "Thema zusammenfassen" searching: "Suche nach: „%{query}“" @@ -502,7 +502,7 @@ de: one: "Wir konnten dieses Modell nicht löschen, weil es von %{settings} verwendet wird. Aktualisiere die Einstellung und versuche es erneut." other: "Wir konnten dieses Modell nicht löschen, weil %{settings} es verwenden. Aktualisiere die Einstellungen und versuche es erneut." cannot_edit_builtin: "Du kannst ein integriertes Modell nicht bearbeiten." - personas: + agents: malformed_examples: "Die angegebenen Beispiele haben das falsche Format." embeddings: delete_failed: "Dieses Modell wird derzeit verwendet. Aktualisiere zuerst `ai embeddings selected model`." @@ -531,12 +531,12 @@ de: quota_exceeded: "Du hast das Kontingent für dieses Modell überschritten. Bitte versuche es erneut in %{relative_time}." quota_required: "Du musst die maximale Anzahl an Token oder Verwendungen für dieses Modell angeben" no_query_specified: Der Abfrageparameter ist erforderlich, bitte gib ihn an. - no_user_for_persona: Die angegebene Persona hat keinen Benutzer, der mit ihr verbunden ist. - persona_not_found: Die angegebene Persona existiert nicht. Überprüfe die Parameter persona_name oder persona_id. + no_user_for_agent: Die angegebene Agent hat keinen Benutzer, der mit ihr verbunden ist. + agent_not_found: Die angegebene Agent existiert nicht. Überprüfe die Parameter agent_name oder agent_id. no_user_specified: Der Benutzername oder der Parameter user_unique_id ist erforderlich. Bitte gib ihn an. user_not_found: Der angegebene Benutzer existiert nicht. Überprüfe den Parameter username. - persona_disabled: Die angegebene Persona ist deaktiviert. Überprüfe die Parameter persona_name oder persona_id. - no_default_llm: Die Persona muss eine default_llm definiert haben. + agent_disabled: Die angegebene Agent ist deaktiviert. Überprüfe die Parameter agent_name oder agent_id. + no_default_llm: Die Agent muss eine default_llm definiert haben. user_not_allowed: Der Benutzer darf nicht am Thema teilnehmen. prompt_message_length: Die Nachricht %{idx} hat die 1000-Zeichen-Grenze überschritten. dashboard: diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 4a71b0db..edfc1a2a 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -10,9 +10,9 @@ en: llm_tool_triage: title: Triage posts using AI Tool description: "Triage posts using custom logic in an AI tool" - llm_persona_triage: - title: Triage posts using AI Persona - description: "Respond to posts using a specific AI persona" + llm_agent_triage: + title: Triage posts using AI Agent + description: "Respond to posts using a specific AI agent" llm_triage: title: Triage posts using AI description: "Triage posts using a large language model" @@ -76,7 +76,7 @@ en: ai_auto_image_caption_allowed_groups: "Users on these groups can toggle automatic image captioning." ai_embeddings_selected_model: "Use the selected model for generating embeddings." - ai_embeddings_generate_for_pms: "Generate embeddings for personal messages." + ai_embeddings_generate_for_pms: "Generate embeddings for agentl messages." ai_embeddings_semantic_related_topics_enabled: "Use Semantic Search for related topics." ai_embeddings_semantic_related_topics: "Maximum number of topics to show in related topic section." ai_embeddings_backfill_batch_size: "Number of embeddings to backfill every 15 minutes." @@ -88,7 +88,7 @@ en: ai_summarization_enabled: "Enable the summarize feature" ai_summarization_model: "Model to use for summarization" - ai_summarization_persona: "Persona to use for summarize feature" + ai_summarization_agent: "Agent to use for summarize feature" ai_custom_summarization_allowed_groups: "Groups allowed to use create new summaries." ai_pm_summarization_allowed_groups: "Groups allowed to create and view summaries in PMs." ai_summary_gists_enabled: "Generate brief summaries of latest replies in topics automatically" @@ -99,7 +99,7 @@ en: ai_bot_enable_chat_warning: "Display a warning when PM chat is initiated. Can be overriden by editing the translation string: discourse_ai.ai_bot.pm_warning" ai_bot_allowed_groups: "When the GPT Bot has access to the PM, it will reply to members of these groups." ai_bot_debugging_allowed_groups: "Allow these groups to see a debug button on posts which displays the raw AI request and response" - ai_bot_public_sharing_allowed_groups: "Allow these groups to share AI personal messages with the public via a unique publicly available link. Note: if your site requires login, shares will also require login." + ai_bot_public_sharing_allowed_groups: "Allow these groups to share AI agentl messages with the public via a unique publicly available link. Note: if your site requires login, shares will also require login." ai_bot_add_to_header: "Display a button in the header to start a PM with a AI Bot" ai_bot_github_access_token: "GitHub access token for use with GitHub AI tools (required for search support)" @@ -114,7 +114,7 @@ en: ai_discord_app_id: "The ID of the Discord application you would like to connect Discord search to" ai_discord_app_public_key: "The public key of the Discord application you would like to connect Discord search to" ai_discord_search_mode: "Select the search mode to use for Discord search" - ai_discord_search_persona: "The persona to use for Discord search." + ai_discord_search_agent: "The agent to use for Discord search." ai_discord_allowed_guilds: "Discord guilds (servers) where the bot is allowed to search" ai_bot_enable_dedicated_ux: "Allow for full screen bot interface, instead of a PM" @@ -129,7 +129,7 @@ en: description: "This report provides sentiment analysis for posts, grouped by category, with positive, negative, and neutral scores for each post and category." overall_sentiment: title: "Overall sentiment" - description: 'The chart compares the number of posts classified as either positive or negative. These are calculated when positive or negative scores > the set threshold score. This means neutral posts are not shown. Personal messages (PMs) are also excluded. Classified with "cardiffnlp/twitter-roberta-base-sentiment-latest"' + description: 'The chart compares the number of posts classified as either positive or negative. These are calculated when positive or negative scores > the set threshold score. This means neutral posts are not shown. Agentl messages (PMs) are also excluded. Classified with "cardiffnlp/twitter-roberta-base-sentiment-latest"' xaxis: "Positive(%)" yaxis: "Date" emotion_admiration: @@ -267,8 +267,8 @@ en: title: "%{title} - AI Conversation - %{site_name}" errors: not_allowed: "You are not allowed to share this topic" - other_people_in_pm: "Personal messages with other humans cannot be shared publicly" - other_content_in_pm: "Personal messages containing posts from other people cannot be shared publicly" + other_people_in_pm: "Agentl messages with other humans cannot be shared publicly" + other_content_in_pm: "Agentl messages containing posts from other people cannot be shared publicly" failed_to_share: "Failed to share the conversation" conversation_deleted: "Conversation share deleted successfully" spam_detection: @@ -283,10 +283,10 @@ en: reply_error: "Sorry, it looks like our system encountered an unexpected issue while trying to reply.\n\n[details='Error details']\n%{details}\n[/details]" default_pm_prefix: "[Untitled AI bot PM]" thinking: "Thinking..." - personas: + agents: default_llm_required: "Default LLM model is required prior to enabling Chat" - cannot_delete_system_persona: "System personas cannot be deleted, please disable it instead" - cannot_edit_system_persona: "System personas can only be renamed, you may not edit tools or system prompt, instead disable and make a copy" + cannot_delete_system_agent: "System agents cannot be deleted, please disable it instead" + cannot_edit_system_agent: "System agents can only be renamed, you may not edit tools or system prompt, instead disable and make a copy" cannot_have_duplicate_tools: "Can not have duplicate tools" github_helper: name: "GitHub Helper" @@ -326,10 +326,10 @@ en: description: "AI Bot specialized in creating interactive web artifacts" summarizer: name: "Summarizer" - description: "Default persona used to power AI summaries" + description: "Default agent used to power AI summaries" short_summarizer: name: "Summarizer (short form)" - description: "Default persona used to power AI short summaries for topic lists' items" + description: "Default agent used to power AI short summaries for topic lists' items" topic_not_found: "Summary unavailable, topic not found!" summarizing: "Summarizing topic" searching: "Searching for: '%{query}'" @@ -521,7 +521,7 @@ en: other: "We couldn't delete this model because %{settings} are using it. Update the settings and try again." cannot_edit_builtin: "You can't edit a built-in model." - personas: + agents: malformed_examples: "The given examples have the wrong format." embeddings: @@ -554,12 +554,12 @@ en: quota_exceeded: "You have exceeded the quota for this model. Please try again in %{relative_time}." quota_required: "You must specify maximum tokens or usages for this model" no_query_specified: The query parameter is required, please specify it. - no_user_for_persona: The persona specified does not have a user associated with it. - persona_not_found: The persona specified does not exist. Check the persona_name or persona_id params. + no_user_for_agent: The agent specified does not have a user associated with it. + agent_not_found: The agent specified does not exist. Check the agent_name or agent_id params. no_user_specified: The username or the user_unique_id parameter is required, please specify it. user_not_found: The user specified does not exist. Check the username param. - persona_disabled: The persona specified is disabled. Check the persona_name or persona_id params. - no_default_llm: The persona must have a default_llm defined. + agent_disabled: The agent specified is disabled. Check the agent_name or agent_id params. + no_default_llm: The agent must have a default_llm defined. user_not_allowed: The user is not allowed to participate in the topic. prompt_message_length: The message %{idx} is over the 1000 character limit. dashboard: diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index d83b5710..231618e1 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -56,7 +56,7 @@ es: composer_ai_helper_allowed_groups: "Los usuarios de estos grupos verán el botón del asistente de IA en el compositor." ai_helper_allowed_in_pm: "Activar el asistente de IA del compositor en los MP." ai_helper_model: "Modelo que se utilizará para el asistente de IA." - ai_helper_custom_prompts_allowed_groups: "Los usuarios de estos grupos verán la opción de aviso personalizado en el ayudante de IA." + ai_helper_custom_prompts_allowed_groups: "Los usuarios de estos grupos verán la opción de aviso agentlizado en el ayudante de IA." ai_helper_automatic_chat_thread_title_delay: "Retraso en minutos antes de que el ayudante de la IA establezca automáticamente el título del hilo del chat." ai_helper_automatic_chat_thread_title: "Establecer automáticamente los títulos de los hilos del chat en función de su contenido." ai_helper_illustrate_post_model: "Modelo a utilizar para la función ilustrar publicación del ayudante de IA del compositor" @@ -65,7 +65,7 @@ es: ai_helper_image_caption_model: "Selecciona el modelo que se utilizará para generar pies de foto" ai_auto_image_caption_allowed_groups: "Los usuarios de estos grupos pueden activar el subtitulado automático de imágenes." ai_embeddings_selected_model: "Utiliza el modelo seleccionado para generar incrustaciones." - ai_embeddings_generate_for_pms: "Generar incrustaciones para mensajes personales." + ai_embeddings_generate_for_pms: "Generar incrustaciones para mensajes agentles." ai_embeddings_semantic_related_topics_enabled: "Utilizar la Búsqueda semántica para temas relacionados." ai_embeddings_semantic_related_topics: "Número máximo de temas que se mostrarán en la sección de temas relacionados." ai_embeddings_backfill_batch_size: "Número de incrustaciones a rellenar cada 15 minutos." @@ -84,14 +84,14 @@ es: ai_bot_enable_chat_warning: "Muestra una advertencia cuando se inicia el chat de MP. Se puede anular editando la cadena de traducción: discourse_ai.ai_bot.pm_warning" ai_bot_allowed_groups: "Cuando el Bot GPT tenga acceso al MP, responderá a los miembros de estos grupos." ai_bot_debugging_allowed_groups: "Permitir que estos grupos vean un botón de depuración en las publicaciones que muestre la solicitud y la respuesta sin procesar de la IA." - ai_bot_public_sharing_allowed_groups: "Permitir que estos grupos compartan mensajes personales de IA con el público a través de un enlace único disponible públicamente. Nota: si tu sitio requiere inicio de sesión, los mensajes compartidos también requerirán inicio de sesión." + ai_bot_public_sharing_allowed_groups: "Permitir que estos grupos compartan mensajes agentles de IA con el público a través de un enlace único disponible públicamente. Nota: si tu sitio requiere inicio de sesión, los mensajes compartidos también requerirán inicio de sesión." ai_bot_add_to_header: "Mostrar un botón en el encabezado para iniciar un MP con un bot de IA" ai_bot_github_access_token: "Token de acceso a GitHub para utilizarlo con las herramientas de IA de GitHub (necesario para la compatibilidad con búsquedas)" ai_stability_api_key: "Clave API para la API de stability.ai" ai_stability_engine: "Motor de generación de imágenes que se utilizará para la API stability.ai" ai_stability_api_url: "URL para la API de stability.ai" - ai_google_custom_search_api_key: "Clave API para la API de búsqueda personalizada de Google, consulta: https://developers.google.com/custom-search" - ai_google_custom_search_cx: "CX para la API de búsqueda personalizada de Google" + ai_google_custom_search_api_key: "Clave API para la API de búsqueda agentlizada de Google, consulta: https://developers.google.com/custom-search" + ai_google_custom_search_cx: "CX para la API de búsqueda agentlizada de Google" reviewables: reasons: flagged_by_toxicity: El plugin de IA lo denunció tras clasificarlo como tóxico. @@ -99,7 +99,7 @@ es: reports: overall_sentiment: title: "Sentimiento general" - description: 'El gráfico compara el número de publicaciones clasificadas como positivas o negativas. Se calculan cuando las puntuaciones positivas o negativas superan la puntuación umbral establecida. Esto significa que no se muestran las publicaciones neutrales. También se excluyen los mensajes personales (MP). Clasificados con «cardiffnlp/twitter-roberta-base-sentiment-latest»' + description: 'El gráfico compara el número de publicaciones clasificadas como positivas o negativas. Se calculan cuando las puntuaciones positivas o negativas superan la puntuación umbral establecida. Esto significa que no se muestran las publicaciones neutrales. También se excluyen los mensajes agentles (MP). Clasificados con «cardiffnlp/twitter-roberta-base-sentiment-latest»' xaxis: "Positivas(%)" yaxis: "Fecha" emotion_admiration: @@ -193,7 +193,7 @@ es: view_changes: "Ver cambios" unknown_model: "Modelo de IA desconocido" tools: - custom_name: "%{name} (personalizado)" + custom_name: "%{name} (agentlizado)" presets: browse_web_jina: name: "Navegar por la web (jina.ai)" @@ -213,7 +213,7 @@ es: generate_titles: Sugerir títulos de temas proofread: Corregir el texto markdown_table: Generar tabla Markdown - custom_prompt: "Aviso personalizado" + custom_prompt: "Aviso agentlizado" explain: "Explicar" illustrate_post: "Ilustrar publicación" replace_dates: "Fechas inteligentes" @@ -230,8 +230,8 @@ es: title: "%{title} - Conversación IA - %{site_name}" errors: not_allowed: "No tienes permiso para compartir este tema." - other_people_in_pm: "Los mensajes personales con otros humanos no se pueden compartir públicamente" - other_content_in_pm: "Los mensajes personales que contengan mensajes de otras personas no pueden compartirse públicamente" + other_people_in_pm: "Los mensajes agentles con otros humanos no se pueden compartir públicamente" + other_content_in_pm: "Los mensajes agentles que contengan mensajes de otras agents no pueden compartirse públicamente" failed_to_share: "No se pudo compartir la conversación" conversation_deleted: "La conversación compartida se eliminó correctamente" spam_detection: @@ -243,10 +243,10 @@ es: ai_bot: reply_error: "Lo sentimos, parece que nuestro sistema ha encontrado un problema inesperado al intentar responder.\n\n[details='Detalles del error']\n%{details}\n[/details]" default_pm_prefix: "[MP de bot de IA sin título]" - personas: + agents: default_llm_required: "Se requiere el modelo LLM predeterminado antes de activar el Chat" - cannot_delete_system_persona: "Las personas del sistema no se pueden eliminar, desactívalas en su lugar" - cannot_edit_system_persona: "Las personas del sistema solo se pueden renombrar, no puedes editar las herramientas ni el aviso del sistema, en su lugar deshabilita y haz una copia" + cannot_delete_system_agent: "Las agents del sistema no se pueden eliminar, desactívalas en su lugar" + cannot_edit_system_agent: "Las agents del sistema solo se pueden renombrar, no puedes editar las herramientas ni el aviso del sistema, en su lugar deshabilita y haz una copia" github_helper: name: "Asistente de GitHub" description: "Bot de IA especializado en ayudar con tareas y preguntas relacionadas con GitHub" @@ -430,11 +430,11 @@ es: quota_exceeded: "Has superado la cuota para este modelo. Inténtalo de nuevo en %{relative_time}." quota_required: "Debes especificar los tokens o usos máximos para este modelo" no_query_specified: El parámetro de consulta es obligatorio, especifícalo. - no_user_for_persona: La persona especificada no tiene ningún usuario asociado. - persona_not_found: La persona especificada no existe. Comprueba los parámetros persona_name o persona_id. + no_user_for_agent: La agent especificada no tiene ningún usuario asociado. + agent_not_found: La agent especificada no existe. Comprueba los parámetros agent_name o agent_id. no_user_specified: El nombre de usuario o user_unique_id es obligatorio, por favor, especifícalo. user_not_found: El usuario especificado no existe. Comprueba el parámetro username. - persona_disabled: La persona especificada está desactivada. Comprueba los parámetros persona_name o persona_id. - no_default_llm: La persona debe tener un default_llm definido. + agent_disabled: La agent especificada está desactivada. Comprueba los parámetros agent_name o agent_id. + no_default_llm: La agent debe tener un default_llm definido. user_not_allowed: El usuario no está autorizado a participar en el tema. prompt_message_length: El mensaje %{idx} supera el límite de 1000 caracteres. diff --git a/config/locales/server.fa_IR.yml b/config/locales/server.fa_IR.yml index 35a7d9b5..899482ff 100644 --- a/config/locales/server.fa_IR.yml +++ b/config/locales/server.fa_IR.yml @@ -16,7 +16,7 @@ fa_IR: formatted_excerpt: "گفتگوی هوشمصنوعی با %{llm_name}:\n %{excerpt}" title: "%{title} - گفتگوی هوشمصنوعی - %{site_name}" ai_bot: - personas: + agents: dall_e3: name: "DALL-E 3" tool_summary: diff --git a/config/locales/server.fi.yml b/config/locales/server.fi.yml index 06cc4fd8..dc75cf13 100644 --- a/config/locales/server.fi.yml +++ b/config/locales/server.fi.yml @@ -243,10 +243,10 @@ fi: ai_bot: reply_error: "Näyttää siltä, että järjestelmässä tapahtui odottamaton ongelma, kun se yritti vastata.\n\n[details='Error details']\n%{details}\n[/details]" default_pm_prefix: "[Nimetön tekoälybotin yksityisviesti]" - personas: + agents: default_llm_required: "Oletus-LLM-malli vaaditaan ennen chatin käyttöönottoa" - cannot_delete_system_persona: "Järjestelmäpersoonia ei voi poistaa, poista se sen sijaan käytöstä" - cannot_edit_system_persona: "Järjestelmäpersoonia voi vain nimetä uudelleen, et saa muokata työkaluja tai järjestelmäkehotetta, sen sijaan voit poistaa ne käytöstä ja tehdä kopion" + cannot_delete_system_agent: "Järjestelmäpersoonia ei voi poistaa, poista se sen sijaan käytöstä" + cannot_edit_system_agent: "Järjestelmäpersoonia voi vain nimetä uudelleen, et saa muokata työkaluja tai järjestelmäkehotetta, sen sijaan voit poistaa ne käytöstä ja tehdä kopion" github_helper: name: "GitHub-apulainen" description: "Tekoälybotti, joka on erikoistunut avustamaan GitHubiin liittyvissä tehtävissä ja kysymyksissä" @@ -430,11 +430,11 @@ fi: quota_exceeded: "Olet ylittänyt tämän mallin kiintiön. Odota %{relative_time} ja yritä sitten uudelleen." quota_required: "Sinun on määritettävä tälle mallille saneiden tai käyttökertojen enimmäismäärä" no_query_specified: Kyselyparametri on pakollinen, määritä se. - no_user_for_persona: Määritetyllä persoonalle ei ole siihen liitettyä käyttäjää. - persona_not_found: Määritettyä persoonaa ei ole olemassa. Tarkista parametrit persona_name tai persona_id. + no_user_for_agent: Määritetyllä persoonalle ei ole siihen liitettyä käyttäjää. + agent_not_found: Määritettyä persoonaa ei ole olemassa. Tarkista parametrit agent_name tai agent_id. no_user_specified: Parametri username tai user_unique_id vaaditaan, määritä se. user_not_found: Määritettyä käyttäjää ei ole olemassa. Tarkista username-parametri. - persona_disabled: Määritetty persoona ei ole käytössä. Tarkista parametrit persona_name tai persona_id. + agent_disabled: Määritetty persoona ei ole käytössä. Tarkista parametrit agent_name tai agent_id. no_default_llm: Persoonalla täytyy olla default_llm määritelty. user_not_allowed: Käyttäjä ei saa osallistua ketjuun. prompt_message_length: Viesti %{idx} ylittää 1 000 merkin rajan. diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index f14c97ef..0f80c14b 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -243,10 +243,10 @@ fr: ai_bot: reply_error: "Il semble que notre système ait rencontré un problème inattendu en essayant de répondre.\n\n[details='Détails de l'erreur']\n%{details}\n[/details]" default_pm_prefix: "[MD de robot IA sans titre]" - personas: + agents: default_llm_required: "Le modèle LLM par défaut est requis avant d'activer Chat" - cannot_delete_system_persona: "Les personnages système ne peuvent pas être supprimés, veuillez plutôt les désactiver" - cannot_edit_system_persona: "Les personnages du système peuvent uniquement être renommés, vous ne pouvez pas modifier les outils ou l'invite du système, mais seulement les désactiver et en faire une copie" + cannot_delete_system_agent: "Les personnages système ne peuvent pas être supprimés, veuillez plutôt les désactiver" + cannot_edit_system_agent: "Les personnages du système peuvent uniquement être renommés, vous ne pouvez pas modifier les outils ou l'invite du système, mais seulement les désactiver et en faire une copie" github_helper: name: "Assistant GitHub" description: "Robot IA spécialisé dans l'assistance aux tâches et questions liées à GitHub" @@ -430,11 +430,11 @@ fr: quota_exceeded: "Vous avez dépassé le quota pour ce modèle. Veuillez réessayer dans %{relative_time}." quota_required: "Vous devez spécifier un nombre maximal de jetons ou d'utilisations pour ce modèle" no_query_specified: Le paramètre de requête est obligatoire, veuillez le spécifier. - no_user_for_persona: Le personnage spécifié n'a pas d'utilisateur associé. - persona_not_found: Le personnage spécifié n'existe pas. Vérifiez les paramètres persona_name ou persona_id. + no_user_for_agent: Le personnage spécifié n'a pas d'utilisateur associé. + agent_not_found: Le personnage spécifié n'existe pas. Vérifiez les paramètres agent_name ou agent_id. no_user_specified: Le nom d'utilisateur ou le paramètre user_unique_id est obligatoire. Veuillez le préciser. user_not_found: L'utilisateur spécifié n'existe pas. Vérifiez le paramètre du nom d'utilisateur. - persona_disabled: Le personnage spécifié est désactivé. Vérifiez les paramètres persona_name ou persona_id. + agent_disabled: Le personnage spécifié est désactivé. Vérifiez les paramètres agent_name ou agent_id. no_default_llm: Le personnage doit avoir un default_llm défini. user_not_allowed: L'utilisateur n'est pas autorisé à participer au sujet. prompt_message_length: Le message %{idx} dépasse la limite de 1 000 caractères. diff --git a/config/locales/server.he.yml b/config/locales/server.he.yml index ce1544f8..724f705a 100644 --- a/config/locales/server.he.yml +++ b/config/locales/server.he.yml @@ -80,7 +80,7 @@ he: ai_embeddings_per_post_enabled: יצירת הטמעות בכל פוסט בנפרד ai_summarization_enabled: "הפעלת יכולת הסיכום" ai_summarization_model: "מודל לשימוש לסיכום" - ai_summarization_persona: "דמות לשימוש ליכולת הסיכום" + ai_summarization_agent: "דמות לשימוש ליכולת הסיכום" ai_custom_summarization_allowed_groups: "קבוצות שמורשות להשתמש ליצירת סיכומים חדשים." ai_pm_summarization_allowed_groups: "קבוצות שמורשות ליצור ולצפות בתקצירים בהודעות פרטיות." ai_summary_gists_enabled: "יצירת תקצירים של התגובות האחרונות בנושאים אוטומטית" @@ -102,7 +102,7 @@ he: ai_discord_app_id: "המזהה של יישום ה־Discord שברצונך לחבר אליו את החיפוש ב־Discord" ai_discord_app_public_key: "המפתח הציבורי של יישום ה־Discord שברצונך לחבר אליו את החיפוש ב־Discord" ai_discord_search_mode: "נא לבחור את מצב החיפוש לחיפוש ב־Discord" - ai_discord_search_persona: "הדמות לשימוש בחיפוש Discord." + ai_discord_search_agent: "הדמות לשימוש בחיפוש Discord." ai_discord_allowed_guilds: "גילדות (שרתי) Discord בהן מותר לבוט לחפש" reviewables: reasons: @@ -260,10 +260,10 @@ he: reply_error: "נראה שהמערכת שלך נתקלה בקשיים לא צפויים בעת הניסיון להשיב, עמך הסליחה.\n\n[details='פרטי השגיאה']\n%{details}\n[/details]" default_pm_prefix: "[הודעה פרטית של בינה מלאכותית ללא כותרת]" thinking: "בתהליך חשיבה…" - personas: + agents: default_llm_required: "ברירת מחדל של מודל שפה הגדול נחוצה בטרם הפעלת הצ׳אט" - cannot_delete_system_persona: "אי אפשר למחוק דמויות מערכת, נא להשבית אותן במקום" - cannot_edit_system_persona: "לדמויות מערכת אפשר רק לשנות את השם, אסור לערוך כלים או את בקשת הבסיס של המערכת, במקום יש להשבית ולשבט" + cannot_delete_system_agent: "אי אפשר למחוק דמויות מערכת, נא להשבית אותן במקום" + cannot_edit_system_agent: "לדמויות מערכת אפשר רק לשנות את השם, אסור לערוך כלים או את בקשת הבסיס של המערכת, במקום יש להשבית ולשבט" cannot_have_duplicate_tools: "לא יכולים להיות כלים כפולים" github_helper: name: "מסייע GitHub" @@ -519,11 +519,11 @@ he: quota_exceeded: "חרגת מהמכסה למודל הזה. נא לנסות שוב בעוד %{relative_time}." quota_required: "יש לציין את כמות האסימונים המרבית לשימוש למודל הזה" no_query_specified: משתנה השאילתה נחוץ, נא לציין אותו. - no_user_for_persona: לדמות שצוינה אין משתמש שמשויך אליה. - persona_not_found: הדמות שצוינה לא קיימת. נא לבדוק את המשתנים persona_name או persona_id. + no_user_for_agent: לדמות שצוינה אין משתמש שמשויך אליה. + agent_not_found: הדמות שצוינה לא קיימת. נא לבדוק את המשתנים agent_name או agent_id. no_user_specified: המשתנים username או user_unique_id נחוצים, נא לציין לפחות אחד מהם. user_not_found: המשתמש שצוין לא קיים. נא לבדוק את המשתנה username. - persona_disabled: הדמות שצוינה הושבתה. נא לבדוק את המשתנים persona_name או persona_id. + agent_disabled: הדמות שצוינה הושבתה. נא לבדוק את המשתנים agent_name או agent_id. no_default_llm: לדמות חייב להיות מוגדר default_llm (מודל ברירת מחדל). user_not_allowed: המשתמש לא מורשה להשתתף בנושא. prompt_message_length: ההודעה %{idx} חורגת ממגבלת 1000 התווים. diff --git a/config/locales/server.id.yml b/config/locales/server.id.yml index 4efe05f1..5f09396b 100644 --- a/config/locales/server.id.yml +++ b/config/locales/server.id.yml @@ -52,7 +52,7 @@ id: attribution: "Keterangan oleh AI" ai_bot: default_pm_prefix: "[PM bot AI tanpa judul]" - personas: + agents: default_llm_required: "Model LLM default diperlukan sebelum mengaktifkan Obrolan" tool_options: search: diff --git a/config/locales/server.it.yml b/config/locales/server.it.yml index 72794d14..5d31e369 100644 --- a/config/locales/server.it.yml +++ b/config/locales/server.it.yml @@ -56,7 +56,7 @@ it: composer_ai_helper_allowed_groups: "Gli utenti di questi gruppi vedranno il pulsante dell'assistente IA nella sezione di scrittura." ai_helper_allowed_in_pm: "Abilita l'assistente IA nei MP." ai_helper_model: "Modello da utilizzare per l'assistente IA." - ai_helper_custom_prompts_allowed_groups: "Gli utenti di questi gruppi vedranno l'opzione di comando personalizzato nell'assistente IA." + ai_helper_custom_prompts_allowed_groups: "Gli utenti di questi gruppi vedranno l'opzione di comando agentlizzato nell'assistente IA." ai_helper_automatic_chat_thread_title_delay: "Ritardo in minuti prima che l'assistente IA imposti automaticamente il titolo del thread della chat." ai_helper_automatic_chat_thread_title: "Imposta automaticamente i titoli dei thread della chat in base ai contenuti del thread." ai_helper_illustrate_post_model: "Modello da utilizzare per la funzionalità di messaggio illustrato dell'assistente IA compositore" @@ -65,7 +65,7 @@ it: ai_helper_image_caption_model: "Seleziona il modello da utilizzare per generare le didascalie delle immagini" ai_auto_image_caption_allowed_groups: "Gli utenti di questi gruppi possono attivare/disattivare i sottotitoli automatici delle immagini." ai_embeddings_selected_model: "Usa il modello selezionato per generare le integrazioni." - ai_embeddings_generate_for_pms: "Genera integrazioni per messaggi personali." + ai_embeddings_generate_for_pms: "Genera integrazioni per messaggi agentli." ai_embeddings_semantic_related_topics_enabled: "Usa la ricerca semantica per argomenti correlati." ai_embeddings_semantic_related_topics: "Numero massimo di argomenti da mostrare nella sezione degli argomenti correlati." ai_embeddings_backfill_batch_size: "Numero di incorporamenti da riempire ogni 15 minuti." @@ -84,14 +84,14 @@ it: ai_bot_enable_chat_warning: "Visualizza un avviso quando viene avviata la chat MP. Può essere sovrascritto modificando la stringa di traduzione: discourse_ai.ai_bot.pm_warning" ai_bot_allowed_groups: "Quando il bot GPT ha accesso a un MP, risponderà ai membri di questi gruppi." ai_bot_debugging_allowed_groups: "Consenti a questi gruppi di vedere un pulsante di debug sui post che mostra la richiesta e la risposta IA non elaborate" - ai_bot_public_sharing_allowed_groups: "Consenti a questi gruppi di condividere messaggi personali dell'IA con il pubblico tramite un link univoco disponibile al pubblico. Nota: se il tuo sito richiede l'accesso, anche le condivisioni richiederanno l'accesso." + ai_bot_public_sharing_allowed_groups: "Consenti a questi gruppi di condividere messaggi agentli dell'IA con il pubblico tramite un link univoco disponibile al pubblico. Nota: se il tuo sito richiede l'accesso, anche le condivisioni richiederanno l'accesso." ai_bot_add_to_header: "Mostra un pulsante nell'intestazione per avviare un MP con un Bot IA" ai_bot_github_access_token: "Token di accesso GitHub da utilizzare con gli strumenti GitHub IA (richiesto per il supporto della ricerca)" ai_stability_api_key: "Chiave API per l'API stability.ai" ai_stability_engine: "Sistema di generazione di immagini da utilizzare per l'API stability.ai" ai_stability_api_url: "URL per l'API stability.ai" - ai_google_custom_search_api_key: "Chiave API per l'API della ricerca personalizzata di Google: https://developers.google.com/custom-search" - ai_google_custom_search_cx: "CX per l'API di ricerca personalizzata di Google" + ai_google_custom_search_api_key: "Chiave API per l'API della ricerca agentlizzata di Google: https://developers.google.com/custom-search" + ai_google_custom_search_cx: "CX per l'API di ricerca agentlizzata di Google" reviewables: reasons: flagged_by_toxicity: Il plugin IA lo ha segnalato dopo averlo classificato come tossico. @@ -99,7 +99,7 @@ it: reports: overall_sentiment: title: "Sentimento generale" - description: 'Il grafico confronta il numero di messaggi classificati come positivi o negativi. Questi vengono calcolati quando i punteggi positivi o negativi > il punteggio soglia impostato. Ciò significa che i messaggi neutri non vengono mostrati. Sono esclusi anche i messaggi personali (MP). Classificazione effettuata con "cardiffnlp/twitter-roberta-base-sentiment-latest"' + description: 'Il grafico confronta il numero di messaggi classificati come positivi o negativi. Questi vengono calcolati quando i punteggi positivi o negativi > il punteggio soglia impostato. Ciò significa che i messaggi neutri non vengono mostrati. Sono esclusi anche i messaggi agentli (MP). Classificazione effettuata con "cardiffnlp/twitter-roberta-base-sentiment-latest"' xaxis: "Positiva(%)" yaxis: "Data" emotion_admiration: @@ -193,7 +193,7 @@ it: view_changes: "Visualizza modifiche" unknown_model: "Modello IA sconosciuto" tools: - custom_name: "%{name} (personalizzato)" + custom_name: "%{name} (agentlizzato)" presets: browse_web_jina: name: "Naviga sul web (jina.ai)" @@ -213,7 +213,7 @@ it: generate_titles: Suggerisci i titoli degli argomenti proofread: Testo corretto markdown_table: Genera tabella di markdown - custom_prompt: "Comando personalizzato" + custom_prompt: "Comando agentlizzato" explain: "Spiega" illustrate_post: "Illustra messaggio" replace_dates: "Date smart" @@ -230,8 +230,8 @@ it: title: "%{title} - Conversazione IA - %{site_name}" errors: not_allowed: "Non hai l'autorizzazione a condividere questo argomento" - other_people_in_pm: "I messaggi personali con altri esseri umani non possono essere condivisi pubblicamente" - other_content_in_pm: "I messaggi personali contenenti post di altre persone non possono essere condivisi pubblicamente" + other_people_in_pm: "I messaggi agentli con altri esseri umani non possono essere condivisi pubblicamente" + other_content_in_pm: "I messaggi agentli contenenti post di altre persone non possono essere condivisi pubblicamente" failed_to_share: "Condivisione della conversazione non riuscita" conversation_deleted: "Condivisione di conversazione eliminata correttamente" spam_detection: @@ -243,10 +243,10 @@ it: ai_bot: reply_error: "Spiacenti, il nostro sistema ha riscontrato un problema imprevisto durante il tentativo di risposta.\n\n[details='Dettagli errore']\n%{details}\n[/details]" default_pm_prefix: "[Bot IA senza titolo MP]" - personas: + agents: default_llm_required: "Il modello LLM predefinito è obbligatorio prima di abilitare la chat" - cannot_delete_system_persona: "I personaggi di sistema non possono essere eliminati, vanno invece disattivati" - cannot_edit_system_persona: "I personaggi di sistema possono solo essere rinominati, non è possibile modificare strumenti di sistema, piuttosto disabilitali e crearne una copia" + cannot_delete_system_agent: "I agentggi di sistema non possono essere eliminati, vanno invece disattivati" + cannot_edit_system_agent: "I agentggi di sistema possono solo essere rinominati, non è possibile modificare strumenti di sistema, piuttosto disabilitali e crearne una copia" github_helper: name: "Assistente GitHub" description: "Bot IA specializzato nell'assistenza con attività e domande relative a GitHub" @@ -430,11 +430,11 @@ it: quota_exceeded: "Hai superato la quota per questo modello. Riprova tra %{relative_time}." quota_required: "È necessario specificare il numero massimo di token o utilizzi per questo modello" no_query_specified: Il parametro di query è obbligatorio, specificalo. - no_user_for_persona: Il personaggio specificato non ha alcun utente associato. - persona_not_found: Il personaggio specificato non esiste. Controlla i parametri persona_name o persona_id. + no_user_for_agent: Il agentggio specificato non ha alcun utente associato. + agent_not_found: Il agentggio specificato non esiste. Controlla i parametri agent_name o agent_id. no_user_specified: Il parametro username o user_unique_id è obbligatorio, specificalo. user_not_found: L'utente specificato non esiste. Controlla il parametro username. - persona_disabled: Il personaggio specificato è disattivato. Controlla i parametri persona_name o persona_id. - no_default_llm: Il personaggio deve avere un default_llm definito. + agent_disabled: Il agentggio specificato è disattivato. Controlla i parametri agent_name o agent_id. + no_default_llm: Il agentggio deve avere un default_llm definito. user_not_allowed: All'utente non è consentito partecipare all'argomento. prompt_message_length: Il messaggio %{idx} supera il limite di 1000 caratteri. diff --git a/config/locales/server.ja.yml b/config/locales/server.ja.yml index 24cce090..a4585cff 100644 --- a/config/locales/server.ja.yml +++ b/config/locales/server.ja.yml @@ -241,10 +241,10 @@ ja: ai_bot: reply_error: "返信中に予期しない問題が発生したようです。\n\n\n[details='エラーの詳細']\n%{details}\n[/details]" default_pm_prefix: "[無題の AI ボット PM]" - personas: + agents: default_llm_required: "チャットを有効にする前に、デフォルトの LLM モデルが必要です" - cannot_delete_system_persona: "システムペルソナは削除できません。代わりに無効にしてください" - cannot_edit_system_persona: "システムペルソナは名前変更のみ可能です。ツールまたはシステムプロンプトは編集できません。代わりに無効にするかコピーを作成してください" + cannot_delete_system_agent: "システムペルソナは削除できません。代わりに無効にしてください" + cannot_edit_system_agent: "システムペルソナは名前変更のみ可能です。ツールまたはシステムプロンプトは編集できません。代わりに無効にするかコピーを作成してください" github_helper: name: "GitHub ヘルパー" description: "GitHub 関連タスクと質問の支援を専門とする AI ボット" @@ -419,11 +419,11 @@ ja: quota_exceeded: "このモデルの使用量制限を超過しました。%{relative_time}後にもう一度お試しください。" quota_required: "このモデルのトークンまたは使用量の上限を指定する必要があります" no_query_specified: クエリパラメーターは必須です。指定してください。 - no_user_for_persona: 指定されたペルソナにはユーザーが関連付けられていません。 - persona_not_found: 指定されたペルソナは存在しません。persona_name または persona_id パラメーターを確認してください。 + no_user_for_agent: 指定されたペルソナにはユーザーが関連付けられていません。 + agent_not_found: 指定されたペルソナは存在しません。agent_name または agent_id パラメーターを確認してください。 no_user_specified: username または user_unique_id パラメーターは必須です。指定してください。 user_not_found: 指定されたユーザーは存在しません。username パラメーターを確認してください。 - persona_disabled: 指定されたペルソナは無効化されています。persona_name または persona_id パラメーターを確認してください。 + agent_disabled: 指定されたペルソナは無効化されています。agent_name または agent_id パラメーターを確認してください。 no_default_llm: ペルソナには default_llm が定義されている必要があります。 user_not_allowed: ユーザーはトピックに参加できません。 prompt_message_length: メッセージ %{idx} は 1000 文字の上限を超えています。 diff --git a/config/locales/server.nl.yml b/config/locales/server.nl.yml index a7757034..b5bf58cf 100644 --- a/config/locales/server.nl.yml +++ b/config/locales/server.nl.yml @@ -243,10 +243,10 @@ nl: ai_bot: reply_error: "Het lijkt erop dat ons systeem een onverwacht probleem heeft ondervonden terwijl het probeerde te antwoorden.\n\n[details='Error details']\n%{details}\n[/details]" default_pm_prefix: "[Ongetitelde PB van AI-bot]" - personas: + agents: default_llm_required: "Standaard LLM-model is vereist voordat Chat wordt ingeschakeld" - cannot_delete_system_persona: "Systeempersona's kunnen niet worden verwijderd. Schakel deze in plaats daarvan uit" - cannot_edit_system_persona: "Systeempersona's kunnen alleen worden hernoemd. Je kunt tools of systeemprompts niet bewerken. Je kunt ze wel uitschakelen en een kopie maken" + cannot_delete_system_agent: "Systeemagent's kunnen niet worden verwijderd. Schakel deze in plaats daarvan uit" + cannot_edit_system_agent: "Systeemagent's kunnen alleen worden hernoemd. Je kunt tools of systeemprompts niet bewerken. Je kunt ze wel uitschakelen en een kopie maken" github_helper: name: "GitHub Helper" description: "AI-bot gespecialiseerd in het assisteren bij GitHub-gerelateerde taken en vragen" @@ -430,11 +430,11 @@ nl: quota_exceeded: "Je hebt het quotum voor dit model overschreden. Probeer het opnieuw over %{relative_time}." quota_required: "Je moet het maximale aantal tokens of gebruiken opgeven voor dit model" no_query_specified: De queryparameter is verplicht. Geef deze op. - no_user_for_persona: Er is geen gebruiker gekoppeld aan de opgegeven persona. - persona_not_found: De opgegeven persona bestaat niet. Controleer de parameters persona_name en persona_id. + no_user_for_agent: Er is geen gebruiker gekoppeld aan de opgegeven agent. + agent_not_found: De opgegeven agent bestaat niet. Controleer de parameters agent_name en agent_id. no_user_specified: De gebruikersnaam of de parameter user_unique_id is vereist. Geef deze op. user_not_found: De opgegeven gebruiker bestaat niet. Controleer de gebruikersnaamparameter. - persona_disabled: De opgegeven persona is uitgeschakeld. Controleer de parameters persona_name en persona_id. - no_default_llm: Er moet een default_llm zijn gedefinieerd voor de persona. + agent_disabled: De opgegeven agent is uitgeschakeld. Controleer de parameters agent_name en agent_id. + no_default_llm: Er moet een default_llm zijn gedefinieerd voor de agent. user_not_allowed: De gebruiker mag niet deelnemen aan het topic. prompt_message_length: Het bericht %{idx} is langer dan de limiet van 1000 tekens. diff --git a/config/locales/server.pl_PL.yml b/config/locales/server.pl_PL.yml index 3f4b8495..d48ed09a 100644 --- a/config/locales/server.pl_PL.yml +++ b/config/locales/server.pl_PL.yml @@ -127,8 +127,8 @@ pl_PL: ai_bot: default_pm_prefix: "[PW bota AI bez tytułu]" thinking: "Myślenie..." - personas: - cannot_delete_system_persona: "Person systemowych nie można usunąć, zamiast tego wyłącz je." + agents: + cannot_delete_system_agent: "Person systemowych nie można usunąć, zamiast tego wyłącz je." general: name: Pomocnik forumowy description: "Bot AI ogólnego przeznaczenia zdolny do wykonywania różnych zadań" @@ -276,6 +276,6 @@ pl_PL: quota_exceeded: "Przekroczyłeś limit dla tego modelu. Spróbuj ponownie za %{relative_time}." quota_required: "Musisz określić maksymalną liczbę tokenów lub użyć dla tego modelu" no_query_specified: Parametr zapytania jest wymagany, podaj go. - no_user_for_persona: Określona persona nie ma powiązanego użytkownika. - persona_not_found: Określona persona nie istnieje. Sprawdź parametry persona_name lub persona_id. + no_user_for_agent: Określona agent nie ma powiązanego użytkownika. + agent_not_found: Określona agent nie istnieje. Sprawdź parametry agent_name lub agent_id. prompt_message_length: Wiadomość %{idx} przekracza limit 1000 znaków. diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index b4400448..97817810 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -56,7 +56,7 @@ pt_BR: composer_ai_helper_allowed_groups: "Os usuários desses grupos visualizarão o botão de assistência por IA no compositor." ai_helper_allowed_in_pm: "Ative o ajudante de IA do compositor em MPs." ai_helper_model: "Modelo usado para o ajudante de IA." - ai_helper_custom_prompts_allowed_groups: "Os(as) usuários(as) que estiverem nestes grupos verão um prompt personalizado no ajudante da IA." + ai_helper_custom_prompts_allowed_groups: "Os(as) usuários(as) que estiverem nestes grupos verão um prompt agentlizado no ajudante da IA." ai_helper_automatic_chat_thread_title_delay: "O atraso, em minutos, até que o ajudante de IA defina automaticamente o título da thread do chat." ai_helper_automatic_chat_thread_title: "Defina automaticamente os títulos das linhas de discussão do chat com base no conteúdo delas." ai_helper_illustrate_post_model: "O modelo a ser usado para o ajudante de IA ilustrar o recurso da postagem" @@ -89,8 +89,8 @@ pt_BR: ai_stability_api_key: "Chave de API para a API stability.ai" ai_stability_engine: "Mecanismo de geração de imagem usado para a API stability.ai" ai_stability_api_url: "URL para a API stability.ai" - ai_google_custom_search_api_key: "Chave de API para a API de pesquisa personalizada do Google. Veja: https://developers.google.com/custom-search" - ai_google_custom_search_cx: "CX para API de pesquisa personalizada do Google" + ai_google_custom_search_api_key: "Chave de API para a API de pesquisa agentlizada do Google. Veja: https://developers.google.com/custom-search" + ai_google_custom_search_cx: "CX para API de pesquisa agentlizada do Google" reviewables: reasons: flagged_by_toxicity: O plugin de IA sinalizou isto após classificar como tóxico. @@ -192,7 +192,7 @@ pt_BR: view_changes: "Visualizar alterações" unknown_model: "Modelo de IA desconhecido" tools: - custom_name: "%{name} (personalizado)" + custom_name: "%{name} (agentlizado)" presets: browse_web_jina: name: "Navegar na Web com (jina.ai)" @@ -212,7 +212,7 @@ pt_BR: generate_titles: Sugerir títulos de tópicos proofread: Revisar texto markdown_table: Gerar tabela de Markdown - custom_prompt: "Prompt personalizado" + custom_prompt: "Prompt agentlizado" explain: "Explicar" illustrate_post: "Ilustrar postagem" replace_dates: "Datas inteligentes" @@ -242,10 +242,10 @@ pt_BR: ai_bot: reply_error: "Desculpe, parece que nosso sistema encontrou um problema inesperado ao tentar responder.\n\n[details='Error details']\n%{details}\n[/details]" default_pm_prefix: "[MP de bot de IA não identificado]" - personas: + agents: default_llm_required: "É preciso de um modelo de LLM padrão para ativar o chat" - cannot_delete_system_persona: "Personas de sistema não podem ser excluídas, desative" - cannot_edit_system_persona: "Personas de sistema só podem ser renomeadas, você não pode editar ferramentas ou prompt de sistema. Desative e faça uma cópia" + cannot_delete_system_agent: "Agents de sistema não podem ser excluídas, desative" + cannot_edit_system_agent: "Agents de sistema só podem ser renomeadas, você não pode editar ferramentas ou prompt de sistema. Desative e faça uma cópia" github_helper: name: "Ajudante do GitHub" description: "Bot de IA especializado em auxiliar em tarefas e perguntas relacionadas ao GitHub" @@ -429,11 +429,11 @@ pt_BR: quota_exceeded: "Você ultrapassou a cota deste modelo. Tente novamente em %{relative_time}." quota_required: "Você deve especificar o máximo de tokens ou usos para este modelo" no_query_specified: Requer parâmetro de consulta, especifique um. - no_user_for_persona: A persona especificada não tem usuário(a) associado(a). - persona_not_found: A persona especificada não existe. Confira os parâmetros persona_name ou persona_id + no_user_for_agent: A agent especificada não tem usuário(a) associado(a). + agent_not_found: A agent especificada não existe. Confira os parâmetros agent_name ou agent_id no_user_specified: Requer o parâmetro username ou user_unique_id. Insira-o. user_not_found: O(a) usuário(a) especificado(a) não existe. Confira o parâmetro username. - persona_disabled: A persona especificada está desativada. Confira os parâmetros persona_name ou persona_id - no_default_llm: A persona deve ter default_llm definida. + agent_disabled: A agent especificada está desativada. Confira os parâmetros agent_name ou agent_id + no_default_llm: A agent deve ter default_llm definida. user_not_allowed: O(a) usuário(a) não tem permissão para participar do tópico. prompt_message_length: A mensagem %{idx} excede o limite de 1000 caracteres. diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index 45c4be30..3a293026 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -243,10 +243,10 @@ ru: ai_bot: reply_error: "Извините, похоже, наша система столкнулась с неожиданной проблемой при попытке ответить.\n\n[details='Сведения об ошибке']\n%{details}\n[/details]" default_pm_prefix: "[Личное сообщение от AI-бота без названия]" - personas: + agents: default_llm_required: "Для включения чата требуется модель LLM по умолчанию" - cannot_delete_system_persona: "Системные персоны нельзя удалить, вместо этого отключите их" - cannot_edit_system_persona: "Системные персоны можно только переименовать, вы не можете редактировать инструменты или системный запрос, вместо этого отключите их и создайте копию" + cannot_delete_system_agent: "Системные персоны нельзя удалить, вместо этого отключите их" + cannot_edit_system_agent: "Системные персоны можно только переименовать, вы не можете редактировать инструменты или системный запрос, вместо этого отключите их и создайте копию" github_helper: name: "Помощник по GitHub" description: "AI-бот, специализирующийся на оказании помощи в решении задач и вопросов, связанных с GitHub" @@ -448,11 +448,11 @@ ru: quota_exceeded: "Вы превысили квоту для этой модели. Повторите попытку через %{relative_time}." quota_required: "Необходимо указать максимальное количество токенов или использований для этой модели" no_query_specified: Параметр запроса обязателен, укажите его. - no_user_for_persona: Указанная персона не имеет связанного с ней пользователя. - persona_not_found: Указанная персона не существует. Проверьте параметры persona_name или persona_id. + no_user_for_agent: Указанная персона не имеет связанного с ней пользователя. + agent_not_found: Указанная персона не существует. Проверьте параметры agent_name или agent_id. no_user_specified: Параметры user_unique_id или username обязательны, укажите нужное. user_not_found: Указанный пользователь не существует. Проверьте параметр username. - persona_disabled: Указанная персона отключена. Проверьте параметры persona_name или persona_id. + agent_disabled: Указанная персона отключена. Проверьте параметры agent_name или agent_id. no_default_llm: У персоны должен быть определен параметр default_llm. user_not_allowed: Пользователю не разрешено участвовать в теме. prompt_message_length: Сообщение %{idx} превышает ограничение в 1000 символов. diff --git a/config/locales/server.tr_TR.yml b/config/locales/server.tr_TR.yml index e6c704cd..0becd7d4 100644 --- a/config/locales/server.tr_TR.yml +++ b/config/locales/server.tr_TR.yml @@ -243,10 +243,10 @@ tr_TR: ai_bot: reply_error: "Üzgünüz, sistemimiz yanıtlamaya çalışırken beklenmeyen bir sorunla karşılaşmış gibi görünüyor.\n\n[details='Hata ayrıntıları']\n%{details}\n[/details]" default_pm_prefix: "[Adsız YZ botu kişisel mesajı]" - personas: + agents: default_llm_required: "Sohbeti etkinleştirmeden önce varsayılan LLM modeli gereklidir" - cannot_delete_system_persona: "Sistem kişilikleri silinemez, lütfen bunun yerine devre dışı bırakın" - cannot_edit_system_persona: "Sistem kişileri yalnızca yeniden adlandırılabilir, araçları veya sistem istemini düzenleyemezsiniz, bunun yerine devre dışı bırakıp bir kopyasını oluşturabilirsiniz" + cannot_delete_system_agent: "Sistem kişilikleri silinemez, lütfen bunun yerine devre dışı bırakın" + cannot_edit_system_agent: "Sistem kişileri yalnızca yeniden adlandırılabilir, araçları veya sistem istemini düzenleyemezsiniz, bunun yerine devre dışı bırakıp bir kopyasını oluşturabilirsiniz" github_helper: name: "GitHub Yardımcısı" description: "GitHub ile ilgili görevlere ve sorulara yardımcı olma konusunda uzmanlaşmış YZ Botu" @@ -431,11 +431,11 @@ tr_TR: quota_exceeded: "Bu model için kotayı aştınız. Lütfen %{relative_time} içinde tekrar deneyin." quota_required: "Bu model için maksimum token veya kullanımları belirtmelisiniz" no_query_specified: Sorgu parametresi gerekli, lütfen belirtin. - no_user_for_persona: Belirtilen kişilik ile ilişkilendirilmiş bir kullanıcı yok. - persona_not_found: Belirtilen kişilik mevcut değil. persona_name veya persona_id parametrelerini kontrol edin. + no_user_for_agent: Belirtilen kişilik ile ilişkilendirilmiş bir kullanıcı yok. + agent_not_found: Belirtilen kişilik mevcut değil. agent_name veya agent_id parametrelerini kontrol edin. no_user_specified: Kullanıcı adı veya user_unique_id parametresi gerekli, lütfen belirtin. user_not_found: Belirtilen kullanıcı mevcut değil. Kullanıcı adı parametresini kontrol edin. - persona_disabled: Belirtilen kişilik devre dışı. persona_name veya persona_id parametrelerini kontrol edin. + agent_disabled: Belirtilen kişilik devre dışı. agent_name veya agent_id parametrelerini kontrol edin. no_default_llm: Kişiliğin tanımlanmış bir default_llm'si olmalıdır. user_not_allowed: Kullanıcının konuya katılmasına izin verilmiyor. prompt_message_length: '%{idx} mesajı 1000 karakter limitinin üzerinde.' diff --git a/config/locales/server.uk.yml b/config/locales/server.uk.yml index b23b2477..b2a3eb12 100644 --- a/config/locales/server.uk.yml +++ b/config/locales/server.uk.yml @@ -152,10 +152,10 @@ uk: conversation_deleted: "Спільний доступ до розмови успішно видалено" ai_bot: default_pm_prefix: "[ПП від ШІ-бота без назви]" - personas: + agents: default_llm_required: "Перед увімкненням чату потрібно встановити модель LLM за замовчуванням" - cannot_delete_system_persona: "Системні персони не можуть бути видалені, замість цього, будь ласка, вимкніть їх" - cannot_edit_system_persona: "Системні персони можна лише перейменовувати, ви не можете редагувати інструменти або системні підказки, натомість вимкніть і зробіть копію" + cannot_delete_system_agent: "Системні персони не можуть бути видалені, замість цього, будь ласка, вимкніть їх" + cannot_edit_system_agent: "Системні персони можна лише перейменовувати, ви не можете редагувати інструменти або системні підказки, натомість вимкніть і зробіть копію" github_helper: name: "Помічник GitHub" description: "AI Bot, що спеціалізується на допомозі із завданнями та запитаннями, пов’язаними з GitHub" @@ -341,11 +341,11 @@ uk: missing_provider_param: "%{param} не може бути пустим" errors: no_query_specified: Параметр запиту є обов'язковим, будь ласка, вкажіть його. - no_user_for_persona: Зазначена персона не має пов'язаного з нею користувача. - persona_not_found: Вказана персона не існує. Перевірте параметри persona_name або persona_id. + no_user_for_agent: Зазначена персона не має пов'язаного з нею користувача. + agent_not_found: Вказана персона не існує. Перевірте параметри agent_name або agent_id. no_user_specified: Ім'я користувача або параметр user_unique_id є обов'язковим, будь ласка, вкажіть його. user_not_found: Зазначений користувач не існує. Перевірте параметр імені користувача. - persona_disabled: Вказана особа вимкнена. Перевірте параметри persona_name або persona_id. + agent_disabled: Вказана особа вимкнена. Перевірте параметри agent_name або agent_id. no_default_llm: Особа повинна мати значення default_llm. user_not_allowed: Користувачеві заборонено брати участь у темі. prompt_message_length: Повідомлення %{idx} перевищує ліміт у 1000 символів. diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index 8f71be3b..69595d28 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -241,10 +241,10 @@ zh_CN: ai_bot: reply_error: "抱歉,我们的系统在尝试回复时似乎遇到了意外问题。\n\n[details='Error details']\n%{details}\n[/details]" default_pm_prefix: "[无标题 AI 机器人私信]" - personas: + agents: default_llm_required: "启用聊天之前需要默认的 LLM 模型" - cannot_delete_system_persona: "无法删除系统角色,请改为将其禁用" - cannot_edit_system_persona: "系统角色只能重命名,您不能编辑工具或系统提示,但可以禁用和复制" + cannot_delete_system_agent: "无法删除系统角色,请改为将其禁用" + cannot_edit_system_agent: "系统角色只能重命名,您不能编辑工具或系统提示,但可以禁用和复制" github_helper: name: "GitHub 助手" description: "专门协助处理 GitHub 相关任务和问题的 AI 机器人" @@ -419,11 +419,11 @@ zh_CN: quota_exceeded: "您已超出此模型的配额。请在 %{relative_time}后再试。" quota_required: "必须为此模型指定最大词元数或使用次数" no_query_specified: 查询参数为必填项,请指定。 - no_user_for_persona: 指定的角色没有与之关联的用户。 - persona_not_found: 指定的角色不存在。请检查 persona_name 或 persona_id 参数。 + no_user_for_agent: 指定的角色没有与之关联的用户。 + agent_not_found: 指定的角色不存在。请检查 agent_name 或 agent_id 参数。 no_user_specified: username 或 user_unique_id 参数为必填项,请指定。 user_not_found: 指定的用户不存在。请检查 username 参数。 - persona_disabled: 指定的角色被禁用。请检查 persona_name 或 persona_id 参数。 + agent_disabled: 指定的角色被禁用。请检查 agent_name 或 agent_id 参数。 no_default_llm: 角色必须定义一个 default_llm。 user_not_allowed: 该用户无法参与该话题。 prompt_message_length: 消息 %{idx} 超过 1000 个字符限制。 diff --git a/config/routes.rb b/config/routes.rb index a41c966a..3a034d6d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -63,12 +63,12 @@ Discourse::Application.routes.draw do :constraints => StaffConstraint.new scope "/admin/plugins/discourse-ai", constraints: AdminConstraint.new do - resources :ai_personas, + resources :ai_agents, only: %i[index new create edit update destroy], - path: "ai-personas", - controller: "discourse_ai/admin/ai_personas" + path: "ai-agents", + controller: "discourse_ai/admin/ai_agents" - post "/ai-personas/stream-reply" => "discourse_ai/admin/ai_personas#stream_reply" + post "/ai-agents/stream-reply" => "discourse_ai/admin/ai_agents#stream_reply" resources( :ai_tools, @@ -79,10 +79,10 @@ Discourse::Application.routes.draw do post "/ai-tools/:id/test", to: "discourse_ai/admin/ai_tools#test" - post "/ai-personas/:id/create-user", to: "discourse_ai/admin/ai_personas#create_user" + post "/ai-agents/:id/create-user", to: "discourse_ai/admin/ai_agents#create_user" - put "/ai-personas/:id/files/remove", to: "discourse_ai/admin/ai_personas#remove_file" - get "/ai-personas/:id/files/status", to: "discourse_ai/admin/ai_personas#indexing_status_check" + put "/ai-agents/:id/files/remove", to: "discourse_ai/admin/ai_agents#remove_file" + get "/ai-agents/:id/files/status", to: "discourse_ai/admin/ai_agents#indexing_status_check" post "/rag-document-fragments/files/upload", to: "discourse_ai/admin/rag_document_fragments#upload_file" diff --git a/config/settings.yml b/config/settings.yml index 0dde98de..e8af583d 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -243,10 +243,10 @@ discourse_ai: enum: "DiscourseAi::Configuration::LlmEnumerator" validator: "DiscourseAi::Configuration::LlmValidator" hidden: true - ai_summarization_persona: + ai_summarization_agent: default: "-11" type: enum - enum: "DiscourseAi::Configuration::PersonaEnumerator" + enum: "DiscourseAi::Configuration::AgentEnumerator" area: "ai-features/summarization" ai_pm_summarization_allowed_groups: type: group_list @@ -261,10 +261,10 @@ discourse_ai: ai_summary_gists_enabled: default: false area: "ai-features/gists" - ai_summary_gists_persona: + ai_summary_gists_agent: default: "-12" type: enum - enum: "DiscourseAi::Configuration::PersonaEnumerator" + enum: "DiscourseAi::Configuration::AgentEnumerator" area: "ai-features/gists" ai_summary_gists_allowed_groups: # Deprecated. TODO(roman): Remove 2025-09-01 type: group_list @@ -324,11 +324,11 @@ discourse_ai: hidden: true type: list list_type: compact - ai_bot_discover_persona: + ai_bot_discover_agent: default: "" type: enum client: true - enum: "DiscourseAi::Configuration::PersonaEnumerator" + enum: "DiscourseAi::Configuration::AgentEnumerator" area: "ai-features/discoveries" ai_automation_max_triage_per_minute: default: 60 @@ -359,12 +359,12 @@ discourse_ai: type: enum choices: - search - - persona + - agent area: "ai-features/discord_search" - ai_discord_search_persona: + ai_discord_search_agent: default: "" type: enum - enum: "DiscourseAi::Configuration::PersonaEnumerator" + enum: "DiscourseAi::Configuration::AgentEnumerator" area: "ai-features/discord_search" ai_discord_allowed_guilds: type: list diff --git a/db/migrate/20250529151301_copy_persona_tables_to_agent.rb b/db/migrate/20250529151301_copy_persona_tables_to_agent.rb new file mode 100644 index 00000000..dd796a4f --- /dev/null +++ b/db/migrate/20250529151301_copy_persona_tables_to_agent.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +class CopyPersonaTablesToAgent < ActiveRecord::Migration[7.0] + def up + # Copy the main table structure and data + if table_exists?(:ai_personas) && !table_exists?(:ai_agents) + execute <<~SQL + CREATE TABLE ai_agents AS + SELECT * FROM ai_personas + SQL + + # Copy indexes from ai_personas to ai_agents + execute <<~SQL + CREATE UNIQUE INDEX index_ai_agents_on_id + ON ai_agents USING btree (id) + SQL + + # Copy any other indexes that exist on ai_personas + indexes = execute(<<~SQL).to_a + SELECT indexname, indexdef + FROM pg_indexes + WHERE tablename = 'ai_personas' + AND indexname != 'ai_personas_pkey' + SQL + + indexes.each do |index| + new_index_def = index['indexdef'].gsub('ai_personas', 'ai_agents') + new_index_name = index['indexname'].gsub('ai_personas', 'ai_agents') + new_index_def = new_index_def.gsub(index['indexname'], new_index_name) + execute(new_index_def) + end + end + + # Update polymorphic associations to point to new table + execute <<~SQL + UPDATE rag_document_fragments + SET target_type = 'AiAgent' + WHERE target_type = 'AiPersona' + SQL + + execute <<~SQL + UPDATE upload_references + SET target_type = 'AiAgent' + WHERE target_type = 'AiPersona' + SQL + end + + def down + drop_table :ai_agents if table_exists?(:ai_agents) + + # Revert polymorphic associations + execute <<~SQL + UPDATE rag_document_fragments + SET target_type = 'AiPersona' + WHERE target_type = 'AiAgent' + SQL + + execute <<~SQL + UPDATE upload_references + SET target_type = 'AiPersona' + WHERE target_type = 'AiAgent' + SQL + end +end diff --git a/db/post_migrate/20250529151302_drop_persona_tables.rb b/db/post_migrate/20250529151302_drop_persona_tables.rb new file mode 100644 index 00000000..2f5f56a3 --- /dev/null +++ b/db/post_migrate/20250529151302_drop_persona_tables.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class DropPersonaTables < ActiveRecord::Migration[7.0] + def up + # Drop the old table after copying to new one + drop_table :ai_personas if table_exists?(:ai_personas) + end + + def down + raise IrreversibleMigration, "Cannot recreate dropped persona tables" + end +end diff --git a/discourse_automation/llm_persona_triage.rb b/discourse_automation/llm_agent_triage.rb similarity index 71% rename from discourse_automation/llm_persona_triage.rb rename to discourse_automation/llm_agent_triage.rb index cbe6c1ae..584c2889 100644 --- a/discourse_automation/llm_persona_triage.rb +++ b/discourse_automation/llm_agent_triage.rb @@ -1,17 +1,17 @@ # frozen_string_literal: true if defined?(DiscourseAutomation) - DiscourseAutomation::Scriptable.add("llm_persona_triage") do + DiscourseAutomation::Scriptable.add("llm_agent_triage") do version 1 run_in_background triggerables %i[post_created_edited] - field :persona, + field :agent, component: :choices, required: true, extra: { - content: DiscourseAi::Automation.available_persona_choices, + content: DiscourseAi::Automation.available_agent_choices, } field :whisper, component: :boolean field :silent_mode, component: :boolean @@ -20,28 +20,28 @@ if defined?(DiscourseAutomation) post = context["post"] next if post&.user&.bot? - persona_id = fields.dig("persona", "value") + agent_id = fields.dig("agent", "value") whisper = !!fields.dig("whisper", "value") silent_mode = !!fields.dig("silent_mode", "value") begin RateLimiter.new( Discourse.system_user, - "llm_persona_triage_#{post.id}", + "llm_agent_triage_#{post.id}", SiteSetting.ai_automation_max_triage_per_post_per_minute, 1.minute, ).performed! RateLimiter.new( Discourse.system_user, - "llm_persona_triage", + "llm_agent_triage", SiteSetting.ai_automation_max_triage_per_minute, 1.minute, ).performed! - DiscourseAi::Automation::LlmPersonaTriage.handle( + DiscourseAi::Automation::LlmAgentTriage.handle( post: post, - persona_id: persona_id, + agent_id: agent_id, whisper: whisper, automation: self.automation, silent_mode: silent_mode, @@ -49,7 +49,7 @@ if defined?(DiscourseAutomation) rescue => e Discourse.warn_exception( e, - message: "llm_persona_triage: skipped triage on post #{post.id}", + message: "llm_agent_triage: skipped triage on post #{post.id}", ) raise e if Rails.env.tests? end diff --git a/discourse_automation/llm_triage.rb b/discourse_automation/llm_triage.rb index 0bfc46e6..144496e6 100644 --- a/discourse_automation/llm_triage.rb +++ b/discourse_automation/llm_triage.rb @@ -10,7 +10,7 @@ if defined?(DiscourseAutomation) triggerables %i[post_created_edited] # TODO move to triggerables - field :include_personal_messages, component: :boolean + field :include_agentl_messages, component: :boolean # Inputs field :model, @@ -39,11 +39,11 @@ if defined?(DiscourseAutomation) default: "review" field :canned_reply_user, component: :user field :canned_reply, component: :message - field :reply_persona, + field :reply_agent, component: :choices, extra: { content: - DiscourseAi::Automation.available_persona_choices( + DiscourseAi::Automation.available_agent_choices( require_user: false, require_default_llm: true, ), @@ -55,13 +55,13 @@ if defined?(DiscourseAutomation) next if post&.user&.bot? if post.topic.private_message? - include_personal_messages = fields.dig("include_personal_messages", "value") - next if !include_personal_messages + include_agentl_messages = fields.dig("include_agentl_messages", "value") + next if !include_agentl_messages end canned_reply = fields.dig("canned_reply", "value") canned_reply_user = fields.dig("canned_reply_user", "value") - reply_persona_id = fields.dig("reply_persona", "value") + reply_agent_id = fields.dig("reply_agent", "value") whisper = fields.dig("whisper", "value") # nothing to do if we already replied @@ -113,7 +113,7 @@ if defined?(DiscourseAutomation) tags: tags, canned_reply: canned_reply, canned_reply_user: canned_reply_user, - reply_persona_id: reply_persona_id, + reply_agent_id: reply_agent_id, whisper: whisper, hide_topic: hide_topic, flag_post: flag_post, diff --git a/lib/personas/artifact_update_strategies/base.rb b/lib/agents/artifact_update_strategies/base.rb similarity index 98% rename from lib/personas/artifact_update_strategies/base.rb rename to lib/agents/artifact_update_strategies/base.rb index 624830f1..404d0ec3 100644 --- a/lib/personas/artifact_update_strategies/base.rb +++ b/lib/agents/artifact_update_strategies/base.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module ArtifactUpdateStrategies class InvalidFormatError < StandardError end diff --git a/lib/personas/artifact_update_strategies/diff.rb b/lib/agents/artifact_update_strategies/diff.rb similarity index 99% rename from lib/personas/artifact_update_strategies/diff.rb rename to lib/agents/artifact_update_strategies/diff.rb index af96c0fb..d8ba29df 100644 --- a/lib/personas/artifact_update_strategies/diff.rb +++ b/lib/agents/artifact_update_strategies/diff.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module ArtifactUpdateStrategies class Diff < Base attr_reader :failed_searches diff --git a/lib/personas/artifact_update_strategies/full.rb b/lib/agents/artifact_update_strategies/full.rb similarity index 99% rename from lib/personas/artifact_update_strategies/full.rb rename to lib/agents/artifact_update_strategies/full.rb index 3942cdf8..85fc4c86 100644 --- a/lib/personas/artifact_update_strategies/full.rb +++ b/lib/agents/artifact_update_strategies/full.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module ArtifactUpdateStrategies class Full < Base private diff --git a/lib/personas/artist.rb b/lib/agents/artist.rb similarity index 96% rename from lib/personas/artist.rb rename to lib/agents/artist.rb index 93e2361e..8741e9fe 100644 --- a/lib/personas/artist.rb +++ b/lib/agents/artist.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class Artist < Persona + module Agents + class Artist < Agent def tools [Tools::Image] end diff --git a/lib/personas/bot.rb b/lib/agents/bot.rb similarity index 90% rename from lib/personas/bot.rb rename to lib/agents/bot.rb index b6e852c5..91e9231d 100644 --- a/lib/personas/bot.rb +++ b/lib/agents/bot.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents class Bot attr_reader :model @@ -13,19 +13,19 @@ module DiscourseAi # limit is arbitrary, but 5 which was used in the past was too low MAX_TOOLS = 20 - def self.as(bot_user, persona: DiscourseAi::Personas::General.new, model: nil) - new(bot_user, persona, model) + def self.as(bot_user, agent: DiscourseAi::Agents::General.new, model: nil) + new(bot_user, agent, model) end - def initialize(bot_user, persona, model = nil) + def initialize(bot_user, agent, model = nil) @bot_user = bot_user - @persona = persona + @agent = agent @model = - model || self.class.guess_model(bot_user) || LlmModel.find(@persona.class.default_llm_id) + model || self.class.guess_model(bot_user) || LlmModel.find(@agent.class.default_llm_id) end attr_reader :bot_user - attr_accessor :persona + attr_accessor :agent def llm DiscourseAi::Completions::Llm.proxy(model) @@ -35,12 +35,12 @@ module DiscourseAi return if prompt.tool_choice == :none context.chosen_tools ||= [] - forced_tools = persona.force_tool_use.map { |tool| tool.name } + forced_tools = agent.force_tool_use.map { |tool| tool.name } force_tool = forced_tools.find { |name| !context.chosen_tools.include?(name) } - if force_tool && persona.forced_tool_count > 0 + if force_tool && agent.forced_tool_count > 0 user_turns = prompt.messages.select { |m| m[:type] == :user }.length - force_tool = false if user_turns > persona.forced_tool_count + force_tool = false if user_turns > agent.forced_tool_count end if force_tool @@ -57,7 +57,7 @@ module DiscourseAi end context.cancel_manager ||= DiscourseAi::Completions::CancelManager.new current_llm = llm - prompt = persona.craft_prompt(context, llm: current_llm) + prompt = agent.craft_prompt(context, llm: current_llm) total_completions = 0 ongoing_chain = true @@ -67,11 +67,11 @@ module DiscourseAi llm_kwargs = llm_args.dup llm_kwargs[:user] = user - llm_kwargs[:temperature] = persona.temperature if persona.temperature - llm_kwargs[:top_p] = persona.top_p if persona.top_p + llm_kwargs[:temperature] = agent.temperature if agent.temperature + llm_kwargs[:top_p] = agent.top_p if agent.top_p llm_kwargs[:response_format] = build_json_schema( - persona.response_format, - ) if persona.response_format.present? + agent.response_format, + ) if agent.response_format.present? needs_newlines = false tools_ran = 0 @@ -82,7 +82,7 @@ module DiscourseAi tool_halted = false - allow_partial_tool_calls = persona.allow_partial_tool_calls? + allow_partial_tool_calls = agent.allow_partial_tool_calls? existing_tools = Set.new current_thinking = [] @@ -96,7 +96,7 @@ module DiscourseAi **llm_kwargs, ) do |partial| tool = - persona.find_tool( + agent.find_tool( partial, bot_user: user, llm: current_llm, @@ -183,7 +183,7 @@ module DiscourseAi end def returns_json? - persona.response_format.present? + agent.response_format.present? end private @@ -285,7 +285,7 @@ module DiscourseAi def self.guess_model(bot_user) associated_llm = LlmModel.find_by(user_id: bot_user.id) - return if associated_llm.nil? # Might be a persona user. Handled by constructor. + return if associated_llm.nil? # Might be a agent user. Handled by constructor. associated_llm end diff --git a/lib/personas/bot_context.rb b/lib/agents/bot_context.rb similarity index 99% rename from lib/personas/bot_context.rb rename to lib/agents/bot_context.rb index 69d86669..18bd4a5f 100644 --- a/lib/personas/bot_context.rb +++ b/lib/agents/bot_context.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents class BotContext attr_accessor :messages, :topic_id, diff --git a/lib/personas/creative.rb b/lib/agents/creative.rb similarity index 81% rename from lib/personas/creative.rb rename to lib/agents/creative.rb index 407c2240..30a54015 100644 --- a/lib/personas/creative.rb +++ b/lib/agents/creative.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class Creative < Persona + module Agents + class Creative < Agent def tools [] end diff --git a/lib/personas/dall_e_3.rb b/lib/agents/dall_e_3.rb similarity index 96% rename from lib/personas/dall_e_3.rb rename to lib/agents/dall_e_3.rb index 851756c8..85a907d0 100644 --- a/lib/personas/dall_e_3.rb +++ b/lib/agents/dall_e_3.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class DallE3 < Persona + module Agents + class DallE3 < Agent def tools [Tools::DallE] end diff --git a/lib/personas/designer.rb b/lib/agents/designer.rb similarity index 94% rename from lib/personas/designer.rb rename to lib/agents/designer.rb index f2aa8dea..042de678 100644 --- a/lib/personas/designer.rb +++ b/lib/agents/designer.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class Designer < Persona + module Agents + class Designer < Agent def tools [Tools::CreateImage, Tools::EditImage] end diff --git a/lib/personas/discourse_helper.rb b/lib/agents/discourse_helper.rb similarity index 97% rename from lib/personas/discourse_helper.rb rename to lib/agents/discourse_helper.rb index 2e9db142..246b1c3c 100644 --- a/lib/personas/discourse_helper.rb +++ b/lib/agents/discourse_helper.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class DiscourseHelper < Persona + module Agents + class DiscourseHelper < Agent def tools [Tools::DiscourseMetaSearch] end diff --git a/lib/personas/forum_researcher.rb b/lib/agents/forum_researcher.rb similarity index 97% rename from lib/personas/forum_researcher.rb rename to lib/agents/forum_researcher.rb index a94d9e46..fde18043 100644 --- a/lib/personas/forum_researcher.rb +++ b/lib/agents/forum_researcher.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class ForumResearcher < Persona + module Agents + class ForumResearcher < Agent def self.default_enabled false end diff --git a/lib/personas/general.rb b/lib/agents/general.rb similarity index 94% rename from lib/personas/general.rb rename to lib/agents/general.rb index 01c05e08..6d7840c3 100644 --- a/lib/personas/general.rb +++ b/lib/agents/general.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class General < Persona + module Agents + class General < Agent def tools [ Tools::Search, diff --git a/lib/personas/github_helper.rb b/lib/agents/github_helper.rb similarity index 94% rename from lib/personas/github_helper.rb rename to lib/agents/github_helper.rb index bd75624d..e2f53455 100644 --- a/lib/personas/github_helper.rb +++ b/lib/agents/github_helper.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true module DiscourseAi - module Personas - class GithubHelper < Persona + module Agents + class GithubHelper < Agent def tools [ Tools::GithubFileContent, diff --git a/lib/personas/persona.rb b/lib/agents/persona.rb similarity index 90% rename from lib/personas/persona.rb rename to lib/agents/persona.rb index 62426f77..8d4433f2 100644 --- a/lib/personas/persona.rb +++ b/lib/agents/persona.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class Persona + module Agents + class Agent class << self def default_enabled true @@ -36,8 +36,8 @@ module DiscourseAi false end - def system_personas - @system_personas ||= { + def system_agents + @system_agents ||= { General => -1, SqlHelper => -2, Artist => -3, @@ -55,17 +55,17 @@ module DiscourseAi } end - def system_personas_by_id - @system_personas_by_id ||= system_personas.invert + def system_agents_by_id + @system_agents_by_id ||= system_agents.invert end def all(user:) # listing tools has to be dynamic cause site settings may change - AiPersona.all_personas.filter do |persona| - next false if !user.in_any_groups?(persona.allowed_group_ids) + AiAgent.all_agents.filter do |agent| + next false if !user.in_any_groups?(agent.allowed_group_ids) - if persona.system - instance = persona.new + if agent.system + instance = agent.new ( instance.required_tools == [] || (instance.required_tools - all_available_tools).empty? @@ -77,15 +77,15 @@ module DiscourseAi end def find_by(id: nil, name: nil, user:) - all(user: user).find { |persona| persona.id == id || persona.name == name } + all(user: user).find { |agent| agent.id == id || agent.name == name } end def name - I18n.t("discourse_ai.ai_bot.personas.#{to_s.demodulize.underscore}.name") + I18n.t("discourse_ai.ai_bot.agents.#{to_s.demodulize.underscore}.name") end def description - I18n.t("discourse_ai.ai_bot.personas.#{to_s.demodulize.underscore}.description") + I18n.t("discourse_ai.ai_bot.agents.#{to_s.demodulize.underscore}.description") end def all_available_tools @@ -134,8 +134,8 @@ module DiscourseAi end def id - @ai_persona&.id || self.class.system_personas[self.class.superclass] || - self.class.system_personas[self.class] + @ai_agent&.id || self.class.system_agents[self.class.superclass] || + self.class.system_agents[self.class] end def tools @@ -234,7 +234,7 @@ module DiscourseAi prompt.max_pixels = self.class.vision_max_pixels if self.class.vision_enabled prompt.tools = available_tools.map(&:signature) if available_tools available_tools.each do |tool| - tool.inject_prompt(prompt: prompt, context: context, persona: self) + tool.inject_prompt(prompt: prompt, context: context, agent: self) end prompt end @@ -307,7 +307,7 @@ module DiscourseAi tool_klass.new( arguments, tool_call_id: function_id || function_name, - persona_options: options[tool_klass].to_h, + agent_options: options[tool_klass].to_h, bot_user: bot_user, llm: llm, context: context, @@ -331,7 +331,7 @@ module DiscourseAi def rag_fragments_prompt(conversation_context, llm:, user:) upload_refs = - UploadReference.where(target_id: id, target_type: "AiPersona").pluck(:upload_id) + UploadReference.where(target_id: id, target_type: "AiAgent").pluck(:upload_id) return nil if !DiscourseAi::Embeddings.enabled? return nil if conversation_context.blank? || upload_refs.blank? @@ -346,7 +346,7 @@ module DiscourseAi consolidated_question = latest_interactions[0][:content] else consolidated_question = - DiscourseAi::Personas::QuestionConsolidator.consolidate_question( + DiscourseAi::Agents::QuestionConsolidator.consolidate_question( llm, latest_interactions, user, @@ -376,7 +376,7 @@ module DiscourseAi interactions_vector, limit: search_limit, offset: 0, - ) { |builder| builder.join(<<~SQL, target_id: id, target_type: "AiPersona") } + ) { |builder| builder.join(<<~SQL, target_id: id, target_type: "AiAgent") } rag_document_fragments ON rag_document_fragments.id = rag_document_fragment_id AND rag_document_fragments.target_id = :target_id AND diff --git a/lib/personas/question_consolidator.rb b/lib/agents/question_consolidator.rb similarity index 99% rename from lib/personas/question_consolidator.rb rename to lib/agents/question_consolidator.rb index f1e0c476..a2ee95fd 100644 --- a/lib/personas/question_consolidator.rb +++ b/lib/agents/question_consolidator.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents class QuestionConsolidator attr_reader :llm, :messages, :user, :max_tokens diff --git a/lib/personas/researcher.rb b/lib/agents/researcher.rb similarity index 97% rename from lib/personas/researcher.rb rename to lib/agents/researcher.rb index 6650836b..115e4ae4 100644 --- a/lib/personas/researcher.rb +++ b/lib/agents/researcher.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class Researcher < Persona + module Agents + class Researcher < Agent def tools [Tools::Google, Tools::WebBrowser] end diff --git a/lib/personas/settings_explorer.rb b/lib/agents/settings_explorer.rb similarity index 92% rename from lib/personas/settings_explorer.rb rename to lib/agents/settings_explorer.rb index 77ae45e3..80d25ac5 100644 --- a/lib/personas/settings_explorer.rb +++ b/lib/agents/settings_explorer.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class SettingsExplorer < Persona + module Agents + class SettingsExplorer < Agent def tools [Tools::SettingContext, Tools::SearchSettings] end diff --git a/lib/personas/short_summarizer.rb b/lib/agents/short_summarizer.rb similarity index 96% rename from lib/personas/short_summarizer.rb rename to lib/agents/short_summarizer.rb index 26af56b9..56a4d415 100644 --- a/lib/personas/short_summarizer.rb +++ b/lib/agents/short_summarizer.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true module DiscourseAi - module Personas - class ShortSummarizer < Persona + module Agents + class ShortSummarizer < Agent def self.default_enabled false end diff --git a/lib/personas/sql_helper.rb b/lib/agents/sql_helper.rb similarity index 95% rename from lib/personas/sql_helper.rb rename to lib/agents/sql_helper.rb index 720bc145..bb8eef1f 100644 --- a/lib/personas/sql_helper.rb +++ b/lib/agents/sql_helper.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class SqlHelper < Persona + module Agents + class SqlHelper < Agent def self.schema return @schema if defined?(@schema) @@ -74,7 +74,7 @@ module DiscourseAi ``` The user_actions tables stores likes (action_type 1). - The topics table stores private/personal messages it uses archetype private_message for them. + The topics table stores private/agentl messages it uses archetype private_message for them. notification_level can be: {muted: 0, regular: 1, tracking: 2, watching: 3, watching_first_post: 4}. bookmarkable_type can be: Post,Topic,ChatMessage and more diff --git a/lib/personas/summarizer.rb b/lib/agents/summarizer.rb similarity index 97% rename from lib/personas/summarizer.rb rename to lib/agents/summarizer.rb index fe5d496b..6d42bd5b 100644 --- a/lib/personas/summarizer.rb +++ b/lib/agents/summarizer.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true module DiscourseAi - module Personas - class Summarizer < Persona + module Agents + class Summarizer < Agent def self.default_enabled false end diff --git a/lib/personas/tool_runner.rb b/lib/agents/tool_runner.rb similarity index 89% rename from lib/personas/tool_runner.rb rename to lib/agents/tool_runner.rb index 6ce68476..2e1cd32e 100644 --- a/lib/personas/tool_runner.rb +++ b/lib/agents/tool_runner.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents class ToolRunner attr_reader :tool, :parameters, :llm attr_accessor :running_attached_function, :timeout, :custom_raw @@ -14,11 +14,11 @@ module DiscourseAi MAX_HTTP_REQUESTS = 20 def initialize(parameters:, llm:, bot_user:, context: nil, tool:, timeout: nil) - if context && !context.is_a?(DiscourseAi::Personas::BotContext) + if context && !context.is_a?(DiscourseAi::Agents::BotContext) raise ArgumentError, "context must be a BotContext object" end - context ||= DiscourseAi::Personas::BotContext.new + context ||= DiscourseAi::Agents::BotContext.new @parameters = parameters @llm = llm @@ -82,8 +82,8 @@ module DiscourseAi search: function(params) { return _discourse_search(params); }, - updatePersona: function(persona_id_or_name, updates) { - const result = _discourse_update_persona(persona_id_or_name, updates); + updateAgent: function(agent_id_or_name, updates) { + const result = _discourse_update_agent(agent_id_or_name, updates); if (result.error) { throw new Error(result.error); } @@ -92,29 +92,29 @@ module DiscourseAi getPost: _discourse_get_post, getTopic: _discourse_get_topic, getUser: _discourse_get_user, - getPersona: function(name) { - const personaDetails = _discourse_get_persona(name); - if (personaDetails.error) { - throw new Error(personaDetails.error); + getAgent: function(name) { + const agentDetails = _discourse_get_agent(name); + if (agentDetails.error) { + throw new Error(agentDetails.error); } - // merge result.persona with {}.. + // merge result.agent with {}.. return Object.assign({ update: function(updates) { - const result = _discourse_update_persona(name, updates); + const result = _discourse_update_agent(name, updates); if (result.error) { throw new Error(result.error); } return result; }, respondTo: function(params) { - const result = _discourse_respond_to_persona(name, params); + const result = _discourse_respond_to_agent(name, params); if (result.error) { throw new Error(result.error); } return result; } - }, personaDetails.persona); + }, agentDetails.agent); }, createChatMessage: function(params) { const result = _discourse_create_chat_message(params); @@ -365,15 +365,15 @@ module DiscourseAi ) mini_racer_context.attach( - "_discourse_respond_to_persona", - ->(persona_name, params) do + "_discourse_respond_to_agent", + ->(agent_name, params) do in_attached_function do - # if we have 1000s of personas this can be slow ... we may need to optimize - persona_class = AiPersona.all_personas.find { |persona| persona.name == persona_name } - return { error: "Persona not found" } if persona_class.nil? + # if we have 1000s of agents this can be slow ... we may need to optimize + agent_class = AiAgent.all_agents.find { |agent| agent.name == agent_name } + return { error: "Agent not found" } if agent_class.nil? - persona = persona_class.new - bot = DiscourseAi::Personas::Bot.as(@bot_user || persona.user, persona: persona) + agent = agent_class.new + bot = DiscourseAi::Agents::Bot.as(@bot_user || agent.user, agent: agent) playground = DiscourseAi::AiBot::Playground.new(bot) if @context.post_id @@ -479,17 +479,17 @@ module DiscourseAi ) mini_racer_context.attach( - "_discourse_get_persona", - ->(persona_name) do + "_discourse_get_agent", + ->(agent_name) do in_attached_function do - persona = AiPersona.find_by(name: persona_name) + agent = AiAgent.find_by(name: agent_name) - return { error: "Persona not found" } if persona.nil? + return { error: "Agent not found" } if agent.nil? - # Return a subset of relevant persona attributes + # Return a subset of relevant agent attributes { - persona: - persona.attributes.slice( + agent: + agent.attributes.slice( "id", "name", "description", @@ -503,7 +503,7 @@ module DiscourseAi "allow_chat_channel_mentions", "allow_chat_direct_messages", "allow_topic_mentions", - "allow_personal_messages", + "allow_agentl_messages", ), } end @@ -511,19 +511,19 @@ module DiscourseAi ) mini_racer_context.attach( - "_discourse_update_persona", - ->(persona_id_or_name, updates) do + "_discourse_update_agent", + ->(agent_id_or_name, updates) do in_attached_function do - # Find persona by ID or name - persona = nil - if persona_id_or_name.is_a?(Integer) || - persona_id_or_name.to_i.to_s == persona_id_or_name - persona = AiPersona.find_by(id: persona_id_or_name.to_i) + # Find agent by ID or name + agent = nil + if agent_id_or_name.is_a?(Integer) || + agent_id_or_name.to_i.to_s == agent_id_or_name + agent = AiAgent.find_by(id: agent_id_or_name.to_i) else - persona = AiPersona.find_by(name: persona_id_or_name) + agent = AiAgent.find_by(name: agent_id_or_name) end - return { error: "Persona not found" } if persona.nil? + return { error: "Agent not found" } if agent.nil? allowed_updates = {} @@ -545,12 +545,12 @@ module DiscourseAi TrueClass, ) || updates["enabled"].is_a?(FalseClass) - if persona.update(allowed_updates) + if agent.update(allowed_updates) return( { success: true, - persona: - persona.attributes.slice( + agent: + agent.attributes.slice( "id", "name", "description", @@ -562,7 +562,7 @@ module DiscourseAi } ) else - return { error: persona.errors.full_messages.join(", ") } + return { error: agent.errors.full_messages.join(", ") } end end end, @@ -612,7 +612,7 @@ module DiscourseAi headers = (options && options["headers"]) || {} result = {} - DiscourseAi::Personas::Tools::Tool.send_http_request( + DiscourseAi::Agents::Tools::Tool.send_http_request( url, headers: headers, ) do |response| @@ -641,7 +641,7 @@ module DiscourseAi body = options && options["body"] result = {} - DiscourseAi::Personas::Tools::Tool.send_http_request( + DiscourseAi::Agents::Tools::Tool.send_http_request( url, method: method, headers: headers, diff --git a/lib/personas/tools/create_artifact.rb b/lib/agents/tools/create_artifact.rb similarity index 99% rename from lib/personas/tools/create_artifact.rb rename to lib/agents/tools/create_artifact.rb index 548fb9aa..54d44420 100644 --- a/lib/personas/tools/create_artifact.rb +++ b/lib/agents/tools/create_artifact.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class CreateArtifact < Tool def self.name diff --git a/lib/personas/tools/create_image.rb b/lib/agents/tools/create_image.rb similarity index 99% rename from lib/personas/tools/create_image.rb rename to lib/agents/tools/create_image.rb index 8e2971fa..8d76dd37 100644 --- a/lib/personas/tools/create_image.rb +++ b/lib/agents/tools/create_image.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class CreateImage < Tool def self.signature diff --git a/lib/personas/tools/custom.rb b/lib/agents/tools/custom.rb similarity index 95% rename from lib/personas/tools/custom.rb rename to lib/agents/tools/custom.rb index 29dbb12d..b1d322f5 100644 --- a/lib/personas/tools/custom.rb +++ b/lib/agents/tools/custom.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class Custom < Tool def self.class_instance(tool_id) @@ -33,7 +33,7 @@ module DiscourseAi end def self.has_custom_context? - # note on safety, this can be cached safely, we bump the whole persona cache when an ai tool is saved + # note on safety, this can be cached safely, we bump the whole agent cache when an ai tool is saved # which will expire this class return @has_custom_context if defined?(@has_custom_context) @@ -47,7 +47,7 @@ module DiscourseAi @has_custom_context end - def self.inject_prompt(prompt:, context:, persona:) + def self.inject_prompt(prompt:, context:, agent:) if has_custom_context? ai_tool = AiTool.find_by(id: tool_id) if ai_tool diff --git a/lib/personas/tools/dall_e.rb b/lib/agents/tools/dall_e.rb similarity index 99% rename from lib/personas/tools/dall_e.rb rename to lib/agents/tools/dall_e.rb index 1daa7ee1..030ceec9 100644 --- a/lib/personas/tools/dall_e.rb +++ b/lib/agents/tools/dall_e.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class DallE < Tool def self.signature diff --git a/lib/personas/tools/db_schema.rb b/lib/agents/tools/db_schema.rb similarity index 98% rename from lib/personas/tools/db_schema.rb rename to lib/agents/tools/db_schema.rb index b0a6f296..30ee4919 100644 --- a/lib/personas/tools/db_schema.rb +++ b/lib/agents/tools/db_schema.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class DbSchema < Tool def self.signature diff --git a/lib/personas/tools/discourse_meta_search.rb b/lib/agents/tools/discourse_meta_search.rb similarity index 99% rename from lib/personas/tools/discourse_meta_search.rb rename to lib/agents/tools/discourse_meta_search.rb index 5fdfb76e..80b7c6b8 100644 --- a/lib/personas/tools/discourse_meta_search.rb +++ b/lib/agents/tools/discourse_meta_search.rb @@ -1,7 +1,7 @@ #frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class DiscourseMetaSearch < Tool class << self diff --git a/lib/personas/tools/edit_image.rb b/lib/agents/tools/edit_image.rb similarity index 99% rename from lib/personas/tools/edit_image.rb rename to lib/agents/tools/edit_image.rb index b9e3249a..97d7adcc 100644 --- a/lib/personas/tools/edit_image.rb +++ b/lib/agents/tools/edit_image.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class EditImage < Tool def self.signature diff --git a/lib/personas/tools/github_file_content.rb b/lib/agents/tools/github_file_content.rb similarity index 99% rename from lib/personas/tools/github_file_content.rb rename to lib/agents/tools/github_file_content.rb index aef00c1e..0be8fd3d 100644 --- a/lib/personas/tools/github_file_content.rb +++ b/lib/agents/tools/github_file_content.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class GithubFileContent < Tool def self.signature diff --git a/lib/personas/tools/github_pull_request_diff.rb b/lib/agents/tools/github_pull_request_diff.rb similarity index 99% rename from lib/personas/tools/github_pull_request_diff.rb rename to lib/agents/tools/github_pull_request_diff.rb index afbe51f9..699c1ee7 100644 --- a/lib/personas/tools/github_pull_request_diff.rb +++ b/lib/agents/tools/github_pull_request_diff.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class GithubPullRequestDiff < Tool LARGE_OBJECT_THRESHOLD = 30_000 diff --git a/lib/personas/tools/github_search_code.rb b/lib/agents/tools/github_search_code.rb similarity index 99% rename from lib/personas/tools/github_search_code.rb rename to lib/agents/tools/github_search_code.rb index 3bb93d02..be158f4e 100644 --- a/lib/personas/tools/github_search_code.rb +++ b/lib/agents/tools/github_search_code.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class GithubSearchCode < Tool def self.signature diff --git a/lib/personas/tools/github_search_files.rb b/lib/agents/tools/github_search_files.rb similarity index 99% rename from lib/personas/tools/github_search_files.rb rename to lib/agents/tools/github_search_files.rb index c97cbd7c..bcff9cb5 100644 --- a/lib/personas/tools/github_search_files.rb +++ b/lib/agents/tools/github_search_files.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class GithubSearchFiles < Tool def self.signature diff --git a/lib/personas/tools/google.rb b/lib/agents/tools/google.rb similarity index 99% rename from lib/personas/tools/google.rb rename to lib/agents/tools/google.rb index bf90fcdb..642238f4 100644 --- a/lib/personas/tools/google.rb +++ b/lib/agents/tools/google.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class Google < Tool def self.signature diff --git a/lib/personas/tools/image.rb b/lib/agents/tools/image.rb similarity index 99% rename from lib/personas/tools/image.rb rename to lib/agents/tools/image.rb index 3ab2b705..8f888339 100644 --- a/lib/personas/tools/image.rb +++ b/lib/agents/tools/image.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class Image < Tool def self.signature diff --git a/lib/personas/tools/javascript_evaluator.rb b/lib/agents/tools/javascript_evaluator.rb similarity index 99% rename from lib/personas/tools/javascript_evaluator.rb rename to lib/agents/tools/javascript_evaluator.rb index 23d7c1b1..33b1e532 100644 --- a/lib/personas/tools/javascript_evaluator.rb +++ b/lib/agents/tools/javascript_evaluator.rb @@ -4,7 +4,7 @@ require "mini_racer" require "json" module DiscourseAi - module Personas + module Agents module Tools class JavascriptEvaluator < Tool TIMEOUT = 500 diff --git a/lib/personas/tools/list_categories.rb b/lib/agents/tools/list_categories.rb similarity index 98% rename from lib/personas/tools/list_categories.rb rename to lib/agents/tools/list_categories.rb index 895eaae5..6cd2ab96 100644 --- a/lib/personas/tools/list_categories.rb +++ b/lib/agents/tools/list_categories.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class ListCategories < Tool def self.signature diff --git a/lib/personas/tools/list_tags.rb b/lib/agents/tools/list_tags.rb similarity index 97% rename from lib/personas/tools/list_tags.rb rename to lib/agents/tools/list_tags.rb index 852c9abc..83313ec8 100644 --- a/lib/personas/tools/list_tags.rb +++ b/lib/agents/tools/list_tags.rb @@ -1,7 +1,7 @@ #frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class ListTags < Tool def self.signature diff --git a/lib/personas/tools/option.rb b/lib/agents/tools/option.rb similarity index 97% rename from lib/personas/tools/option.rb rename to lib/agents/tools/option.rb index 777475b8..4c61fe2a 100644 --- a/lib/personas/tools/option.rb +++ b/lib/agents/tools/option.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class Option attr_reader :tool, :name, :type, :values, :default diff --git a/lib/personas/tools/random_picker.rb b/lib/agents/tools/random_picker.rb similarity index 99% rename from lib/personas/tools/random_picker.rb rename to lib/agents/tools/random_picker.rb index 1abd537f..1a9da699 100644 --- a/lib/personas/tools/random_picker.rb +++ b/lib/agents/tools/random_picker.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class RandomPicker < Tool def self.signature diff --git a/lib/personas/tools/read.rb b/lib/agents/tools/read.rb similarity index 99% rename from lib/personas/tools/read.rb rename to lib/agents/tools/read.rb index 3077de47..24bcde52 100644 --- a/lib/personas/tools/read.rb +++ b/lib/agents/tools/read.rb @@ -1,7 +1,7 @@ #frozen_string_literal: true module DiscourseAi - module Personas + module Agents MAX_POSTS = 100 module Tools diff --git a/lib/personas/tools/read_artifact.rb b/lib/agents/tools/read_artifact.rb similarity index 99% rename from lib/personas/tools/read_artifact.rb rename to lib/agents/tools/read_artifact.rb index e3074d71..fa4dfbb4 100644 --- a/lib/personas/tools/read_artifact.rb +++ b/lib/agents/tools/read_artifact.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class ReadArtifact < Tool MAX_HTML_SIZE = 30.kilobytes diff --git a/lib/personas/tools/researcher.rb b/lib/agents/tools/researcher.rb similarity index 99% rename from lib/personas/tools/researcher.rb rename to lib/agents/tools/researcher.rb index d8221a5e..38a4c510 100644 --- a/lib/personas/tools/researcher.rb +++ b/lib/agents/tools/researcher.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class Researcher < Tool attr_reader :filter, :result_count, :goals, :dry_run diff --git a/lib/personas/tools/search.rb b/lib/agents/tools/search.rb similarity index 99% rename from lib/personas/tools/search.rb rename to lib/agents/tools/search.rb index 869cff58..0f4a60dc 100644 --- a/lib/personas/tools/search.rb +++ b/lib/agents/tools/search.rb @@ -1,7 +1,7 @@ #frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class Search < Tool attr_reader :last_query diff --git a/lib/personas/tools/search_settings.rb b/lib/agents/tools/search_settings.rb similarity index 99% rename from lib/personas/tools/search_settings.rb rename to lib/agents/tools/search_settings.rb index fc66c926..68e84fbb 100644 --- a/lib/personas/tools/search_settings.rb +++ b/lib/agents/tools/search_settings.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class SearchSettings < Tool INCLUDE_DESCRIPTIONS_MAX_LENGTH = 10 diff --git a/lib/personas/tools/setting_context.rb b/lib/agents/tools/setting_context.rb similarity index 99% rename from lib/personas/tools/setting_context.rb rename to lib/agents/tools/setting_context.rb index 7fb7c6b7..2d82850b 100644 --- a/lib/personas/tools/setting_context.rb +++ b/lib/agents/tools/setting_context.rb @@ -1,7 +1,7 @@ #frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class SettingContext < Tool MAX_CONTEXT_TOKENS = 2000 diff --git a/lib/personas/tools/summarize.rb b/lib/agents/tools/summarize.rb similarity index 99% rename from lib/personas/tools/summarize.rb rename to lib/agents/tools/summarize.rb index 94f6cf49..af237ab1 100644 --- a/lib/personas/tools/summarize.rb +++ b/lib/agents/tools/summarize.rb @@ -1,7 +1,7 @@ #frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class Summarize < Tool def self.signature diff --git a/lib/personas/tools/time.rb b/lib/agents/tools/time.rb similarity index 98% rename from lib/personas/tools/time.rb rename to lib/agents/tools/time.rb index da3d4f43..50d5dcdd 100644 --- a/lib/personas/tools/time.rb +++ b/lib/agents/tools/time.rb @@ -1,7 +1,7 @@ #frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class Time < Tool def self.signature diff --git a/lib/personas/tools/tool.rb b/lib/agents/tools/tool.rb similarity index 94% rename from lib/personas/tools/tool.rb rename to lib/agents/tools/tool.rb index 540204c2..87b71c1e 100644 --- a/lib/personas/tools/tool.rb +++ b/lib/agents/tools/tool.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class Tool # Why 30 mega bytes? @@ -43,30 +43,30 @@ module DiscourseAi false end - def inject_prompt(prompt:, context:, persona:) + def inject_prompt(prompt:, context:, agent:) end end # llm being public makes it a bit easier to test attr_accessor :custom_raw, :parameters, :llm - attr_reader :tool_call_id, :persona_options, :bot_user, :context + attr_reader :tool_call_id, :agent_options, :bot_user, :context def initialize( parameters, tool_call_id: "", - persona_options: {}, + agent_options: {}, bot_user:, llm:, context: nil ) @parameters = parameters @tool_call_id = tool_call_id - @persona_options = persona_options + @agent_options = agent_options @bot_user = bot_user @llm = llm - @context = context.nil? ? DiscourseAi::Personas::BotContext.new(messages: []) : context - if !@context.is_a?(DiscourseAi::Personas::BotContext) - raise ArgumentError, "context must be a DiscourseAi::Personas::Context" + @context = context.nil? ? DiscourseAi::Agents::BotContext.new(messages: []) : context + if !@context.is_a?(DiscourseAi::Agents::BotContext) + raise ArgumentError, "context must be a DiscourseAi::Agents::Context" end end @@ -89,7 +89,7 @@ module DiscourseAi def options result = HashWithIndifferentAccess.new self.class.accepted_options.each do |option| - val = @persona_options[option.name] + val = @agent_options[option.name] if val case option.type when :boolean diff --git a/lib/personas/tools/update_artifact.rb b/lib/agents/tools/update_artifact.rb similarity index 98% rename from lib/personas/tools/update_artifact.rb rename to lib/agents/tools/update_artifact.rb index 46038d7a..f054d81c 100644 --- a/lib/personas/tools/update_artifact.rb +++ b/lib/agents/tools/update_artifact.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class UpdateArtifact < Tool def self.name @@ -36,8 +36,8 @@ module DiscourseAi } end - def self.inject_prompt(prompt:, context:, persona:) - return if persona.options["do_not_echo_artifact"].to_s == "true" + def self.inject_prompt(prompt:, context:, agent:) + return if agent.options["do_not_echo_artifact"].to_s == "true" # we inject the current artifact content into the last user message if topic_id = context.topic_id posts = Post.where(topic_id: topic_id) diff --git a/lib/personas/tools/web_browser.rb b/lib/agents/tools/web_browser.rb similarity index 99% rename from lib/personas/tools/web_browser.rb rename to lib/agents/tools/web_browser.rb index 15d5ed1e..7b79a654 100644 --- a/lib/personas/tools/web_browser.rb +++ b/lib/agents/tools/web_browser.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module DiscourseAi - module Personas + module Agents module Tools class WebBrowser < Tool def self.signature diff --git a/lib/personas/web_artifact_creator.rb b/lib/agents/web_artifact_creator.rb similarity index 97% rename from lib/personas/web_artifact_creator.rb rename to lib/agents/web_artifact_creator.rb index 8fe5ef1c..8a870812 100644 --- a/lib/personas/web_artifact_creator.rb +++ b/lib/agents/web_artifact_creator.rb @@ -1,8 +1,8 @@ #frozen_string_literal: true module DiscourseAi - module Personas - class WebArtifactCreator < Persona + module Agents + class WebArtifactCreator < Agent def tools [Tools::CreateArtifact, Tools::UpdateArtifact, Tools::ReadArtifact] end diff --git a/lib/ai_bot/entry_point.rb b/lib/ai_bot/entry_point.rb index 444baaa9..b40208ca 100644 --- a/lib/ai_bot/entry_point.rb +++ b/lib/ai_bot/entry_point.rb @@ -10,9 +10,9 @@ module DiscourseAi Bot = Struct.new(:id, :name, :llm) def self.all_bot_ids - AiPersona - .persona_users - .map { |persona| persona[:user_id] } + AiAgent + .agent_users + .map { |agent| agent[:user_id] } .concat(LlmModel.where(enabled_chat_bot: true).pluck(:user_id)) end @@ -101,7 +101,7 @@ module DiscourseAi plugin.register_modifier(:chat_allowed_bot_user_ids) do |user_ids, guardian| if guardian.user allowed_chat = - AiPersona.allowed_modalities( + AiAgent.allowed_modalities( user: guardian.user, allow_chat_direct_messages: true, allow_chat_channel_mentions: true, @@ -140,7 +140,7 @@ module DiscourseAi :topic_view, :is_bot_pm, include_condition: -> do - object.topic && object.personal_message && + object.topic && object.agentl_message && object.topic.custom_fields[TOPIC_AI_BOT_PM_FIELD] end, ) { true } @@ -155,18 +155,18 @@ module DiscourseAi plugin.add_to_serializer( :current_user, - :ai_enabled_personas, + :ai_enabled_agents, include_condition: -> { scope.authenticated? }, ) do - DiscourseAi::Personas::Persona + DiscourseAi::Agents::Agent .all(user: scope.user) - .map do |persona| + .map do |agent| { - id: persona.id, - name: persona.name, - description: persona.description, - force_default_llm: persona.force_default_llm, - username: persona.username, + id: agent.id, + name: agent.name, + description: agent.description, + force_default_llm: agent.force_default_llm, + username: agent.username, } end end @@ -191,17 +191,17 @@ module DiscourseAi ) do bots_map = ::DiscourseAi::AiBot::EntryPoint.enabled_user_ids_and_models_map - persona_users = AiPersona.persona_users(user: scope.user) - if persona_users.present? - persona_users.filter! { |persona_user| persona_user[:username].present? } + agent_users = AiAgent.agent_users(user: scope.user) + if agent_users.present? + agent_users.filter! { |agent_user| agent_user[:username].present? } bots_map.concat( - persona_users.map do |persona_user| + agent_users.map do |agent_user| { - "id" => persona_user[:user_id], - "username" => persona_user[:username], - "force_default_llm" => persona_user[:force_default_llm], - "is_persona" => true, + "id" => agent_user[:user_id], + "username" => agent_user[:username], + "force_default_llm" => agent_user[:force_default_llm], + "is_agent" => true, } end, ) @@ -216,16 +216,16 @@ module DiscourseAi plugin.add_to_serializer( :current_user, - :can_use_ai_bot_discover_persona, + :can_use_ai_bot_discover_agent, include_condition: -> do SiteSetting.ai_bot_enabled && scope.authenticated? && - SiteSetting.ai_bot_discover_persona.present? + SiteSetting.ai_bot_discover_agent.present? end, ) do - persona_allowed_groups = - AiPersona.find_by(id: SiteSetting.ai_bot_discover_persona)&.allowed_group_ids.to_a + agent_allowed_groups = + AiAgent.find_by(id: SiteSetting.ai_bot_discover_agent)&.allowed_group_ids.to_a - scope.user.in_any_groups?(persona_allowed_groups) + scope.user.in_any_groups?(agent_allowed_groups) end UserUpdater::OPTION_ATTR.push(:ai_search_discoveries) @@ -233,7 +233,7 @@ module DiscourseAi :user_option, :ai_search_discoveries, include_condition: -> do - SiteSetting.ai_bot_enabled && SiteSetting.ai_bot_discover_persona.present? && + SiteSetting.ai_bot_enabled && SiteSetting.ai_bot_discover_agent.present? && scope.authenticated? end, ) { object.ai_search_discoveries } @@ -242,19 +242,19 @@ module DiscourseAi :current_user_option, :ai_search_discoveries, include_condition: -> do - SiteSetting.ai_bot_enabled && SiteSetting.ai_bot_discover_persona.present? && + SiteSetting.ai_bot_enabled && SiteSetting.ai_bot_discover_agent.present? && scope.authenticated? end, ) { object.ai_search_discoveries } plugin.add_to_serializer( :topic_view, - :ai_persona_name, + :ai_agent_name, include_condition: -> { SiteSetting.ai_bot_enabled && object.topic.private_message? }, ) do - id = topic.custom_fields["ai_persona_id"] - name = DiscourseAi::Personas::Persona.find_by(user: scope.user, id: id.to_i)&.name if id - name || topic.custom_fields["ai_persona"] + id = topic.custom_fields["ai_agent_id"] + name = DiscourseAi::Agents::Agent.find_by(user: scope.user, id: id.to_i)&.name if id + name || topic.custom_fields["ai_agent"] end plugin.on(:post_created) { |post| DiscourseAi::AiBot::Playground.schedule_reply(post) } @@ -264,12 +264,12 @@ module DiscourseAi end if plugin.respond_to?(:register_editable_topic_custom_field) - plugin.register_editable_topic_custom_field(:ai_persona_id) + plugin.register_editable_topic_custom_field(:ai_agent_id) end plugin.add_api_key_scope( :discourse_ai, - { stream_completion: { actions: %w[discourse_ai/admin/ai_personas#stream_reply] } }, + { stream_completion: { actions: %w[discourse_ai/admin/ai_agents#stream_reply] } }, ) plugin.on(:site_setting_changed) do |name, old_value, new_value| @@ -277,11 +277,11 @@ module DiscourseAi new_value != old_value RagDocumentFragment.delete_all UploadReference - .where(target: AiPersona.all) + .where(target: AiAgent.all) .each do |ref| Jobs.enqueue( :digest_rag_upload, - ai_persona_id: ref.target_id, + ai_agent_id: ref.target_id, upload_id: ref.upload_id, ) end diff --git a/lib/ai_bot/playground.rb b/lib/ai_bot/playground.rb index 07c4984b..d09fa2e7 100644 --- a/lib/ai_bot/playground.rb +++ b/lib/ai_bot/playground.rb @@ -15,9 +15,9 @@ module DiscourseAi # The bot will take care of completions while this class updates the topic title # and stream replies. - def self.find_chat_persona(message, channel, user) + def self.find_chat_agent(message, channel, user) if channel.direct_message_channel? - AiPersona + AiAgent .allowed_modalities(allow_chat_direct_messages: true) .find do |p| p[:user_id].in?(channel.allowed_user_ids) && (user.group_ids & p[:allowed_group_ids]) @@ -27,7 +27,7 @@ module DiscourseAi if message.message.include?("@") mentions = message.parsed_mentions.parsed_direct_mentions if mentions.present? - AiPersona + AiAgent .allowed_modalities(allow_chat_channel_mentions: true) .find { |p| p[:username].in?(mentions) && (user.group_ids & p[:allowed_group_ids]) } end @@ -39,15 +39,15 @@ module DiscourseAi return if !SiteSetting.ai_bot_enabled all_chat = - AiPersona.allowed_modalities( + AiAgent.allowed_modalities( allow_chat_channel_mentions: true, allow_chat_direct_messages: true, ) return if all_chat.blank? return if all_chat.any? { |m| m[:user_id] == user.id } - persona = find_chat_persona(message, channel, user) - return if !persona + agent = find_chat_agent(message, channel, user) + return if !agent post_ids = nil post_ids = context.dig(:context, :post_ids) if context.is_a?(Hash) @@ -56,7 +56,7 @@ module DiscourseAi :create_ai_chat_reply, channel_id: channel.id, message_id: message.id, - persona_id: persona[:id], + agent_id: agent[:id], context_post_ids: post_ids, ) end @@ -101,9 +101,9 @@ module DiscourseAi if post.topic.private_message? mentionables = - AiPersona.allowed_modalities(user: post.user, allow_personal_messages: true) + AiAgent.allowed_modalities(user: post.user, allow_agentl_messages: true) else - mentionables = AiPersona.allowed_modalities(user: post.user, allow_topic_mentions: true) + mentionables = AiAgent.allowed_modalities(user: post.user, allow_topic_mentions: true) end mentioned = nil @@ -135,7 +135,7 @@ module DiscourseAi mentioned = mentionables.find { |mentionable| bot_user.id == mentionable[:user_id] } end - # public topic so we need to use the persona user + # public topic so we need to use the agent user bot_user ||= User.find_by(id: mentioned[:user_id]) if mentioned end @@ -145,25 +145,25 @@ module DiscourseAi end if bot_user - topic_persona_id = post.topic.custom_fields["ai_persona_id"] - topic_persona_id = topic_persona_id.to_i if topic_persona_id.present? + topic_agent_id = post.topic.custom_fields["ai_agent_id"] + topic_agent_id = topic_agent_id.to_i if topic_agent_id.present? - persona_id = mentioned&.dig(:id) || topic_persona_id + agent_id = mentioned&.dig(:id) || topic_agent_id - persona = nil + agent = nil - if persona_id - persona = DiscourseAi::Personas::Persona.find_by(user: post.user, id: persona_id.to_i) + if agent_id + agent = DiscourseAi::Agents::Agent.find_by(user: post.user, id: agent_id.to_i) end - if !persona && persona_name = post.topic.custom_fields["ai_persona"] - persona = DiscourseAi::Personas::Persona.find_by(user: post.user, name: persona_name) + if !agent && agent_name = post.topic.custom_fields["ai_agent"] + agent = DiscourseAi::Agents::Agent.find_by(user: post.user, name: agent_name) end - # edge case, llm was mentioned in an ai persona conversation - if persona_id == topic_persona_id && post.topic.private_message? && persona && + # edge case, llm was mentioned in an ai agent conversation + if agent_id == topic_agent_id && post.topic.private_message? && agent && all_llm_users.present? - if !persona.force_default_llm && mentions.present? + if !agent.force_default_llm && mentions.present? mentioned_llm_user_id, _ = all_llm_users.find { |id, username| mentions.include?(username) } @@ -173,11 +173,11 @@ module DiscourseAi end end - persona ||= DiscourseAi::Personas::General + agent ||= DiscourseAi::Agents::General - bot_user = User.find(persona.user_id) if persona && persona.force_default_llm + bot_user = User.find(agent.user_id) if agent && agent.force_default_llm - bot = DiscourseAi::Personas::Bot.as(bot_user, persona: persona.new) + bot = DiscourseAi::Agents::Bot.as(bot_user, agent: agent.new) new(bot).update_playground_with(post) end end @@ -185,7 +185,7 @@ module DiscourseAi def self.reply_to_post( post:, user: nil, - persona_id: nil, + agent_id: nil, whisper: nil, add_user_to_pm: false, stream_reply: false, @@ -193,14 +193,14 @@ module DiscourseAi silent_mode: false, feature_name: nil ) - ai_persona = AiPersona.find_by(id: persona_id) - raise Discourse::InvalidParameters.new(:persona_id) if !ai_persona - persona_class = ai_persona.class_instance - persona = persona_class.new + ai_agent = AiAgent.find_by(id: agent_id) + raise Discourse::InvalidParameters.new(:agent_id) if !ai_agent + agent_class = ai_agent.class_instance + agent = agent_class.new - bot_user = user || ai_persona.user + bot_user = user || ai_agent.user raise Discourse::InvalidParameters.new(:user) if bot_user.nil? - bot = DiscourseAi::Personas::Bot.as(bot_user, persona: persona) + bot = DiscourseAi::Agents::Bot.as(bot_user, agent: agent) playground = new(bot) playground.reply_to( @@ -236,7 +236,7 @@ module DiscourseAi post, max_posts: 5, bot_usernames: available_bot_usernames, - include_uploads: bot.persona.class.vision_enabled, + include_uploads: bot.agent.class.vision_enabled, ) # conversation context may contain tool calls, and confusing user names @@ -297,15 +297,15 @@ module DiscourseAi end def reply_to_chat_message(message, channel, context_post_ids) - persona_user = User.find(bot.persona.class.user_id) + agent_user = User.find(bot.agent.class.user_id) participants = channel.user_chat_channel_memberships.map { |m| m.user.username } context_post_ids = nil if !channel.direct_message_channel? max_chat_messages = 40 - if bot.persona.class.respond_to?(:max_context_posts) - max_chat_messages = bot.persona.class.max_context_posts || 40 + if bot.agent.class.respond_to?(:max_context_posts) + max_chat_messages = bot.agent.class.max_context_posts || 40 end if !channel.direct_message_channel? @@ -314,7 +314,7 @@ module DiscourseAi end context = - DiscourseAi::Personas::BotContext.new( + DiscourseAi::Agents::BotContext.new( participants: participants, message_id: message.id, channel_id: channel.id, @@ -324,7 +324,7 @@ module DiscourseAi message, channel: channel, context_post_ids: context_post_ids, - include_uploads: bot.persona.class.vision_enabled, + include_uploads: bot.agent.class.vision_enabled, max_messages: max_chat_messages, bot_user_ids: available_bot_user_ids, instruction_message: instruction_message, @@ -335,7 +335,7 @@ module DiscourseAi ) reply = nil - guardian = Guardian.new(persona_user) + guardian = Guardian.new(agent_user) force_thread = message.thread_id.nil? && channel.direct_message_channel? in_reply_to_id = channel.direct_message_channel? ? message.id : nil @@ -410,12 +410,12 @@ module DiscourseAi # safeguard max_context_posts = 40 - if bot.persona.class.respond_to?(:max_context_posts) - max_context_posts = bot.persona.class.max_context_posts || 40 + if bot.agent.class.respond_to?(:max_context_posts) + max_context_posts = bot.agent.class.max_context_posts || 40 end context = - DiscourseAi::Personas::BotContext.new( + DiscourseAi::Agents::BotContext.new( post: post, custom_instructions: custom_instructions, feature_name: feature_name, @@ -424,19 +424,19 @@ module DiscourseAi post, style: context_style, max_posts: max_context_posts, - include_uploads: bot.persona.class.vision_enabled, + include_uploads: bot.agent.class.vision_enabled, bot_usernames: available_bot_usernames, ), ) reply_user = bot.bot_user - if bot.persona.class.respond_to?(:user_id) - reply_user = User.find_by(id: bot.persona.class.user_id) || reply_user + if bot.agent.class.respond_to?(:user_id) + reply_user = User.find_by(id: bot.agent.class.user_id) || reply_user end stream_reply = post.topic.private_message? if stream_reply.nil? - # we need to ensure persona user is allowed to reply to the pm + # we need to ensure agent user is allowed to reply to the pm if post.topic.private_message? && add_user_to_pm if !post.topic.topic_allowed_users.exists?(user_id: reply_user.id) post.topic.topic_allowed_users.create!(user_id: reply_user.id) @@ -486,7 +486,7 @@ module DiscourseAi ) end - context.skip_tool_details ||= !bot.persona.class.tool_details + context.skip_tool_details ||= !bot.agent.class.tool_details post_streamer = PostStreamer.new(delay: Rails.env.test? ? 0 : 0.5) if stream_reply started_thinking = false @@ -587,11 +587,11 @@ module DiscourseAi def available_bot_usernames @bot_usernames ||= - AiPersona.joins(:user).pluck(:username).concat(available_bot_users.map(&:username)) + AiAgent.joins(:user).pluck(:username).concat(available_bot_users.map(&:username)) end def available_bot_user_ids - @bot_ids ||= AiPersona.joins(:user).pluck("users.id").concat(available_bot_users.map(&:id)) + @bot_ids ||= AiAgent.joins(:user).pluck("users.id").concat(available_bot_users.map(&:id)) end private @@ -624,13 +624,13 @@ module DiscourseAi end def schedule_bot_reply(post) - persona_id = - DiscourseAi::Personas::Persona.system_personas[bot.persona.class] || bot.persona.class.id + agent_id = + DiscourseAi::Agents::Agent.system_agents[bot.agent.class] || bot.agent.class.id ::Jobs.enqueue( :create_ai_reply, post_id: post.id, bot_user_id: bot.bot_user.id, - persona_id: persona_id, + agent_id: agent_id, ) end diff --git a/lib/ai_bot/response_http_streamer.rb b/lib/ai_bot/response_http_streamer.rb index cfb2fbeb..9fbd60ef 100644 --- a/lib/ai_bot/response_http_streamer.rb +++ b/lib/ai_bot/response_http_streamer.rb @@ -31,7 +31,7 @@ module DiscourseAi # this allows us to release memory earlier def queue_streamed_reply( io:, - persona:, + agent:, user:, topic:, query:, @@ -53,7 +53,7 @@ module DiscourseAi else post_params[:title] = I18n.t("discourse_ai.ai_bot.default_pm_prefix") post_params[:archetype] = Archetype.private_message - post_params[:target_usernames] = "#{user.username},#{persona.user.username}" + post_params[:target_usernames] = "#{user.username},#{agent.user.username}" end post = PostCreator.create!(user, post_params) @@ -76,15 +76,15 @@ module DiscourseAi io.write CRLF io.flush - persona_class = - DiscourseAi::Personas::Persona.find_by(id: persona.id, user: current_user) - bot = DiscourseAi::Personas::Bot.as(persona.user, persona: persona_class.new) + agent_class = + DiscourseAi::Agents::Agent.find_by(id: agent.id, user: current_user) + bot = DiscourseAi::Agents::Bot.as(agent.user, agent: agent_class.new) data = { topic_id: topic.id, - bot_user_id: persona.user.id, - persona_id: persona.id, + bot_user_id: agent.user.id, + agent_id: agent.id, }.to_json + "\n\n" io.write data.bytesize.to_s(16) diff --git a/lib/automation.rb b/lib/automation.rb index e43bcbc5..0752590d 100644 --- a/lib/automation.rb +++ b/lib/automation.rb @@ -42,15 +42,15 @@ module DiscourseAi values end - def self.available_persona_choices(require_user: true, require_default_llm: true) - relation = AiPersona.joins(:user) + def self.available_agent_choices(require_user: true, require_default_llm: true) + relation = AiAgent.joins(:user) relation = relation.where.not(user_id: nil) if require_user relation = relation.where.not(default_llm: nil) if require_default_llm - relation.map do |persona| + relation.map do |agent| { - id: persona.id, - translated_name: persona.name, - description: "#{persona.name} (#{persona.user.username})", + id: agent.id, + translated_name: agent.name, + description: "#{agent.name} (#{agent.user.username})", } end end diff --git a/lib/automation/llm_persona_triage.rb b/lib/automation/llm_agent_triage.rb similarity index 64% rename from lib/automation/llm_persona_triage.rb rename to lib/automation/llm_agent_triage.rb index dbbdbc6a..decb24eb 100644 --- a/lib/automation/llm_persona_triage.rb +++ b/lib/automation/llm_agent_triage.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true module DiscourseAi module Automation - module LlmPersonaTriage - def self.handle(post:, persona_id:, whisper: false, silent_mode: false, automation: nil) + module LlmAgentTriage + def self.handle(post:, agent_id:, whisper: false, silent_mode: false, automation: nil) DiscourseAi::AiBot::Playground.reply_to_post( post: post, - persona_id: persona_id, + agent_id: agent_id, whisper: whisper, silent_mode: silent_mode, feature_name: "automation - #{automation&.name}", @@ -13,7 +13,7 @@ module DiscourseAi rescue => e Discourse.warn_exception( e, - message: "Error responding to: #{post&.url} in LlmPersonaTriage.handle", + message: "Error responding to: #{post&.url} in LlmAgentTriage.handle", ) raise e if Rails.env.test? nil diff --git a/lib/automation/llm_tool_triage.rb b/lib/automation/llm_tool_triage.rb index 58b9210d..a8eb4eee 100644 --- a/lib/automation/llm_tool_triage.rb +++ b/lib/automation/llm_tool_triage.rb @@ -7,7 +7,7 @@ module DiscourseAi return if !tool return if !tool.parameters.blank? - context = DiscourseAi::Personas::BotContext.new(post: post) + context = DiscourseAi::Agents::BotContext.new(post: post) runner = tool.runner({}, llm: nil, bot_user: Discourse.system_user, context: context) runner.invoke diff --git a/lib/automation/llm_triage.rb b/lib/automation/llm_triage.rb index 20ab9ae4..05cd87de 100644 --- a/lib/automation/llm_triage.rb +++ b/lib/automation/llm_triage.rb @@ -20,11 +20,11 @@ module DiscourseAi stop_sequences: nil, temperature: nil, whisper: nil, - reply_persona_id: nil, + reply_agent_id: nil, action: nil ) if category_id.blank? && tags.blank? && canned_reply.blank? && hide_topic.blank? && - flag_post.blank? && reply_persona_id.blank? + flag_post.blank? && reply_agent_id.blank? raise ArgumentError, "llm_triage: no action specified!" end @@ -69,11 +69,11 @@ module DiscourseAi user = User.find_by_username(canned_reply_user) if canned_reply_user.present? original_user = user user = user || Discourse.system_user - if reply_persona_id.present? && action != :edit + if reply_agent_id.present? && action != :edit begin DiscourseAi::AiBot::Playground.reply_to_post( post: post, - persona_id: reply_persona_id, + agent_id: reply_agent_id, whisper: whisper, user: original_user, ) diff --git a/lib/configuration/persona_enumerator.rb b/lib/configuration/agent_enumerator.rb similarity index 53% rename from lib/configuration/persona_enumerator.rb rename to lib/configuration/agent_enumerator.rb index c115bc50..b25bdf4d 100644 --- a/lib/configuration/persona_enumerator.rb +++ b/lib/configuration/agent_enumerator.rb @@ -4,15 +4,15 @@ require "enum_site_setting" module DiscourseAi module Configuration - class PersonaEnumerator < ::EnumSiteSetting + class AgentEnumerator < ::EnumSiteSetting def self.valid_value?(val) true end def self.values - AiPersona - .all_personas(enabled_only: false) - .map { |persona| { name: persona.name, value: persona.id } } + AiAgent + .all_agents(enabled_only: false) + .map { |agent| { name: agent.name, value: agent.id } } end end end diff --git a/lib/configuration/llm_enumerator.rb b/lib/configuration/llm_enumerator.rb index 200fc0a2..6fa13c32 100644 --- a/lib/configuration/llm_enumerator.rb +++ b/lib/configuration/llm_enumerator.rb @@ -16,10 +16,10 @@ module DiscourseAi end # this is unconditional, so it is clear that we always signal configuration - AiPersona + AiAgent .where("default_llm_id IS NOT NULL") .pluck(:default_llm_id, :name, :id) - .each { |llm_id, name, id| rval[llm_id] << { type: :ai_persona, name: name, id: id } } + .each { |llm_id, name, id| rval[llm_id] << { type: :ai_agent, name: name, id: id } } if SiteSetting.ai_helper_enabled model_id = SiteSetting.ai_helper_model.split(":").last.to_i @@ -32,8 +32,8 @@ module DiscourseAi end if SiteSetting.ai_summarization_enabled - summarization_persona = AiPersona.find_by(id: SiteSetting.ai_summarization_persona) - model_id = summarization_persona.default_llm_id || LlmModel.last&.id + summarization_agent = AiAgent.find_by(id: SiteSetting.ai_summarization_agent) + model_id = summarization_agent.default_llm_id || LlmModel.last&.id rval[model_id] << { type: :ai_summarization } end diff --git a/lib/discord/bot/persona_replier.rb b/lib/discord/bot/agent_replier.rb similarity index 78% rename from lib/discord/bot/persona_replier.rb rename to lib/discord/bot/agent_replier.rb index b64af15c..5ebd1b2f 100644 --- a/lib/discord/bot/persona_replier.rb +++ b/lib/discord/bot/agent_replier.rb @@ -2,18 +2,18 @@ module DiscourseAi module Discord::Bot - class PersonaReplier < Base + class AgentReplier < Base def initialize(body) - @persona = - AiPersona - .all_personas(enabled_only: false) - .find { |persona| persona.id == SiteSetting.ai_discord_search_persona.to_i } + @agent = + AiAgent + .all_agents(enabled_only: false) + .find { |agent| agent.id == SiteSetting.ai_discord_search_agent.to_i } .new @bot = - DiscourseAi::Personas::Bot.as( + DiscourseAi::Agents::Bot.as( Discourse.system_user, - persona: @persona, - model: LlmModel.find(@persona.class.default_llm_id), + agent: @agent, + model: LlmModel.find(@agent.class.default_llm_id), ) super(body) end diff --git a/lib/discord/bot/search.rb b/lib/discord/bot/search.rb index e33e35e7..e214f002 100644 --- a/lib/discord/bot/search.rb +++ b/lib/discord/bot/search.rb @@ -4,7 +4,7 @@ module DiscourseAi module Discord::Bot class Search < Base def initialize(body) - @search = DiscourseAi::Personas::Tools::Search + @search = DiscourseAi::Agents::Tools::Search super(body) end @@ -12,7 +12,7 @@ module DiscourseAi results = @search.new( { search_query: @query }, - persona_options: { + agent_options: { "max_results" => 10, }, bot_user: nil, diff --git a/lib/features.rb b/lib/features.rb index d3b999c2..2095d9e4 100644 --- a/lib/features.rb +++ b/lib/features.rb @@ -9,7 +9,7 @@ module DiscourseAi name_ref: "summarization", name_key: "discourse_ai.features.summarization.name", description_key: "discourse_ai.features.summarization.description", - persona_setting_name: "ai_summarization_persona", + agent_setting_name: "ai_summarization_agent", enable_setting_name: "ai_summarization_enabled", }, { @@ -17,7 +17,7 @@ module DiscourseAi name_ref: "gists", name_key: "discourse_ai.features.gists.name", description_key: "discourse_ai.features.gists.description", - persona_setting_name: "ai_summary_gists_persona", + agent_setting_name: "ai_summary_gists_agent", enable_setting_name: "ai_summary_gists_enabled", }, { @@ -25,7 +25,7 @@ module DiscourseAi name_ref: "discoveries", name_key: "discourse_ai.features.discoveries.name", description_key: "discourse_ai.features.discoveries.description", - persona_setting_name: "ai_bot_discover_persona", + agent_setting_name: "ai_bot_discover_agent", enable_setting_name: "ai_bot_enabled", }, { @@ -33,7 +33,7 @@ module DiscourseAi name_ref: "discord_search", name_key: "discourse_ai.features.discord_search.name", description_key: "discourse_ai.features.discord_search.description", - persona_setting_name: "ai_discord_search_persona", + agent_setting_name: "ai_discord_search_agent", enable_setting_name: "ai_discord_search_enabled", }, ] @@ -46,11 +46,11 @@ module DiscourseAi ref: feature[:name_ref], name: I18n.t(feature[:name_key]), description: I18n.t(feature[:description_key]), - persona: AiPersona.find_by(id: SiteSetting.get(feature[:persona_setting_name])), - persona_setting: { - name: feature[:persona_setting_name], - value: SiteSetting.get(feature[:persona_setting_name]), - type: SiteSetting.type_supervisor.get_type(feature[:persona_setting_name]), + agent: AiAgent.find_by(id: SiteSetting.get(feature[:agent_setting_name])), + agent_setting: { + name: feature[:agent_setting_name], + value: SiteSetting.get(feature[:agent_setting_name]), + type: SiteSetting.type_supervisor.get_type(feature[:agent_setting_name]), }, enable_setting: { name: feature[:enable_setting_name], diff --git a/lib/guardian_extensions.rb b/lib/guardian_extensions.rb index 45cb1b55..7956d3cc 100644 --- a/lib/guardian_extensions.rb +++ b/lib/guardian_extensions.rb @@ -25,25 +25,25 @@ module DiscourseAi return false if !SiteSetting.ai_summarization_enabled return false if !SiteSetting.ai_summary_gists_enabled - if (ai_persona = AiPersona.find_by(id: SiteSetting.ai_summary_gists_persona)).blank? + if (ai_agent = AiAgent.find_by(id: SiteSetting.ai_summary_gists_agent)).blank? return false end - persona_groups = ai_persona.allowed_group_ids.to_a - return true if persona_groups.include?(Group::AUTO_GROUPS[:everyone]) + agent_groups = ai_agent.allowed_group_ids.to_a + return true if agent_groups.include?(Group::AUTO_GROUPS[:everyone]) return false if anonymous? - persona_groups.any? { |group_id| user.group_ids.include?(group_id) } + agent_groups.any? { |group_id| user.group_ids.include?(group_id) } end def can_request_summary? return false if anonymous? user_group_ids = user.group_ids - if (ai_persona = AiPersona.find_by(id: SiteSetting.ai_summarization_persona)).blank? + if (ai_agent = AiAgent.find_by(id: SiteSetting.ai_summarization_agent)).blank? return false end - ai_persona.allowed_group_ids.to_a.any? { |group_id| user.group_ids.include?(group_id) } + ai_agent.allowed_group_ids.to_a.any? { |group_id| user.group_ids.include?(group_id) } end def can_debug_ai_bot_conversation?(target) diff --git a/lib/summarization.rb b/lib/summarization.rb index a7b69763..0b2ceea6 100644 --- a/lib/summarization.rb +++ b/lib/summarization.rb @@ -5,60 +5,60 @@ module DiscourseAi class << self def topic_summary(topic) return nil if !SiteSetting.ai_summarization_enabled - if (ai_persona = AiPersona.find_by(id: SiteSetting.ai_summarization_persona)).blank? + if (ai_agent = AiAgent.find_by(id: SiteSetting.ai_summarization_agent)).blank? return nil end - persona_klass = ai_persona.class_instance - llm_model = find_summarization_model(persona_klass) + agent_klass = ai_agent.class_instance + llm_model = find_summarization_model(agent_klass) return nil if llm_model.blank? DiscourseAi::Summarization::FoldContent.new( - build_bot(persona_klass, llm_model), + build_bot(agent_klass, llm_model), DiscourseAi::Summarization::Strategies::TopicSummary.new(topic), ) end def topic_gist(topic) return nil if !SiteSetting.ai_summarization_enabled - if (ai_persona = AiPersona.find_by(id: SiteSetting.ai_summary_gists_persona)).blank? + if (ai_agent = AiAgent.find_by(id: SiteSetting.ai_summary_gists_agent)).blank? return nil end - persona_klass = ai_persona.class_instance - llm_model = find_summarization_model(persona_klass) + agent_klass = ai_agent.class_instance + llm_model = find_summarization_model(agent_klass) return nil if llm_model.blank? DiscourseAi::Summarization::FoldContent.new( - build_bot(persona_klass, llm_model), + build_bot(agent_klass, llm_model), DiscourseAi::Summarization::Strategies::HotTopicGists.new(topic), ) end def chat_channel_summary(channel, time_window_in_hours) return nil if !SiteSetting.ai_summarization_enabled - if (ai_persona = AiPersona.find_by(id: SiteSetting.ai_summarization_persona)).blank? + if (ai_agent = AiAgent.find_by(id: SiteSetting.ai_summarization_agent)).blank? return nil end - persona_klass = ai_persona.class_instance - llm_model = find_summarization_model(persona_klass) + agent_klass = ai_agent.class_instance + llm_model = find_summarization_model(agent_klass) return nil if llm_model.blank? DiscourseAi::Summarization::FoldContent.new( - build_bot(persona_klass, llm_model), + build_bot(agent_klass, llm_model), DiscourseAi::Summarization::Strategies::ChatMessages.new(channel, time_window_in_hours), persist_summaries: false, ) end # Priorities are: - # 1. Persona's default LLM + # 1. Agent's default LLM # 2. Hidden `ai_summarization_model` setting # 3. Newest LLM config - def find_summarization_model(persona_klass) + def find_summarization_model(agent_klass) model_id = - persona_klass.default_llm_id || SiteSetting.ai_summarization_model&.split(":")&.last # Remove legacy custom provider. + agent_klass.default_llm_id || SiteSetting.ai_summarization_model&.split(":")&.last # Remove legacy custom provider. if model_id.present? LlmModel.find_by(id: model_id) @@ -69,11 +69,11 @@ module DiscourseAi ### Private - def build_bot(persona_klass, llm_model) - persona = persona_klass.new - user = User.find_by(id: persona_klass.user_id) || Discourse.system_user + def build_bot(agent_klass, llm_model) + agent = agent_klass.new + user = User.find_by(id: agent_klass.user_id) || Discourse.system_user - bot = DiscourseAi::Personas::Bot.as(user, persona: persona, model: llm_model) + bot = DiscourseAi::Agents::Bot.as(user, agent: agent, model: llm_model) end end end diff --git a/lib/summarization/entry_point.rb b/lib/summarization/entry_point.rb index ca9cd6d5..e8383003 100644 --- a/lib/summarization/entry_point.rb +++ b/lib/summarization/entry_point.rb @@ -7,10 +7,10 @@ module DiscourseAi plugin.add_to_serializer(:current_user, :can_summarize) do return false if !SiteSetting.ai_summarization_enabled - if (ai_persona = AiPersona.find_by(id: SiteSetting.ai_summarization_persona)).blank? + if (ai_agent = AiAgent.find_by(id: SiteSetting.ai_summarization_agent)).blank? return false end - scope.user.in_any_groups?(ai_persona.allowed_group_ids.to_a) + scope.user.in_any_groups?(ai_agent.allowed_group_ids.to_a) end plugin.add_to_serializer(:topic_view, :summarizable) do diff --git a/lib/summarization/fold_content.rb b/lib/summarization/fold_content.rb index 17df679b..460b2904 100644 --- a/lib/summarization/fold_content.rb +++ b/lib/summarization/fold_content.rb @@ -101,7 +101,7 @@ module DiscourseAi end context = - DiscourseAi::Personas::BotContext.new( + DiscourseAi::Agents::BotContext.new( user: user, skip_tool_details: true, feature_name: strategy.feature, @@ -114,7 +114,7 @@ module DiscourseAi buffer_blk = Proc.new do |partial, _, type| if type == :structured_output - json_summary_schema_key = bot.persona.response_format&.first.to_h + json_summary_schema_key = bot.agent.response_format&.first.to_h partial_summary = partial.read_buffered_property(json_summary_schema_key["key"]&.to_sym) diff --git a/plugin.rb b/plugin.rb index 8510dbf9..0bd79f71 100644 --- a/plugin.rb +++ b/plugin.rb @@ -39,9 +39,9 @@ register_asset "stylesheets/modules/summarization/desktop/ai-summary.scss", :des register_asset "stylesheets/modules/summarization/common/ai-gists.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/common/ai-agent.scss" register_asset "stylesheets/modules/ai-bot/common/ai-discobot-discoveries.scss" -register_asset "stylesheets/modules/ai-bot/mobile/ai-persona.scss", :mobile +register_asset "stylesheets/modules/ai-bot/mobile/ai-agent.scss", :mobile register_asset "stylesheets/modules/ai-bot-conversations/common.scss" @@ -87,11 +87,11 @@ after_initialize do require_relative "discourse_automation/llm_triage" require_relative "discourse_automation/llm_report" require_relative "discourse_automation/llm_tool_triage" - require_relative "discourse_automation/llm_persona_triage" + require_relative "discourse_automation/llm_agent_triage" add_admin_route("discourse_ai.title", "discourse-ai", { use_new_show_route: true }) - register_seedfu_fixtures(Rails.root.join("plugins", "discourse-ai", "db", "fixtures", "personas")) + register_seedfu_fixtures(Rails.root.join("plugins", "discourse-ai", "db", "fixtures", "agents")) [ DiscourseAi::Embeddings::EntryPoint.new, @@ -137,7 +137,7 @@ after_initialize do add_api_key_scope( :discourse_ai, - { update_personas: { actions: %w[discourse_ai/admin/ai_personas#update] } }, + { update_agents: { actions: %w[discourse_ai/admin/ai_agents#update] } }, ) plugin_icons = %w[ diff --git a/rename_persona_to_agent.rb b/rename_persona_to_agent.rb new file mode 100755 index 00000000..6998cbe5 --- /dev/null +++ b/rename_persona_to_agent.rb @@ -0,0 +1,338 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "fileutils" +require "date" + +class PersonaToAgentRenamer + def initialize + @plugin_root = Dir.pwd + @manifest = [] + end + + def run + case ARGV[0] + when "--content-only" + validate_directory + replace_content_in_files + print_content_summary + when "--migrations-only" + validate_directory + create_migration + create_post_migration + print_migration_summary + else + puts colorize("=" * 55, :blue) + puts colorize("Renaming 'persona' to 'agent' throughout the codebase", :blue) + puts colorize("=" * 55, :blue) + puts colorize("Working in: #{@plugin_root}", :blue) + + validate_directory + replace_content_in_files + rename_files_and_directories + create_migration + create_post_migration + print_summary + end + end + + private + + def colorize(text, color) + colors = { + red: "\033[0;31m", + green: "\033[0;32m", + yellow: "\033[0;33m", + blue: "\033[0;34m", + nc: "\033[0m", + } + "#{colors[color]}#{text}#{colors[:nc]}" + end + + def validate_directory + unless File.exist?(File.join(@plugin_root, "plugin.rb")) + puts colorize("Error: Script must be run from the discourse-ai plugin root directory", :red) + exit 1 + end + end + + def git_tracked_files + `git ls-files`.split("\n").map { |f| File.join(@plugin_root, f) } + end + + def all_directories_with_persona + # Find all directories that contain 'persona' in their name + output = `find . -type d -name "*persona*" 2>/dev/null | grep -v "\\.git"`.split("\n") + output.map { |dir| File.join(@plugin_root, dir.sub("./", "")) } + end + + def replace_content_in_files + puts colorize("Replacing content in files...", :blue) + + file_extensions = %w[rb js gjs yml erb scss hbs json] + + git_tracked_files.each do |file| + # Skip files in db/ directory and tokenizers/ + next if file.include?("/db/") + next if file.include?("/tokenizers/") + next if !file_extensions.include?(File.extname(file)[1..-1]) + next if File.basename(file) == "rename_persona_to_agent.rb" + next unless File.exist?(file) + + content = File.read(file) + next unless content.match?(/persona/i) + + original_content = content.dup + + # Replace different case variations + content.gsub!(/persona/, "agent") + content.gsub!(/Persona/, "Agent") + content.gsub!(/PERSONA/, "AGENT") + + # Handle special cases + content.gsub!(/aiPersona/, "aiAgent") + content.gsub!(/AIPersona/, "AIAgent") + content.gsub!(/ai_persona/, "ai_agent") + content.gsub!(/Ai_persona/, "Ai_agent") + content.gsub!(/ai-persona/, "ai-agent") + + if content != original_content + File.write(file, content) + relative_path = file.sub("#{@plugin_root}/", "") + puts colorize("Content updated: #{relative_path}", :green) + @manifest << "Content updated: #{relative_path}" + end + end + end + + def rename_files_and_directories + puts colorize("Renaming files and directories using git mv...", :blue) + + # Get all directories with 'persona' in their path (excluding db/ and tokenizers/) + # Sort by depth (deepest first) to avoid conflicts when renaming parent directories + dirs_to_rename = + all_directories_with_persona + .select { |path| !path.include?("/db/") && !path.include?("/tokenizers/") } + .sort_by { |path| -path.count("/") } + + # Get all files with 'persona' in their names (excluding db/ and tokenizers/) + files_to_rename = + git_tracked_files.select do |path| + !path.include?("/db/") && !path.include?("/tokenizers/") && + File.basename(path).match?(/persona/i) + end + + # First, rename individual files that have 'persona' in their filename + puts colorize(" Renaming individual files with 'persona' in filename...", :blue) + files_to_rename.each do |old_path| + next unless File.exist?(old_path) + next if File.basename(old_path) == "rename_persona_to_agent.rb" + + # Skip files that are inside directories we're going to rename + # (they'll be handled when we rename the directory) + next if dirs_to_rename.any? { |dir| old_path.start_with?(dir + "/") } + + new_path = old_path.gsub(/persona/, "agent").gsub(/Persona/, "Agent") + + if old_path != new_path + # Ensure parent directory exists + FileUtils.mkdir_p(File.dirname(new_path)) + + # Use git mv to preserve history + if system("git", "mv", old_path, new_path) + old_relative = old_path.sub("#{@plugin_root}/", "") + new_relative = new_path.sub("#{@plugin_root}/", "") + puts colorize(" File renamed: #{old_relative} -> #{new_relative}", :green) + @manifest << "File renamed: #{old_relative} -> #{new_relative}" + else + puts colorize(" Failed to rename: #{old_path}", :red) + end + end + end + + # Then rename directories (deepest first to avoid path conflicts) + puts colorize(" Renaming directories with 'persona' in path...", :blue) + dirs_to_rename.each do |old_dir_path| + next unless File.exist?(old_dir_path) && File.directory?(old_dir_path) + + new_dir_path = old_dir_path.gsub(/persona/, "agent").gsub(/Persona/, "Agent") + + if old_dir_path != new_dir_path && !File.exist?(new_dir_path) + # Create parent directory if needed + FileUtils.mkdir_p(File.dirname(new_dir_path)) + + # Use git mv to preserve history for the entire directory tree + if system("git", "mv", old_dir_path, new_dir_path) + old_relative = old_dir_path.sub("#{@plugin_root}/", "") + new_relative = new_dir_path.sub("#{@plugin_root}/", "") + puts colorize(" Directory renamed: #{old_relative} -> #{new_relative}", :green) + @manifest << "Directory renamed: #{old_relative} -> #{new_relative}" + + # Log all files that were moved as part of this directory rename + if File.directory?(new_dir_path) + Dir + .glob("#{new_dir_path}/**/*", File::FNM_DOTMATCH) + .each do |moved_file| + next if File.directory?(moved_file) + next if File.basename(moved_file).start_with?(".") + + # Calculate what the old path would have been + relative_to_new_dir = moved_file.sub(new_dir_path + "/", "") + old_file_path = File.join(old_dir_path, relative_to_new_dir) + + old_file_relative = old_file_path.sub("#{@plugin_root}/", "") + new_file_relative = moved_file.sub("#{@plugin_root}/", "") + puts colorize( + " File moved: #{old_file_relative} -> #{new_file_relative}", + :green, + ) + @manifest << "File moved: #{old_file_relative} -> #{new_file_relative}" + end + end + else + puts colorize(" Failed to rename directory: #{old_dir_path}", :red) + end + end + end + end + + def create_migration + puts colorize("Creating database migration to copy persona tables...", :blue) + + timestamp = Time.now.strftime("%Y%m%d%H%M%S") + migration_file = + File.join(@plugin_root, "db", "migrate", "#{timestamp}_copy_persona_tables_to_agent.rb") + + migration_content = <<~RUBY + # frozen_string_literal: true + + class CopyPersonaTablesToAgent < ActiveRecord::Migration[7.0] + def up + # Copy the main table structure and data + if table_exists?(:ai_personas) && !table_exists?(:ai_agents) + execute <<~SQL + CREATE TABLE ai_agents AS + SELECT * FROM ai_personas + SQL + + # Copy indexes from ai_personas to ai_agents + execute <<~SQL + CREATE UNIQUE INDEX index_ai_agents_on_id + ON ai_agents USING btree (id) + SQL + + # Copy any other indexes that exist on ai_personas + indexes = execute(<<~SQL).to_a + SELECT indexname, indexdef + FROM pg_indexes + WHERE tablename = 'ai_personas' + AND indexname != 'ai_personas_pkey' + SQL + + indexes.each do |index| + new_index_def = index['indexdef'].gsub('ai_personas', 'ai_agents') + new_index_name = index['indexname'].gsub('ai_personas', 'ai_agents') + new_index_def = new_index_def.gsub(index['indexname'], new_index_name) + execute(new_index_def) + end + end + + # Update polymorphic associations to point to new table + execute <<~SQL + UPDATE rag_document_fragments + SET target_type = 'AiAgent' + WHERE target_type = 'AiPersona' + SQL + + execute <<~SQL + UPDATE upload_references + SET target_type = 'AiAgent' + WHERE target_type = 'AiPersona' + SQL + end + + def down + drop_table :ai_agents if table_exists?(:ai_agents) + + # Revert polymorphic associations + execute <<~SQL + UPDATE rag_document_fragments + SET target_type = 'AiPersona' + WHERE target_type = 'AiAgent' + SQL + + execute <<~SQL + UPDATE upload_references + SET target_type = 'AiPersona' + WHERE target_type = 'AiAgent' + SQL + end + end + RUBY + + FileUtils.mkdir_p(File.dirname(migration_file)) + File.write(migration_file, migration_content) + + relative_migration = migration_file.sub("#{@plugin_root}/", "") + puts colorize("Created migration file: #{relative_migration}", :green) + @manifest << "Created migration file: #{relative_migration}" + end + + def create_post_migration + puts colorize("Creating post-migration to drop old persona tables...", :blue) + + timestamp = (Time.now + 1).strftime("%Y%m%d%H%M%S") # Ensure this runs after the main migration + post_migrate_dir = File.join(@plugin_root, "db", "post_migrate") + migration_file = File.join(post_migrate_dir, "#{timestamp}_drop_persona_tables.rb") + + migration_content = <<~RUBY + # frozen_string_literal: true + + class DropPersonaTables < ActiveRecord::Migration[7.0] + def up + # Drop the old table after copying to new one + drop_table :ai_personas if table_exists?(:ai_personas) + end + + def down + raise IrreversibleMigration, "Cannot recreate dropped persona tables" + end + end + RUBY + + FileUtils.mkdir_p(post_migrate_dir) + File.write(migration_file, migration_content) + + relative_migration = migration_file.sub("#{@plugin_root}/", "") + puts colorize("Created post-migration file: #{relative_migration}", :green) + @manifest << "Created post-migration file: #{relative_migration}" + end + + def print_content_summary + puts colorize("Content replacement completed!", :green) + puts colorize("Files updated: #{@manifest.count}", :yellow) + @manifest.each { |change| puts " #{change}" } + end + + def print_migration_summary + puts colorize("Database migrations created!", :green) + puts colorize("Files created: #{@manifest.count}", :yellow) + @manifest.each { |change| puts " #{change}" } + end + + def print_summary + puts colorize("=" * 55, :blue) + puts colorize("Completed renaming 'persona' to 'agent' in the codebase", :green) + puts colorize("=" * 55, :blue) + puts colorize("Changes made:", :yellow) + @manifest.each { |change| puts " #{change}" } + puts colorize("Next steps:", :yellow) + puts "1. Review changes with 'git diff'" + puts "2. Run tests and fix any remaining issues" + puts "3. Run the database migrations (migrate, then post_migrate)" + puts colorize("=" * 55, :blue) + end +end + +# Run the renamer if this file is executed directly +PersonaToAgentRenamer.new.run if __FILE__ == $0 diff --git a/spec/fabricators/ai_persona_fabricator.rb b/spec/fabricators/ai_agent_fabricator.rb similarity index 57% rename from spec/fabricators/ai_persona_fabricator.rb rename to spec/fabricators/ai_agent_fabricator.rb index 4b3c3f02..622ba68f 100644 --- a/spec/fabricators/ai_persona_fabricator.rb +++ b/spec/fabricators/ai_agent_fabricator.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -Fabricator(:ai_persona) do - name { sequence(:name) { |i| "persona_#{i}" } } +Fabricator(:ai_agent) do + name { sequence(:name) { |i| "agent_#{i}" } } description "I am a test bot" system_prompt "You are a test bot" end diff --git a/spec/fixtures/search_meta/search.json b/spec/fixtures/search_meta/search.json index b02212a1..2cb5cf17 100644 --- a/spec/fixtures/search_meta/search.json +++ b/spec/fixtures/search_meta/search.json @@ -1 +1 @@ -{"posts":[{"id":218354,"name":"Robin Ward","username":"eviltrout","avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png","created_at":"2016-08-24T20:48:02.587Z","like_count":15,"blurb":"Automated tests are a great way to protect your code against future regressions. Many people are familiar with how to do this in our Rails codebase with http://rspec.info/ rspec , but the Javascript s...","post_number":1,"topic_title_headline":"Write acceptance tests and component tests for Ember code in Discourse","topic_id":49167},{"id":138484,"name":"Robin Ward","username":"eviltrout","avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png","created_at":"2015-08-27T21:32:26.407Z","like_count":29,"blurb":"Previous tutorial: https://meta.discourse.org/t/developing-discourse-plugins-part-5-add-an-admin-interface/31761 Developing Discourse Plugins - Part 5 - Add an admin interface Did you know that Discou...","post_number":1,"topic_title_headline":"Developing Discourse Plugins - Part 6 - Add acceptance tests","topic_id":32619},{"id":1381521,"name":"Alan Tan","username":"tgxworld","avatar_template":"/user_avatar/meta.discourse.org/tgxworld/{size}/106117_2.png","created_at":"2023-10-24T23:13:37.118Z","like_count":16,"blurb":"Writing automated tests for themes is an important part of the theme development process which can help ensure that the features being introduced by a theme continues to work well overtime with core D...","post_number":1,"topic_title_headline":"End-to-end system testing for themes and theme components","topic_id":281579},{"id":311252,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-07-26T14:09:58.126Z","like_count":17,"blurb":"Discourse has extensive frontend tests for core, plugins and themes. Once you have a functioning local development environment, those tests can be run locally in a number of different ways. Running te...","post_number":1,"topic_title_headline":"How to run Discourse core, plugin and theme QUnit test suites","topic_id":66857},{"id":59431,"name":"Erlend Sogge Heggen","username":"erlend_sh","avatar_template":"/user_avatar/meta.discourse.org/erlend_sh/{size}/119475_2.png","created_at":"2014-07-03T11:45:09.463Z","like_count":5,"blurb":"...view=1 to an URL, but the emulator has the added benefit of letting you select the screen profile of a specific device. I also tested all of the most popular online screen emulators, but unfortunately...","post_number":1,"topic_title_headline":"Test Discourse in mobile screen emulator","topic_id":17155},{"id":1419473,"name":"","username":"ToddZ","avatar_template":"/user_avatar/meta.discourse.org/toddz/{size}/328350_2.png","created_at":"2023-12-12T10:51:08.401Z","like_count":2,"blurb":"...amount of time troubleshooting inbound email because Discourse was rejecting every reply-by-email from my fake users. It had worked fine when I first tested several weeks ago… I finally realized that ...","post_number":1,"topic_title_headline":"Tip: when testing inbound email with fake user accounts…","topic_id":288363},{"id":722424,"name":"Falco","username":"Falco","avatar_template":"/user_avatar/meta.discourse.org/falco/{size}/179432_2.png","created_at":"2020-03-26T21:31:38.463Z","like_count":17,"blurb":"Continuing the discussion from https://meta.discourse.org/t/user-api-keys-specification/48536 User API keys specification : I created a small utility script in order to test User API keys locally. Fir...","post_number":1,"topic_title_headline":"Generate User API Keys for testing","topic_id":145744},{"id":266441,"name":"Andrew Waugh","username":"JagWaugh","avatar_template":"/user_avatar/meta.discourse.org/jagwaugh/{size}/69335_2.png","created_at":"2017-03-03T13:34:08.009Z","like_count":19,"blurb":"Regardless of if you're a moderator or an admin, you will no doubt at some time think about making some change to your live site and wonder if this will bring shame on you, and/or cause yourself an en...","post_number":1,"topic_title_headline":"Build a sandbox to test changes before making them live","topic_id":58298},{"id":582008,"name":"","username":"Wurzelseppi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/w/eada6e/{size}.png","created_at":"2019-05-21T10:42:51.099Z","like_count":0,"blurb":"Hi guys, just wanted to migrate from 2.3.0beta9 to stable release and got this error: What can I do here ? Caused by: PG::UndefinedColumn: ERROR: column \"email_private_messages\" of relation \"user_opti...","post_number":1,"topic_title_headline":"Migrate from tests-passed to stable","topic_id":118296},{"id":1421414,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-12-14T17:11:26.575Z","like_count":1,"blurb":"Restarting the server, restarting the container, console rebuilding doesn't help. Container is up as I can ./launcher enter app Got a bunch of these in logs, ideas on how to investigate redis failure?...","post_number":1,"topic_title_headline":"502 Bad Gateway after online rebuild of tests-passed Production just now","topic_id":288705},{"id":1204506,"name":"Coin-coin le Canapin","username":"Canapin","avatar_template":"/user_avatar/meta.discourse.org/canapin/{size}/119591_2.png","created_at":"2022-12-02T20:49:55.867Z","like_count":1,"blurb":"Hi! I want to add support of /shorts/ Youtube link. My modification of the YoutubeOnebox class works, but it is required that I add a test in https://github.com/discourse/discourse/blob/493d437e79f88a...","post_number":1,"topic_title_headline":"Trouble on adding a simple unit test for Youtube oneboxing","topic_id":247546},{"id":1339808,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-08-07T10:05:22.444Z","like_count":2,"blurb":"I have a strange issue with QUnit. This test is extremely simple and should be straightforward … but … A plugin setting is changing from those I have set up. https://github.com/paviliondev/discourse-l...","post_number":1,"topic_title_headline":"Strange QUnit behaviour?: test failing because setting value doesn’t survive","topic_id":274165},{"id":1211823,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2022-12-16T18:32:29.231Z","like_count":1,"blurb":"...presenting formatted location on User Card by merefield · Pull Request #73 · paviliondev/discourse-locations · GitHub I'm attempting to cover the change with a new Front End test. But the test fails t...","post_number":1,"topic_title_headline":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","topic_id":249167},{"id":1408389,"name":"Pierre Romera","username":"pirhoo","avatar_template":"/user_avatar/meta.discourse.org/pirhoo/{size}/120058_2.png","created_at":"2023-11-22T18:46:46.469Z","like_count":5,"blurb":"...m setting up a new plugin based on the https://github.com/discourse/discourse-plugin-skeleton/tree/main/assets skeleton you provided which already helped me a lot. I am now writing tests, both for the...","post_number":1,"topic_title_headline":"Acceptance tests failing on Github Actions","topic_id":286355},{"id":443129,"name":"JK Baseer","username":"JKBaseer","avatar_template":"/user_avatar/meta.discourse.org/jkbaseer/{size}/80471_2.png","created_at":"2018-07-01T17:12:48.446Z","like_count":0,"blurb":"...but still could myself. Background: I installed discourse using digitalocean oneclick installer. The website is running under http://forum.example.org forum.example.org without any problem except the ...","post_number":1,"topic_title_headline":"There was a problem sending the test email","topic_id":91312},{"id":1412219,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-11-29T23:18:11.815Z","like_count":1,"blurb":"I'm trying to create a foreign key relationship with the Topics table. The problem is it is failing in github workflow test environment during tests for the strangest reason, it is trying to access a ...","post_number":1,"topic_title_headline":"Strange migration error in tests during GH workflow","topic_id":287022},{"id":1160803,"name":"Bryan Joseph","username":"Bryan_Joseph","avatar_template":"/user_avatar/meta.discourse.org/bryan_joseph/{size}/273248_2.png","created_at":"2022-09-07T20:22:11.191Z","like_count":1,"blurb":"...SETTINGS ==================== DISCOURSE_HOSTNAME=url SMTP_ADDRESS=smtp.mailgun.org DEVELOPER_EMAILS=REDACTED SMTP_PASSWORD=REDACTED SMTP_PORT=2525 SMTP_USER_NAME=url LETSENCRYPT_ACCOUNT_EMAIL=REDACTED...","post_number":1,"topic_title_headline":"Smtp doctor test using port 465 even though its configured to use 2525","topic_id":238372},{"id":725228,"name":"james.network","username":"sunjam","avatar_template":"/user_avatar/meta.discourse.org/sunjam/{size}/175682_2.png","created_at":"2020-03-31T18:51:44.216Z","like_count":0,"blurb":"...Redis or updating it; it hasn't really been touched in the last 8+ months. I have not personally dealt with Redis before, but our Tests-Pass Discourse instance was setup using https://hub.docker.com/r...","post_number":1,"topic_title_headline":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","topic_id":146326},{"id":1240758,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2023-02-16T22:12:10.456Z","like_count":4,"blurb":"I see that the discourse-plugin-skeleton now has this: uses: discourse/.github/.github/workflows/discourse-plugin.yml@v1 so we don't have to keep updating stuff. But I have a plugin that requires the ...","post_number":1,"topic_title_headline":"Tests for plugin that requires a plugin","topic_id":255406},{"id":1197679,"name":"","username":"SilK","avatar_template":"/user_avatar/meta.discourse.org/silk/{size}/268124_2.png","created_at":"2022-11-18T17:03:46.056Z","like_count":0,"blurb":"...a new dev environment for working on plugins. Discourse is up to date with the main branch. I need to restart Ember in order to test changes made to the front end. This includes changes to Handlebars,...","post_number":1,"topic_title_headline":"Need to restart Ember in order to test front-end changes","topic_id":246069},{"id":1103913,"name":"Banibrata Dutta","username":"bdutta","avatar_template":"/user_avatar/meta.discourse.org/bdutta/{size}/259973_2.png","created_at":"2022-05-15T18:02:33.290Z","like_count":0,"blurb":"...only in a captive host-only testbed, so wondering if there is any local network SMTP daemon / service that I could start to complete the testing ? I'm happy with 100% command line mail client and serv...","post_number":1,"topic_title_headline":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","topic_id":227090},{"id":722608,"name":"Lona Lee","username":"Lona_Lee","avatar_template":"/user_avatar/meta.discourse.org/lona_lee/{size}/169072_2.png","created_at":"2020-03-27T07:14:10.239Z","like_count":1,"blurb":"Hello. I'm trying to get email setup working on my discourse instance. Done set-up properly and looks fine(no errors), so sent test emails. (logs confirmed : \"Admin\" - \"Emails\" - \"Sent\") However, I ha...","post_number":1,"topic_title_headline":"Test emails sent but","topic_id":145781},{"id":679950,"name":"Oleg Bovykin","username":"arrowcircle","avatar_template":"/user_avatar/meta.discourse.org/arrowcircle/{size}/100035_2.png","created_at":"2020-01-01T11:20:33.618Z","like_count":1,"blurb":"Hi! I found strange error in my admin page, that sidekiq is not running. I opened logs and found hundreds errors like: /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger...","post_number":1,"topic_title_headline":"Sidekiq heartbeat test failed, restarting","topic_id":137496},{"id":1033333,"name":"М. М.","username":"М_М","avatar_template":"/user_avatar/meta.discourse.org/м_м/{size}/243710_2.png","created_at":"2021-12-20T13:52:10.488Z","like_count":0,"blurb":"...user, the logs say like this Job exception: could not get 3xx (421: 421 Domain sandbox410fe5c7bb85483c941c05b4ec5f3495.mailgun.org is not allowed to send: Sandbox subdomains are for test purposes only...","post_number":1,"topic_title_headline":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","topic_id":212684},{"id":498559,"name":"","username":"desrocchi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/eb9ed0/{size}.png","created_at":"2018-11-14T15:20:59.607Z","like_count":0,"blurb":"Is there a way for me to see or test the admin options in the demo area? I am just a moderator on the platform we use but I would like to see which options could be of use without having to install th...","post_number":1,"topic_title_headline":"Test admin features without having to install Discourse","topic_id":102035},{"id":596557,"name":"Flaviu","username":"UnivacTwo","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/u/df705f/{size}.png","created_at":"2019-06-25T17:12:34.710Z","like_count":0,"blurb":"Let's encrypt has a limit of how many certificates can be generated in a week for the same domain. Unfortunately we reach this limit and we cannot generate a new certificate this week. We did a backup...","post_number":1,"topic_title_headline":"Install discourse with a staging (test) ssl certificate","topic_id":121299},{"id":583541,"name":"mark78","username":"Mark_Schmucker","avatar_template":"/user_avatar/meta.discourse.org/mark_schmucker/{size}/124810_2.png","created_at":"2019-05-24T00:14:35.895Z","like_count":0,"blurb":"Should I be able to run any Badge Query in https://meta.discourse.org/t/32566 Data Explorer ? I want to create a custom Badge Query using \"Appreciated\" as a starting point. I type the Appreciated quer...","post_number":1,"topic_title_headline":"Problem testing Badge Query from Data Explorer","topic_id":118568},{"id":569209,"name":"Penar Musaraj","username":"pmusaraj","avatar_template":"/user_avatar/meta.discourse.org/pmusaraj/{size}/119489_2.png","created_at":"2019-04-25T01:24:56.741Z","like_count":26,"blurb":"...device and installing the app via TestFlight: https://testflight.apple.com/join/NkdBQgmg testflight.apple.com https://testflight.apple.com/join/NkdBQgmg TestFlight - Apple Using TestFlight is a great ...","post_number":1,"topic_title_headline":"New iOS mobile app beta available for testing","topic_id":115912},{"id":1090520,"name":"Mac玩儿法","username":"waerfa","avatar_template":"/user_avatar/meta.discourse.org/waerfa/{size}/216044_2.png","created_at":"2022-04-17T21:46:04.755Z","like_count":0,"blurb":"...rebuild the container: git pull ./launcher rebuild app I got the fatal error which shows: FAILED -------------------- Pups::ExecError: cd /var/www/discourse & & git fetch --depth 1 origin tests-passed...","post_number":1,"topic_title_headline":"502 Bad Gateway after trying to rebuild test-passed branch","topic_id":224560},{"id":860593,"name":"james.network","username":"sunjam","avatar_template":"/user_avatar/meta.discourse.org/sunjam/{size}/175682_2.png","created_at":"2020-12-11T18:44:18.021Z","like_count":1,"blurb":"Continuing the discussion from https://meta.discourse.org/t/postgresql-13-update/172563/27 PostgreSQL 13 update : Run into trouble while updating 2.7.0beta1 Tests-Pass in order to remove some troubles...","post_number":1,"topic_title_headline":"Forum offline due to failed rebuilds on Tests-Pass","topic_id":173019},{"id":960683,"name":"","username":"daniyal","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/58f4c7/{size}.png","created_at":"2021-07-08T19:58:55.634Z","like_count":3,"blurb":"...which we want to experiment. An example would be to experiment different styles of topic list view. For this we are using Google Optimize A/B testing. Currently we plan to show theme without changes t...","post_number":1,"topic_title_headline":"[A/B Testing] Changing parent CSS class based on experiment variable","topic_id":196501},{"id":1389188,"name":"Angus McLeod","username":"angus","avatar_template":"/user_avatar/meta.discourse.org/angus/{size}/341715_2.png","created_at":"2023-10-20T03:44:49.737Z","like_count":7,"blurb":"I've been looking at the performance of the https://meta.discourse.org/t/activitypub-plugin/266794 ActivityPub plugin recently and considering the best ways to reliably test, and prove, performance fo...","post_number":1,"topic_title_headline":"Code-level performance testing","topic_id":282856},{"id":1329017,"name":"","username":"dodibi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/9fc348/{size}.png","created_at":"2023-07-19T12:45:57.446Z","like_count":0,"blurb":"Hello everyone! I'm currently facing some challenges while configuring my local environment to run discourse tests in a docker container. My main objective is to run the core tests with plugins attach...","post_number":1,"topic_title_headline":"Running core tests in docker environment","topic_id":272112},{"id":421687,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2018-05-11T22:03:23.232Z","like_count":0,"blurb":"Is there a way to initiate an email test from the Rails console? For a zillion reasons I would love to be able to send a test email without having to create an account. I've looked in config/routes.rb...","post_number":1,"topic_title_headline":"Email test from the console?","topic_id":87295},{"id":272436,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-03-20T20:29:06.608Z","like_count":1,"blurb":"It is my understanding that running rake qunit:test should run all of the qunit tests in Discourse, including those for any installed plugins. However, when I run the task in the docker development en...","post_number":1,"topic_title_headline":"Plugin QUnit tests are not running as part of rake qunit:test","topic_id":59577},{"id":899205,"name":"","username":"JQ331","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/j/41988e/{size}.png","created_at":"2021-03-03T18:09:29.452Z","like_count":6,"blurb":"I came across this https://blog.codinghorror.com/low-fi-usability-testing/ excellent article on how to do low-fi usability testing by @codinghorror . Usability testing (and user testing in general) is...","post_number":1,"topic_title_headline":"How does the Discourse team do usability testing?","topic_id":181856},{"id":530438,"name":"","username":"kleinfreund","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/k/a6a055/{size}.png","created_at":"2019-02-01T09:52:27.150Z","like_count":1,"blurb":"One can write tests for the backend of a plugin. For example, I created the following file in my plugin directory: spec/lib/route_store_spec.rb : require 'rails_helper' describe MyPlugin::RouteStore d...","post_number":1,"topic_title_headline":"Advice on writing Ruby tests for plugins","topic_id":108110},{"id":260691,"name":"Rimian Perkins","username":"rimian","avatar_template":"/user_avatar/meta.discourse.org/rimian/{size}/120658_2.png","created_at":"2017-02-13T02:48:10.177Z","like_count":0,"blurb":"What's the best way to (QUnit) assert an element on the page has some content in it? This passes: ok($.trim($('.foo').text()) == 'bar', 'content bar renders on page'); But isn't very practical. Is the...","post_number":1,"topic_title_headline":"Acceptance test content is present on page","topic_id":57292},{"id":1432567,"name":"Ayke","username":"rrit","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/{size}.png","created_at":"2024-01-09T16:53:57.916Z","like_count":0,"blurb":"Right now Discourse on meta.discourse.org serves the mobile-view instead of the crawler-view to the https://search.google.com/test/rich-results Google Rich Results Test . As there is no Schema Markup ...","post_number":1,"topic_title_headline":"Google Search Console/Schema Markup test tool “Google Rich Results Test”: mobile-view instead of crawler-view","topic_id":291039},{"id":1305996,"name":"Larry Diehl","username":"larrytheliquid","avatar_template":"/user_avatar/meta.discourse.org/larrytheliquid/{size}/310576_2.png","created_at":"2023-06-08T22:35:51.914Z","like_count":1,"blurb":"Hi Discourse Community! :slight_smile: I've been working on tech ( https://colimit.io Colimit ) that helps people apply https://en.wikipedia.org/wiki/Model-based_testing Model-based testing to test th...","post_number":1,"topic_title_headline":"Experiments with Model-Based Testing","topic_id":267737},{"id":1135474,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2022-07-20T08:48:28.868Z","like_count":0,"blurb":"...my case running: rake \"plugin:qunit[discourse-multilingual]\" with a branch installed. I'm declaring a function in my initializer (i'm extending I18n ) The tests sometimes (25%?) seem to run before the...","post_number":1,"topic_title_headline":"Qunit tests not deterministic in Plugin?","topic_id":233389},{"id":672801,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2019-12-12T21:01:31.303Z","like_count":0,"blurb":"Thanks, @Mittineague ! After a while, that made sense. I even wrote a spec, but even before I added my spec (and when I reverted to before I added any code), specs fail because: An error occurred whil...","post_number":1,"topic_title_headline":"Issues migrating test database","topic_id":135876},{"id":871105,"name":"","username":"Alteras","avatar_template":"/user_avatar/meta.discourse.org/alteras/{size}/179824_2.png","created_at":"2021-01-07T19:56:05.071Z","like_count":4,"blurb":"Hello! I'm currently working on a Markdown Extension/plugin that adds quite a number of BBCode tags, and I am looking to write QUnit Acceptance tests for them (I got really tired of constantly checkin...","post_number":1,"topic_title_headline":"Acceptance Test for Markdown Extension?","topic_id":175413},{"id":747613,"name":"","username":"xrav3nz","avatar_template":"/user_avatar/meta.discourse.org/xrav3nz/{size}/76894_2.png","created_at":"2020-05-08T04:27:56.920Z","like_count":8,"blurb":"...development - #2 by taylorthurlow - A May Of WTFs - Ruby on Rails Discussions Not sure if we have explored this before, but Rails can automatically maintain test databse schema with ActiveRecord::Migr...","post_number":1,"topic_title_headline":"Auto migrate test database schema","topic_id":150786},{"id":583961,"name":"Kim Miller","username":"kimardenmiller","avatar_template":"/user_avatar/meta.discourse.org/kimardenmiller/{size}/119631_2.png","created_at":"2019-05-24T22:21:24.996Z","like_count":3,"blurb":"Adding some polls API endpoints for PR to discourse_api, which work fine. Now I'm trying to understand how to create tests before submitting the PR, e.g.: require 'spec_helper' describe DiscourseApi::...","post_number":1,"topic_title_headline":"Building Tests for New discourse_api Endpoints","topic_id":118639},{"id":621707,"name":"Andrew Lank","username":"alank","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/a/c89c15/{size}.png","created_at":"2019-08-17T02:13:06.679Z","like_count":1,"blurb":"In my development and testing I'm running a Discourse instance (Docker Discourse from Bitnami) and it fulfills most of my API testing for our API service which talks to Discourse, however I now need t...","post_number":1,"topic_title_headline":"Seed or API calls to create test users","topic_id":126025},{"id":334186,"name":"Chris","username":"ChrisBeach","avatar_template":"/user_avatar/meta.discourse.org/chrisbeach/{size}/214628_2.png","created_at":"2017-10-01T08:18:48.148Z","like_count":1,"blurb":"...from the core team. I propose that on hitting the upgrade button, a new docker image is built in the background, and within it, acceptance tests of all plugins are run before the switch-over happens f...","post_number":1,"topic_title_headline":"Smoke-testing plugins during upgrade process","topic_id":71118},{"id":288991,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-05-16T10:28:58.496Z","like_count":1,"blurb":"I'm trying to use the docker image for tests both on my mac, and also https://meta.discourse.org/t/setting-up-plugin-continuous-integration-tests-on-travis-ci/59612 on travis . For a while now the qun...","post_number":1,"topic_title_headline":"QUnit tests won’t pass in discourse_dev docker image","topic_id":62797},{"id":187533,"name":"Sckott","username":"sckott","avatar_template":"/user_avatar/meta.discourse.org/sckott/{size}/115359_2.png","created_at":"2016-04-21T19:15:00.191Z","like_count":0,"blurb":"What's the best or fastest way to get Discourse installed on Travis for testing a client for the Discourse API ? It appears as though the discourse_api gem uses webmock so I think does not use a real ...","post_number":1,"topic_title_headline":"Testing a Discourse API client on Travis-CI","topic_id":42947},{"id":966756,"name":"Connor Parrish","username":"Connor_Parrish","avatar_template":"/user_avatar/meta.discourse.org/connor_parrish/{size}/225463_2.png","created_at":"2021-07-22T16:57:05.885Z","like_count":1,"blurb":"When you're conditionally adding a PostMenuButton using the plugin-api , the extra button is included in _extraButtons in between acceptance tests. When I run tests, if the tests where the button shou...","post_number":1,"topic_title_headline":"PostMenu’s ‘_extraButtons’ isn’t reset in between acceptance tests","topic_id":197887}],"topics":[{"id":49167,"title":"Write acceptance tests and component tests for Ember code in Discourse","fancy_title":"Write acceptance tests and component tests for Ember code in Discourse","slug":"write-acceptance-tests-and-component-tests-for-ember-code-in-discourse","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2016-08-24T20:48:02.492Z","last_posted_at":"2017-02-01T18:22:01.859Z","bumped":true,"bumped_at":"2017-02-01T18:22:01.859Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["tutorial","ember","testing"],"tags_descriptions":{},"category_id":56,"has_accepted_answer":false},{"id":32619,"title":"Developing Discourse Plugins - Part 6 - Add acceptance tests","fancy_title":"Developing Discourse Plugins - Part 6 - Add acceptance tests","slug":"developing-discourse-plugins-part-6-add-acceptance-tests","posts_count":33,"reply_count":26,"highest_post_number":38,"created_at":"2015-08-27T21:32:26.323Z","last_posted_at":"2022-06-02T11:06:38.274Z","bumped":true,"bumped_at":"2022-06-02T11:06:38.274Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["plugins","tutorial","plugin-guides","testing"],"tags_descriptions":{"plugins":""},"category_id":56,"has_accepted_answer":false},{"id":281579,"title":"End-to-end system testing for themes and theme components","fancy_title":"End-to-end system testing for themes and theme components","slug":"end-to-end-system-testing-for-themes-and-theme-components","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-10-24T23:13:37.118Z","last_posted_at":"2023-10-24T23:13:37.118Z","bumped":true,"bumped_at":"2023-11-13T23:20:01.596Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to","themes"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":66857,"title":"How to run Discourse core, plugin and theme QUnit test suites","fancy_title":"How to run Discourse core, plugin and theme QUnit test suites","slug":"how-to-run-discourse-core-plugin-and-theme-qunit-test-suites","posts_count":1,"reply_count":2,"highest_post_number":1,"created_at":"2017-07-26T14:09:58.032Z","last_posted_at":"2017-07-26T14:09:58.126Z","bumped":true,"bumped_at":"2023-09-04T17:56:33.079Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":17155,"title":"Test Discourse in mobile screen emulator","fancy_title":"Test Discourse in mobile screen emulator","slug":"test-discourse-in-mobile-screen-emulator","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2014-07-03T11:45:09.360Z","last_posted_at":"2014-10-12T22:19:24.137Z","bumped":true,"bumped_at":"2014-10-12T22:19:24.137Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":288363,"title":"Tip: when testing inbound email with fake user accounts...","fancy_title":"Tip: when testing inbound email with fake user accounts…","slug":"tip-when-testing-inbound-email-with-fake-user-accounts","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-12-12T10:51:07.868Z","last_posted_at":"2023-12-12T10:51:08.401Z","bumped":true,"bumped_at":"2023-12-12T10:51:08.401Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["email"],"tags_descriptions":{},"category_id":55,"has_accepted_answer":false},{"id":145744,"title":"Generate User API Keys for testing","fancy_title":"Generate User API Keys for testing","slug":"generate-user-api-keys-for-testing","posts_count":4,"reply_count":1,"highest_post_number":4,"created_at":"2020-03-26T21:31:38.269Z","last_posted_at":"2022-08-07T15:27:18.788Z","bumped":true,"bumped_at":"2022-08-07T15:27:18.788Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":58298,"title":"Build a sandbox to test changes before making them live","fancy_title":"Build a sandbox to test changes before making them live","slug":"build-a-sandbox-to-test-changes-before-making-them-live","posts_count":21,"reply_count":13,"highest_post_number":21,"created_at":"2017-03-03T13:34:07.921Z","last_posted_at":"2022-02-16T23:18:54.680Z","bumped":true,"bumped_at":"2022-02-16T23:18:54.680Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":55,"has_accepted_answer":false},{"id":118296,"title":"Migrate from tests-passed to stable","fancy_title":"Migrate from tests-passed to stable","slug":"migrate-from-tests-passed-to-stable","posts_count":12,"reply_count":7,"highest_post_number":12,"created_at":"2019-05-21T10:42:51.017Z","last_posted_at":"2019-06-21T15:55:53.072Z","bumped":true,"bumped_at":"2019-05-22T15:55:47.651Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":288705,"title":"502 Bad Gateway after online rebuild of tests-passed Production just now","fancy_title":"502 Bad Gateway after online rebuild of tests-passed Production just now","slug":"502-bad-gateway-after-online-rebuild-of-tests-passed-production-just-now","posts_count":6,"reply_count":1,"highest_post_number":6,"created_at":"2023-12-14T17:11:26.387Z","last_posted_at":"2024-01-13T17:37:27.535Z","bumped":true,"bumped_at":"2023-12-14T17:36:57.066Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":247546,"title":"Trouble on adding a simple unit test for Youtube oneboxing","fancy_title":"Trouble on adding a simple unit test for Youtube oneboxing","slug":"trouble-on-adding-a-simple-unit-test-for-youtube-oneboxing","posts_count":10,"reply_count":5,"highest_post_number":10,"created_at":"2022-12-02T20:49:55.713Z","last_posted_at":"2023-01-05T17:47:40.629Z","bumped":true,"bumped_at":"2022-12-06T17:47:28.771Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["onebox","testing"],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":274165,"title":"Strange QUnit behaviour?: test failing because setting value doesn't survive","fancy_title":"Strange QUnit behaviour?: test failing because setting value doesn’t survive","slug":"strange-qunit-behaviour-test-failing-because-setting-value-doesnt-survive","posts_count":5,"reply_count":2,"highest_post_number":5,"created_at":"2023-08-07T10:05:22.298Z","last_posted_at":"2023-09-06T11:19:24.509Z","bumped":true,"bumped_at":"2023-08-07T11:24:27.150Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":249167,"title":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","fancy_title":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","slug":"is-it-possible-to-override-the-site-object-with-own-fixture-during-front-end-tests-of-a-plugin","posts_count":4,"reply_count":0,"highest_post_number":4,"created_at":"2022-12-16T18:32:29.079Z","last_posted_at":"2022-12-28T21:57:56.070Z","bumped":true,"bumped_at":"2022-12-28T21:57:56.070Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":286355,"title":"Acceptance tests failing on Github Actions","fancy_title":"Acceptance tests failing on Github Actions","slug":"acceptance-tests-failing-on-github-actions","posts_count":6,"reply_count":0,"highest_post_number":6,"created_at":"2023-11-22T18:46:46.128Z","last_posted_at":"2023-11-24T13:59:40.100Z","bumped":true,"bumped_at":"2023-11-24T13:59:40.100Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":91312,"title":"There was a problem sending the test email","fancy_title":"There was a problem sending the test email","slug":"there-was-a-problem-sending-the-test-email","posts_count":8,"reply_count":5,"highest_post_number":8,"created_at":"2018-07-01T17:12:48.366Z","last_posted_at":"2018-08-01T09:28:21.935Z","bumped":true,"bumped_at":"2018-07-02T09:28:16.014Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":287022,"title":"Strange migration error in tests during GH workflow","fancy_title":"Strange migration error in tests during GH workflow","slug":"strange-migration-error-in-tests-during-gh-workflow","posts_count":6,"reply_count":3,"highest_post_number":6,"created_at":"2023-11-29T23:18:11.686Z","last_posted_at":"2023-12-31T15:09:05.633Z","bumped":true,"bumped_at":"2023-12-01T15:08:31.257Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":238372,"title":"Smtp doctor test using port 465 even though its configured to use 2525","fancy_title":"Smtp doctor test using port 465 even though its configured to use 2525","slug":"smtp-doctor-test-using-port-465-even-though-its-configured-to-use-2525","posts_count":12,"reply_count":7,"highest_post_number":12,"created_at":"2022-09-07T20:22:10.976Z","last_posted_at":"2022-10-09T00:21:55.656Z","bumped":true,"bumped_at":"2022-09-09T00:21:48.272Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":146326,"title":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","fancy_title":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","slug":"sidekiq-not-running-sidekiq-heartbeat-test-failed-restarting","posts_count":16,"reply_count":9,"highest_post_number":16,"created_at":"2020-03-31T18:51:44.061Z","last_posted_at":"2020-06-10T01:39:28.366Z","bumped":true,"bumped_at":"2020-05-11T01:39:26.054Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["unsupported-install"],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":255406,"title":"Tests for plugin that requires a plugin","fancy_title":"Tests for plugin that requires a plugin","slug":"tests-for-plugin-that-requires-a-plugin","posts_count":3,"reply_count":0,"highest_post_number":3,"created_at":"2023-02-16T22:12:10.344Z","last_posted_at":"2023-02-28T16:10:36.498Z","bumped":true,"bumped_at":"2023-02-28T20:21:06.122Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":246069,"title":"Need to restart Ember in order to test front-end changes","fancy_title":"Need to restart Ember in order to test front-end changes","slug":"need-to-restart-ember-in-order-to-test-front-end-changes","posts_count":5,"reply_count":1,"highest_post_number":5,"created_at":"2022-11-18T17:03:45.900Z","last_posted_at":"2022-11-18T18:17:53.648Z","bumped":true,"bumped_at":"2022-11-18T18:17:53.648Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":227090,"title":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","fancy_title":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","slug":"bitnami-discourse-vm-on-virtualbox-smtp-mail-server-for-testing","posts_count":3,"reply_count":0,"highest_post_number":3,"created_at":"2022-05-15T18:02:33.136Z","last_posted_at":"2022-05-16T09:10:43.794Z","bumped":true,"bumped_at":"2022-05-16T09:10:43.794Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["unsupported-install"],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":145781,"title":"Test emails sent but","fancy_title":"Test emails sent but","slug":"test-emails-sent-but","posts_count":5,"reply_count":2,"highest_post_number":5,"created_at":"2020-03-27T07:14:10.116Z","last_posted_at":"2020-04-29T02:52:31.927Z","bumped":true,"bumped_at":"2020-03-30T02:52:29.062Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":137496,"title":"Sidekiq heartbeat test failed, restarting","fancy_title":"Sidekiq heartbeat test failed, restarting","slug":"sidekiq-heartbeat-test-failed-restarting","posts_count":13,"reply_count":8,"highest_post_number":13,"created_at":"2020-01-01T11:20:33.492Z","last_posted_at":"2020-02-11T23:09:42.375Z","bumped":true,"bumped_at":"2020-01-12T23:09:39.730Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":212684,"title":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","fancy_title":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","slug":"mailgun-discourse-sandbox-subdomains-are-for-test-purposes-only-please-add-your-own-domain","posts_count":4,"reply_count":2,"highest_post_number":5,"created_at":"2021-12-20T13:52:10.405Z","last_posted_at":"2022-01-19T15:27:11.368Z","bumped":true,"bumped_at":"2021-12-20T15:26:24.911Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":102035,"title":"Test admin features without having to install Discourse","fancy_title":"Test admin features without having to install Discourse","slug":"test-admin-features-without-having-to-install-discourse","posts_count":6,"reply_count":2,"highest_post_number":6,"created_at":"2018-11-14T15:20:59.484Z","last_posted_at":"2021-09-09T07:35:18.270Z","bumped":true,"bumped_at":"2021-09-09T07:35:18.270Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":121299,"title":"Install discourse with a staging (test) ssl certificate","fancy_title":"Install discourse with a staging (test) ssl certificate","slug":"install-discourse-with-a-staging-test-ssl-certificate","posts_count":5,"reply_count":1,"highest_post_number":5,"created_at":"2019-06-25T17:12:34.552Z","last_posted_at":"2023-04-01T03:25:32.925Z","bumped":true,"bumped_at":"2019-09-04T15:15:31.153Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":118568,"title":"Problem testing Badge Query from Data Explorer","fancy_title":"Problem testing Badge Query from Data Explorer","slug":"problem-testing-badge-query-from-data-explorer","posts_count":5,"reply_count":1,"highest_post_number":6,"created_at":"2019-05-24T00:14:35.814Z","last_posted_at":"2019-05-24T00:46:42.560Z","bumped":true,"bumped_at":"2019-05-24T00:46:42.560Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["sql-triggered-badge"],"tags_descriptions":{"sql-triggered-badge":"SQL queries for custom triggered badges"},"category_id":148,"has_accepted_answer":true},{"id":115912,"title":"New iOS mobile app beta available for testing","fancy_title":"New iOS mobile app beta available for testing","slug":"new-ios-mobile-app-beta-available-for-testing","posts_count":49,"reply_count":31,"highest_post_number":49,"created_at":"2019-04-25T01:24:56.608Z","last_posted_at":"2019-05-31T17:31:02.516Z","bumped":true,"bumped_at":"2020-01-21T17:07:37.817Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":224560,"title":"502 Bad Gateway after trying to rebuild test-passed branch","fancy_title":"502 Bad Gateway after trying to rebuild test-passed branch","slug":"502-bad-gateway-after-trying-to-rebuild-test-passed-branch","posts_count":6,"reply_count":1,"highest_post_number":6,"created_at":"2022-04-17T21:46:04.598Z","last_posted_at":"2022-04-17T22:15:37.255Z","bumped":true,"bumped_at":"2022-04-17T22:15:37.255Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":false},{"id":173019,"title":"Forum offline due to failed rebuilds on Tests-Pass","fancy_title":"Forum offline due to failed rebuilds on Tests-Pass","slug":"forum-offline-due-to-failed-rebuilds-on-tests-pass","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2020-12-11T18:44:17.952Z","last_posted_at":"2020-12-11T19:12:11.141Z","bumped":true,"bumped_at":"2020-12-11T19:12:11.141Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":false},{"id":196501,"title":"[A/B Testing] Changing parent CSS class based on experiment variable","fancy_title":"[A/B Testing] Changing parent CSS class based on experiment variable","slug":"a-b-testing-changing-parent-css-class-based-on-experiment-variable","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2021-07-08T19:58:55.503Z","last_posted_at":"2021-07-15T18:19:21.092Z","bumped":true,"bumped_at":"2021-07-15T18:19:21.092Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":282856,"title":"Code-level performance testing","fancy_title":"Code-level performance testing","slug":"code-level-performance-testing","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2023-10-20T03:44:49.568Z","last_posted_at":"2023-10-23T23:29:59.741Z","bumped":true,"bumped_at":"2023-10-23T23:29:59.741Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":272112,"title":"Running core tests in docker environment","fancy_title":"Running core tests in docker environment","slug":"running-core-tests-in-docker-environment","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-07-19T12:45:57.266Z","last_posted_at":"2023-07-19T12:45:57.446Z","bumped":true,"bumped_at":"2023-07-19T12:45:57.446Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["docker","spec","testing"],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":87295,"title":"Email test from the console?","fancy_title":"Email test from the console?","slug":"email-test-from-the-console","posts_count":4,"reply_count":1,"highest_post_number":4,"created_at":"2018-05-11T22:03:23.101Z","last_posted_at":"2018-05-12T00:55:41.843Z","bumped":true,"bumped_at":"2018-05-12T00:55:41.843Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":59577,"title":"Plugin QUnit tests are not running as part of rake qunit:test","fancy_title":"Plugin QUnit tests are not running as part of rake qunit:test","slug":"plugin-qunit-tests-are-not-running-as-part-of-rake-qunit-test","posts_count":8,"reply_count":5,"highest_post_number":8,"created_at":"2017-03-20T20:29:06.536Z","last_posted_at":"2017-07-17T18:26:45.188Z","bumped":true,"bumped_at":"2017-07-17T18:26:45.188Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":181856,"title":"How does the Discourse team do usability testing?","fancy_title":"How does the Discourse team do usability testing?","slug":"how-does-the-discourse-team-do-usability-testing","posts_count":5,"reply_count":2,"highest_post_number":6,"created_at":"2021-03-03T18:09:29.357Z","last_posted_at":"2021-03-04T15:23:46.571Z","bumped":true,"bumped_at":"2021-03-04T15:23:46.571Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":3,"has_accepted_answer":false},{"id":108110,"title":"Advice on writing Ruby tests for plugins","fancy_title":"Advice on writing Ruby tests for plugins","slug":"advice-on-writing-ruby-tests-for-plugins","posts_count":15,"reply_count":13,"highest_post_number":15,"created_at":"2019-02-01T09:52:27.051Z","last_posted_at":"2019-05-03T03:54:47.163Z","bumped":true,"bumped_at":"2019-05-03T03:54:47.163Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":57292,"title":"Acceptance test content is present on page","fancy_title":"Acceptance test content is present on page","slug":"acceptance-test-content-is-present-on-page","posts_count":7,"reply_count":3,"highest_post_number":7,"created_at":"2017-02-13T02:48:10.108Z","last_posted_at":"2017-02-14T05:55:31.520Z","bumped":true,"bumped_at":"2017-02-14T05:55:31.520Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":291039,"title":"Google Search Console/Schema Markup test tool \"Google Rich Results Test\": mobile-view instead of crawler-view","fancy_title":"Google Search Console/Schema Markup test tool “Google Rich Results Test”: mobile-view instead of crawler-view","slug":"google-search-console-schema-markup-test-tool-google-rich-results-test-mobile-view-instead-of-crawler-view","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2024-01-09T16:53:57.797Z","last_posted_at":"2024-01-09T17:17:09.039Z","bumped":true,"bumped_at":"2024-01-09T17:17:09.039Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":1,"has_accepted_answer":false},{"id":267737,"title":"Experiments with Model-Based Testing","fancy_title":"Experiments with Model-Based Testing","slug":"experiments-with-model-based-testing","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-06-08T22:35:51.784Z","last_posted_at":"2023-06-08T22:35:51.914Z","bumped":true,"bumped_at":"2023-06-08T22:35:51.914Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":233389,"title":"Qunit tests not deterministic in Plugin?","fancy_title":"Qunit tests not deterministic in Plugin?","slug":"qunit-tests-not-deterministic-in-plugin","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2022-07-20T08:48:28.757Z","last_posted_at":"2022-07-20T11:03:42.522Z","bumped":true,"bumped_at":"2022-07-20T11:03:42.522Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":135876,"title":"Issues migrating test database","fancy_title":"Issues migrating test database","slug":"issues-migrating-test-database","posts_count":9,"reply_count":1,"highest_post_number":9,"created_at":"2019-12-12T21:01:31.303Z","last_posted_at":"2021-03-02T16:44:20.120Z","bumped":true,"bumped_at":"2021-03-02T16:44:20.120Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":175413,"title":"Acceptance Test for Markdown Extension?","fancy_title":"Acceptance Test for Markdown Extension?","slug":"acceptance-test-for-markdown-extension","posts_count":3,"reply_count":0,"highest_post_number":4,"created_at":"2021-01-07T19:56:04.931Z","last_posted_at":"2021-01-10T11:26:16.888Z","bumped":true,"bumped_at":"2021-01-10T16:30:56.164Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":150786,"title":"Auto migrate test database schema","fancy_title":"Auto migrate test database schema","slug":"auto-migrate-test-database-schema","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2020-05-08T04:27:56.739Z","last_posted_at":"2020-05-08T13:15:45.247Z","bumped":true,"bumped_at":"2020-05-08T13:15:45.247Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":118639,"title":"Building Tests for New discourse_api Endpoints","fancy_title":"Building Tests for New discourse_api Endpoints","slug":"building-tests-for-new-discourse-api-endpoints","posts_count":20,"reply_count":9,"highest_post_number":21,"created_at":"2019-05-24T22:21:24.896Z","last_posted_at":"2019-10-02T21:13:35.140Z","bumped":true,"bumped_at":"2019-10-02T21:36:26.639Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":126025,"title":"Seed or API calls to create test users","fancy_title":"Seed or API calls to create test users","slug":"seed-or-api-calls-to-create-test-users","posts_count":7,"reply_count":5,"highest_post_number":7,"created_at":"2019-08-17T02:13:06.578Z","last_posted_at":"2019-08-19T11:50:36.137Z","bumped":true,"bumped_at":"2019-08-19T11:50:36.137Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":71118,"title":"Smoke-testing plugins during upgrade process","fancy_title":"Smoke-testing plugins during upgrade process","slug":"smoke-testing-plugins-during-upgrade-process","posts_count":22,"reply_count":17,"highest_post_number":22,"created_at":"2017-10-01T08:18:48.067Z","last_posted_at":"2017-10-02T23:44:59.852Z","bumped":true,"bumped_at":"2017-10-02T23:44:59.852Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["pr-welcome"],"tags_descriptions":{"pr-welcome":"You're welcome to submit a Github pull request that implements this"},"category_id":2,"has_accepted_answer":false},{"id":62797,"title":"QUnit tests won't pass in discourse_dev docker image","fancy_title":"QUnit tests won’t pass in discourse_dev docker image","slug":"qunit-tests-wont-pass-in-discourse-dev-docker-image","posts_count":20,"reply_count":11,"highest_post_number":21,"created_at":"2017-05-16T10:28:58.397Z","last_posted_at":"2017-07-25T15:50:27.716Z","bumped":true,"bumped_at":"2017-07-25T15:50:27.716Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":42947,"title":"Testing a Discourse API client on Travis-CI","fancy_title":"Testing a Discourse API client on Travis-CI","slug":"testing-a-discourse-api-client-on-travis-ci","posts_count":5,"reply_count":3,"highest_post_number":5,"created_at":"2016-04-21T19:15:00.130Z","last_posted_at":"2016-04-22T03:03:47.976Z","bumped":true,"bumped_at":"2016-04-22T03:03:47.976Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":197887,"title":"PostMenu's '_extraButtons' isn't reset in between acceptance tests","fancy_title":"PostMenu’s ‘_extraButtons’ isn’t reset in between acceptance tests","slug":"postmenus-extrabuttons-isnt-reset-in-between-acceptance-tests","posts_count":2,"reply_count":0,"highest_post_number":4,"created_at":"2021-07-22T16:57:05.803Z","last_posted_at":"2021-08-04T09:11:29.538Z","bumped":true,"bumped_at":"2021-08-04T09:11:29.538Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false}],"users":[],"categories":[],"tags":[],"groups":[],"grouped_search_result":{"more_posts":null,"more_users":null,"more_categories":null,"term":"testing","search_log_id":2089836,"more_full_page_results":true,"can_create_topic":false,"error":null,"post_ids":[218354,138484,1381521,311252,59431,1419473,722424,266441,582008,1421414,1204506,1339808,1211823,1408389,443129,1412219,1160803,725228,1240758,1197679,1103913,722608,679950,1033333,498559,596557,583541,569209,1090520,860593,960683,1389188,1329017,421687,272436,899205,530438,260691,1432567,1305996,1135474,672801,871105,747613,583961,621707,334186,288991,187533,966756],"user_ids":[],"category_ids":[],"tag_ids":[],"group_ids":[]}} \ No newline at end of file +{"posts":[{"id":218354,"name":"Robin Ward","username":"eviltrout","avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png","created_at":"2016-08-24T20:48:02.587Z","like_count":15,"blurb":"Automated tests are a great way to protect your code against future regressions. Many people are familiar with how to do this in our Rails codebase with http://rspec.info/ rspec , but the Javascript s...","post_number":1,"topic_title_headline":"Write acceptance tests and component tests for Ember code in Discourse","topic_id":49167},{"id":138484,"name":"Robin Ward","username":"eviltrout","avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png","created_at":"2015-08-27T21:32:26.407Z","like_count":29,"blurb":"Previous tutorial: https://meta.discourse.org/t/developing-discourse-plugins-part-5-add-an-admin-interface/31761 Developing Discourse Plugins - Part 5 - Add an admin interface Did you know that Discou...","post_number":1,"topic_title_headline":"Developing Discourse Plugins - Part 6 - Add acceptance tests","topic_id":32619},{"id":1381521,"name":"Alan Tan","username":"tgxworld","avatar_template":"/user_avatar/meta.discourse.org/tgxworld/{size}/106117_2.png","created_at":"2023-10-24T23:13:37.118Z","like_count":16,"blurb":"Writing automated tests for themes is an important part of the theme development process which can help ensure that the features being introduced by a theme continues to work well overtime with core D...","post_number":1,"topic_title_headline":"End-to-end system testing for themes and theme components","topic_id":281579},{"id":311252,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-07-26T14:09:58.126Z","like_count":17,"blurb":"Discourse has extensive frontend tests for core, plugins and themes. Once you have a functioning local development environment, those tests can be run locally in a number of different ways. Running te...","post_number":1,"topic_title_headline":"How to run Discourse core, plugin and theme QUnit test suites","topic_id":66857},{"id":59431,"name":"Erlend Sogge Heggen","username":"erlend_sh","avatar_template":"/user_avatar/meta.discourse.org/erlend_sh/{size}/119475_2.png","created_at":"2014-07-03T11:45:09.463Z","like_count":5,"blurb":"...view=1 to an URL, but the emulator has the added benefit of letting you select the screen profile of a specific device. I also tested all of the most popular online screen emulators, but unfortunately...","post_number":1,"topic_title_headline":"Test Discourse in mobile screen emulator","topic_id":17155},{"id":1419473,"name":"","username":"ToddZ","avatar_template":"/user_avatar/meta.discourse.org/toddz/{size}/328350_2.png","created_at":"2023-12-12T10:51:08.401Z","like_count":2,"blurb":"...amount of time troubleshooting inbound email because Discourse was rejecting every reply-by-email from my fake users. It had worked fine when I first tested several weeks ago… I finally realized that ...","post_number":1,"topic_title_headline":"Tip: when testing inbound email with fake user accounts…","topic_id":288363},{"id":722424,"name":"Falco","username":"Falco","avatar_template":"/user_avatar/meta.discourse.org/falco/{size}/179432_2.png","created_at":"2020-03-26T21:31:38.463Z","like_count":17,"blurb":"Continuing the discussion from https://meta.discourse.org/t/user-api-keys-specification/48536 User API keys specification : I created a small utility script in order to test User API keys locally. Fir...","post_number":1,"topic_title_headline":"Generate User API Keys for testing","topic_id":145744},{"id":266441,"name":"Andrew Waugh","username":"JagWaugh","avatar_template":"/user_avatar/meta.discourse.org/jagwaugh/{size}/69335_2.png","created_at":"2017-03-03T13:34:08.009Z","like_count":19,"blurb":"Regardless of if you're a moderator or an admin, you will no doubt at some time think about making some change to your live site and wonder if this will bring shame on you, and/or cause yourself an en...","post_number":1,"topic_title_headline":"Build a sandbox to test changes before making them live","topic_id":58298},{"id":582008,"name":"","username":"Wurzelseppi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/w/eada6e/{size}.png","created_at":"2019-05-21T10:42:51.099Z","like_count":0,"blurb":"Hi guys, just wanted to migrate from 2.3.0beta9 to stable release and got this error: What can I do here ? Caused by: PG::UndefinedColumn: ERROR: column \"email_private_messages\" of relation \"user_opti...","post_number":1,"topic_title_headline":"Migrate from tests-passed to stable","topic_id":118296},{"id":1421414,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-12-14T17:11:26.575Z","like_count":1,"blurb":"Restarting the server, restarting the container, console rebuilding doesn't help. Container is up as I can ./launcher enter app Got a bunch of these in logs, ideas on how to investigate redis failure?...","post_number":1,"topic_title_headline":"502 Bad Gateway after online rebuild of tests-passed Production just now","topic_id":288705},{"id":1204506,"name":"Coin-coin le Canapin","username":"Canapin","avatar_template":"/user_avatar/meta.discourse.org/canapin/{size}/119591_2.png","created_at":"2022-12-02T20:49:55.867Z","like_count":1,"blurb":"Hi! I want to add support of /shorts/ Youtube link. My modification of the YoutubeOnebox class works, but it is required that I add a test in https://github.com/discourse/discourse/blob/493d437e79f88a...","post_number":1,"topic_title_headline":"Trouble on adding a simple unit test for Youtube oneboxing","topic_id":247546},{"id":1339808,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-08-07T10:05:22.444Z","like_count":2,"blurb":"I have a strange issue with QUnit. This test is extremely simple and should be straightforward … but … A plugin setting is changing from those I have set up. https://github.com/paviliondev/discourse-l...","post_number":1,"topic_title_headline":"Strange QUnit behaviour?: test failing because setting value doesn’t survive","topic_id":274165},{"id":1211823,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2022-12-16T18:32:29.231Z","like_count":1,"blurb":"...presenting formatted location on User Card by merefield · Pull Request #73 · paviliondev/discourse-locations · GitHub I'm attempting to cover the change with a new Front End test. But the test fails t...","post_number":1,"topic_title_headline":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","topic_id":249167},{"id":1408389,"name":"Pierre Romera","username":"pirhoo","avatar_template":"/user_avatar/meta.discourse.org/pirhoo/{size}/120058_2.png","created_at":"2023-11-22T18:46:46.469Z","like_count":5,"blurb":"...m setting up a new plugin based on the https://github.com/discourse/discourse-plugin-skeleton/tree/main/assets skeleton you provided which already helped me a lot. I am now writing tests, both for the...","post_number":1,"topic_title_headline":"Acceptance tests failing on Github Actions","topic_id":286355},{"id":443129,"name":"JK Baseer","username":"JKBaseer","avatar_template":"/user_avatar/meta.discourse.org/jkbaseer/{size}/80471_2.png","created_at":"2018-07-01T17:12:48.446Z","like_count":0,"blurb":"...but still could myself. Background: I installed discourse using digitalocean oneclick installer. The website is running under http://forum.example.org forum.example.org without any problem except the ...","post_number":1,"topic_title_headline":"There was a problem sending the test email","topic_id":91312},{"id":1412219,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-11-29T23:18:11.815Z","like_count":1,"blurb":"I'm trying to create a foreign key relationship with the Topics table. The problem is it is failing in github workflow test environment during tests for the strangest reason, it is trying to access a ...","post_number":1,"topic_title_headline":"Strange migration error in tests during GH workflow","topic_id":287022},{"id":1160803,"name":"Bryan Joseph","username":"Bryan_Joseph","avatar_template":"/user_avatar/meta.discourse.org/bryan_joseph/{size}/273248_2.png","created_at":"2022-09-07T20:22:11.191Z","like_count":1,"blurb":"...SETTINGS ==================== DISCOURSE_HOSTNAME=url SMTP_ADDRESS=smtp.mailgun.org DEVELOPER_EMAILS=REDACTED SMTP_PASSWORD=REDACTED SMTP_PORT=2525 SMTP_USER_NAME=url LETSENCRYPT_ACCOUNT_EMAIL=REDACTED...","post_number":1,"topic_title_headline":"Smtp doctor test using port 465 even though its configured to use 2525","topic_id":238372},{"id":725228,"name":"james.network","username":"sunjam","avatar_template":"/user_avatar/meta.discourse.org/sunjam/{size}/175682_2.png","created_at":"2020-03-31T18:51:44.216Z","like_count":0,"blurb":"...Redis or updating it; it hasn't really been touched in the last 8+ months. I have not agentlly dealt with Redis before, but our Tests-Pass Discourse instance was setup using https://hub.docker.com/r...","post_number":1,"topic_title_headline":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","topic_id":146326},{"id":1240758,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2023-02-16T22:12:10.456Z","like_count":4,"blurb":"I see that the discourse-plugin-skeleton now has this: uses: discourse/.github/.github/workflows/discourse-plugin.yml@v1 so we don't have to keep updating stuff. But I have a plugin that requires the ...","post_number":1,"topic_title_headline":"Tests for plugin that requires a plugin","topic_id":255406},{"id":1197679,"name":"","username":"SilK","avatar_template":"/user_avatar/meta.discourse.org/silk/{size}/268124_2.png","created_at":"2022-11-18T17:03:46.056Z","like_count":0,"blurb":"...a new dev environment for working on plugins. Discourse is up to date with the main branch. I need to restart Ember in order to test changes made to the front end. This includes changes to Handlebars,...","post_number":1,"topic_title_headline":"Need to restart Ember in order to test front-end changes","topic_id":246069},{"id":1103913,"name":"Banibrata Dutta","username":"bdutta","avatar_template":"/user_avatar/meta.discourse.org/bdutta/{size}/259973_2.png","created_at":"2022-05-15T18:02:33.290Z","like_count":0,"blurb":"...only in a captive host-only testbed, so wondering if there is any local network SMTP daemon / service that I could start to complete the testing ? I'm happy with 100% command line mail client and serv...","post_number":1,"topic_title_headline":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","topic_id":227090},{"id":722608,"name":"Lona Lee","username":"Lona_Lee","avatar_template":"/user_avatar/meta.discourse.org/lona_lee/{size}/169072_2.png","created_at":"2020-03-27T07:14:10.239Z","like_count":1,"blurb":"Hello. I'm trying to get email setup working on my discourse instance. Done set-up properly and looks fine(no errors), so sent test emails. (logs confirmed : \"Admin\" - \"Emails\" - \"Sent\") However, I ha...","post_number":1,"topic_title_headline":"Test emails sent but","topic_id":145781},{"id":679950,"name":"Oleg Bovykin","username":"arrowcircle","avatar_template":"/user_avatar/meta.discourse.org/arrowcircle/{size}/100035_2.png","created_at":"2020-01-01T11:20:33.618Z","like_count":1,"blurb":"Hi! I found strange error in my admin page, that sidekiq is not running. I opened logs and found hundreds errors like: /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger...","post_number":1,"topic_title_headline":"Sidekiq heartbeat test failed, restarting","topic_id":137496},{"id":1033333,"name":"М. М.","username":"М_М","avatar_template":"/user_avatar/meta.discourse.org/м_м/{size}/243710_2.png","created_at":"2021-12-20T13:52:10.488Z","like_count":0,"blurb":"...user, the logs say like this Job exception: could not get 3xx (421: 421 Domain sandbox410fe5c7bb85483c941c05b4ec5f3495.mailgun.org is not allowed to send: Sandbox subdomains are for test purposes only...","post_number":1,"topic_title_headline":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","topic_id":212684},{"id":498559,"name":"","username":"desrocchi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/eb9ed0/{size}.png","created_at":"2018-11-14T15:20:59.607Z","like_count":0,"blurb":"Is there a way for me to see or test the admin options in the demo area? I am just a moderator on the platform we use but I would like to see which options could be of use without having to install th...","post_number":1,"topic_title_headline":"Test admin features without having to install Discourse","topic_id":102035},{"id":596557,"name":"Flaviu","username":"UnivacTwo","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/u/df705f/{size}.png","created_at":"2019-06-25T17:12:34.710Z","like_count":0,"blurb":"Let's encrypt has a limit of how many certificates can be generated in a week for the same domain. Unfortunately we reach this limit and we cannot generate a new certificate this week. We did a backup...","post_number":1,"topic_title_headline":"Install discourse with a staging (test) ssl certificate","topic_id":121299},{"id":583541,"name":"mark78","username":"Mark_Schmucker","avatar_template":"/user_avatar/meta.discourse.org/mark_schmucker/{size}/124810_2.png","created_at":"2019-05-24T00:14:35.895Z","like_count":0,"blurb":"Should I be able to run any Badge Query in https://meta.discourse.org/t/32566 Data Explorer ? I want to create a custom Badge Query using \"Appreciated\" as a starting point. I type the Appreciated quer...","post_number":1,"topic_title_headline":"Problem testing Badge Query from Data Explorer","topic_id":118568},{"id":569209,"name":"Penar Musaraj","username":"pmusaraj","avatar_template":"/user_avatar/meta.discourse.org/pmusaraj/{size}/119489_2.png","created_at":"2019-04-25T01:24:56.741Z","like_count":26,"blurb":"...device and installing the app via TestFlight: https://testflight.apple.com/join/NkdBQgmg testflight.apple.com https://testflight.apple.com/join/NkdBQgmg TestFlight - Apple Using TestFlight is a great ...","post_number":1,"topic_title_headline":"New iOS mobile app beta available for testing","topic_id":115912},{"id":1090520,"name":"Mac玩儿法","username":"waerfa","avatar_template":"/user_avatar/meta.discourse.org/waerfa/{size}/216044_2.png","created_at":"2022-04-17T21:46:04.755Z","like_count":0,"blurb":"...rebuild the container: git pull ./launcher rebuild app I got the fatal error which shows: FAILED -------------------- Pups::ExecError: cd /var/www/discourse & & git fetch --depth 1 origin tests-passed...","post_number":1,"topic_title_headline":"502 Bad Gateway after trying to rebuild test-passed branch","topic_id":224560},{"id":860593,"name":"james.network","username":"sunjam","avatar_template":"/user_avatar/meta.discourse.org/sunjam/{size}/175682_2.png","created_at":"2020-12-11T18:44:18.021Z","like_count":1,"blurb":"Continuing the discussion from https://meta.discourse.org/t/postgresql-13-update/172563/27 PostgreSQL 13 update : Run into trouble while updating 2.7.0beta1 Tests-Pass in order to remove some troubles...","post_number":1,"topic_title_headline":"Forum offline due to failed rebuilds on Tests-Pass","topic_id":173019},{"id":960683,"name":"","username":"daniyal","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/58f4c7/{size}.png","created_at":"2021-07-08T19:58:55.634Z","like_count":3,"blurb":"...which we want to experiment. An example would be to experiment different styles of topic list view. For this we are using Google Optimize A/B testing. Currently we plan to show theme without changes t...","post_number":1,"topic_title_headline":"[A/B Testing] Changing parent CSS class based on experiment variable","topic_id":196501},{"id":1389188,"name":"Angus McLeod","username":"angus","avatar_template":"/user_avatar/meta.discourse.org/angus/{size}/341715_2.png","created_at":"2023-10-20T03:44:49.737Z","like_count":7,"blurb":"I've been looking at the performance of the https://meta.discourse.org/t/activitypub-plugin/266794 ActivityPub plugin recently and considering the best ways to reliably test, and prove, performance fo...","post_number":1,"topic_title_headline":"Code-level performance testing","topic_id":282856},{"id":1329017,"name":"","username":"dodibi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/9fc348/{size}.png","created_at":"2023-07-19T12:45:57.446Z","like_count":0,"blurb":"Hello everyone! I'm currently facing some challenges while configuring my local environment to run discourse tests in a docker container. My main objective is to run the core tests with plugins attach...","post_number":1,"topic_title_headline":"Running core tests in docker environment","topic_id":272112},{"id":421687,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2018-05-11T22:03:23.232Z","like_count":0,"blurb":"Is there a way to initiate an email test from the Rails console? For a zillion reasons I would love to be able to send a test email without having to create an account. I've looked in config/routes.rb...","post_number":1,"topic_title_headline":"Email test from the console?","topic_id":87295},{"id":272436,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-03-20T20:29:06.608Z","like_count":1,"blurb":"It is my understanding that running rake qunit:test should run all of the qunit tests in Discourse, including those for any installed plugins. However, when I run the task in the docker development en...","post_number":1,"topic_title_headline":"Plugin QUnit tests are not running as part of rake qunit:test","topic_id":59577},{"id":899205,"name":"","username":"JQ331","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/j/41988e/{size}.png","created_at":"2021-03-03T18:09:29.452Z","like_count":6,"blurb":"I came across this https://blog.codinghorror.com/low-fi-usability-testing/ excellent article on how to do low-fi usability testing by @codinghorror . Usability testing (and user testing in general) is...","post_number":1,"topic_title_headline":"How does the Discourse team do usability testing?","topic_id":181856},{"id":530438,"name":"","username":"kleinfreund","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/k/a6a055/{size}.png","created_at":"2019-02-01T09:52:27.150Z","like_count":1,"blurb":"One can write tests for the backend of a plugin. For example, I created the following file in my plugin directory: spec/lib/route_store_spec.rb : require 'rails_helper' describe MyPlugin::RouteStore d...","post_number":1,"topic_title_headline":"Advice on writing Ruby tests for plugins","topic_id":108110},{"id":260691,"name":"Rimian Perkins","username":"rimian","avatar_template":"/user_avatar/meta.discourse.org/rimian/{size}/120658_2.png","created_at":"2017-02-13T02:48:10.177Z","like_count":0,"blurb":"What's the best way to (QUnit) assert an element on the page has some content in it? This passes: ok($.trim($('.foo').text()) == 'bar', 'content bar renders on page'); But isn't very practical. Is the...","post_number":1,"topic_title_headline":"Acceptance test content is present on page","topic_id":57292},{"id":1432567,"name":"Ayke","username":"rrit","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/{size}.png","created_at":"2024-01-09T16:53:57.916Z","like_count":0,"blurb":"Right now Discourse on meta.discourse.org serves the mobile-view instead of the crawler-view to the https://search.google.com/test/rich-results Google Rich Results Test . As there is no Schema Markup ...","post_number":1,"topic_title_headline":"Google Search Console/Schema Markup test tool “Google Rich Results Test”: mobile-view instead of crawler-view","topic_id":291039},{"id":1305996,"name":"Larry Diehl","username":"larrytheliquid","avatar_template":"/user_avatar/meta.discourse.org/larrytheliquid/{size}/310576_2.png","created_at":"2023-06-08T22:35:51.914Z","like_count":1,"blurb":"Hi Discourse Community! :slight_smile: I've been working on tech ( https://colimit.io Colimit ) that helps people apply https://en.wikipedia.org/wiki/Model-based_testing Model-based testing to test th...","post_number":1,"topic_title_headline":"Experiments with Model-Based Testing","topic_id":267737},{"id":1135474,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2022-07-20T08:48:28.868Z","like_count":0,"blurb":"...my case running: rake \"plugin:qunit[discourse-multilingual]\" with a branch installed. I'm declaring a function in my initializer (i'm extending I18n ) The tests sometimes (25%?) seem to run before the...","post_number":1,"topic_title_headline":"Qunit tests not deterministic in Plugin?","topic_id":233389},{"id":672801,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2019-12-12T21:01:31.303Z","like_count":0,"blurb":"Thanks, @Mittineague ! After a while, that made sense. I even wrote a spec, but even before I added my spec (and when I reverted to before I added any code), specs fail because: An error occurred whil...","post_number":1,"topic_title_headline":"Issues migrating test database","topic_id":135876},{"id":871105,"name":"","username":"Alteras","avatar_template":"/user_avatar/meta.discourse.org/alteras/{size}/179824_2.png","created_at":"2021-01-07T19:56:05.071Z","like_count":4,"blurb":"Hello! I'm currently working on a Markdown Extension/plugin that adds quite a number of BBCode tags, and I am looking to write QUnit Acceptance tests for them (I got really tired of constantly checkin...","post_number":1,"topic_title_headline":"Acceptance Test for Markdown Extension?","topic_id":175413},{"id":747613,"name":"","username":"xrav3nz","avatar_template":"/user_avatar/meta.discourse.org/xrav3nz/{size}/76894_2.png","created_at":"2020-05-08T04:27:56.920Z","like_count":8,"blurb":"...development - #2 by taylorthurlow - A May Of WTFs - Ruby on Rails Discussions Not sure if we have explored this before, but Rails can automatically maintain test databse schema with ActiveRecord::Migr...","post_number":1,"topic_title_headline":"Auto migrate test database schema","topic_id":150786},{"id":583961,"name":"Kim Miller","username":"kimardenmiller","avatar_template":"/user_avatar/meta.discourse.org/kimardenmiller/{size}/119631_2.png","created_at":"2019-05-24T22:21:24.996Z","like_count":3,"blurb":"Adding some polls API endpoints for PR to discourse_api, which work fine. Now I'm trying to understand how to create tests before submitting the PR, e.g.: require 'spec_helper' describe DiscourseApi::...","post_number":1,"topic_title_headline":"Building Tests for New discourse_api Endpoints","topic_id":118639},{"id":621707,"name":"Andrew Lank","username":"alank","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/a/c89c15/{size}.png","created_at":"2019-08-17T02:13:06.679Z","like_count":1,"blurb":"In my development and testing I'm running a Discourse instance (Docker Discourse from Bitnami) and it fulfills most of my API testing for our API service which talks to Discourse, however I now need t...","post_number":1,"topic_title_headline":"Seed or API calls to create test users","topic_id":126025},{"id":334186,"name":"Chris","username":"ChrisBeach","avatar_template":"/user_avatar/meta.discourse.org/chrisbeach/{size}/214628_2.png","created_at":"2017-10-01T08:18:48.148Z","like_count":1,"blurb":"...from the core team. I propose that on hitting the upgrade button, a new docker image is built in the background, and within it, acceptance tests of all plugins are run before the switch-over happens f...","post_number":1,"topic_title_headline":"Smoke-testing plugins during upgrade process","topic_id":71118},{"id":288991,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-05-16T10:28:58.496Z","like_count":1,"blurb":"I'm trying to use the docker image for tests both on my mac, and also https://meta.discourse.org/t/setting-up-plugin-continuous-integration-tests-on-travis-ci/59612 on travis . For a while now the qun...","post_number":1,"topic_title_headline":"QUnit tests won’t pass in discourse_dev docker image","topic_id":62797},{"id":187533,"name":"Sckott","username":"sckott","avatar_template":"/user_avatar/meta.discourse.org/sckott/{size}/115359_2.png","created_at":"2016-04-21T19:15:00.191Z","like_count":0,"blurb":"What's the best or fastest way to get Discourse installed on Travis for testing a client for the Discourse API ? It appears as though the discourse_api gem uses webmock so I think does not use a real ...","post_number":1,"topic_title_headline":"Testing a Discourse API client on Travis-CI","topic_id":42947},{"id":966756,"name":"Connor Parrish","username":"Connor_Parrish","avatar_template":"/user_avatar/meta.discourse.org/connor_parrish/{size}/225463_2.png","created_at":"2021-07-22T16:57:05.885Z","like_count":1,"blurb":"When you're conditionally adding a PostMenuButton using the plugin-api , the extra button is included in _extraButtons in between acceptance tests. When I run tests, if the tests where the button shou...","post_number":1,"topic_title_headline":"PostMenu’s ‘_extraButtons’ isn’t reset in between acceptance tests","topic_id":197887}],"topics":[{"id":49167,"title":"Write acceptance tests and component tests for Ember code in Discourse","fancy_title":"Write acceptance tests and component tests for Ember code in Discourse","slug":"write-acceptance-tests-and-component-tests-for-ember-code-in-discourse","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2016-08-24T20:48:02.492Z","last_posted_at":"2017-02-01T18:22:01.859Z","bumped":true,"bumped_at":"2017-02-01T18:22:01.859Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["tutorial","ember","testing"],"tags_descriptions":{},"category_id":56,"has_accepted_answer":false},{"id":32619,"title":"Developing Discourse Plugins - Part 6 - Add acceptance tests","fancy_title":"Developing Discourse Plugins - Part 6 - Add acceptance tests","slug":"developing-discourse-plugins-part-6-add-acceptance-tests","posts_count":33,"reply_count":26,"highest_post_number":38,"created_at":"2015-08-27T21:32:26.323Z","last_posted_at":"2022-06-02T11:06:38.274Z","bumped":true,"bumped_at":"2022-06-02T11:06:38.274Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["plugins","tutorial","plugin-guides","testing"],"tags_descriptions":{"plugins":""},"category_id":56,"has_accepted_answer":false},{"id":281579,"title":"End-to-end system testing for themes and theme components","fancy_title":"End-to-end system testing for themes and theme components","slug":"end-to-end-system-testing-for-themes-and-theme-components","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-10-24T23:13:37.118Z","last_posted_at":"2023-10-24T23:13:37.118Z","bumped":true,"bumped_at":"2023-11-13T23:20:01.596Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to","themes"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":66857,"title":"How to run Discourse core, plugin and theme QUnit test suites","fancy_title":"How to run Discourse core, plugin and theme QUnit test suites","slug":"how-to-run-discourse-core-plugin-and-theme-qunit-test-suites","posts_count":1,"reply_count":2,"highest_post_number":1,"created_at":"2017-07-26T14:09:58.032Z","last_posted_at":"2017-07-26T14:09:58.126Z","bumped":true,"bumped_at":"2023-09-04T17:56:33.079Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":17155,"title":"Test Discourse in mobile screen emulator","fancy_title":"Test Discourse in mobile screen emulator","slug":"test-discourse-in-mobile-screen-emulator","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2014-07-03T11:45:09.360Z","last_posted_at":"2014-10-12T22:19:24.137Z","bumped":true,"bumped_at":"2014-10-12T22:19:24.137Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":288363,"title":"Tip: when testing inbound email with fake user accounts...","fancy_title":"Tip: when testing inbound email with fake user accounts…","slug":"tip-when-testing-inbound-email-with-fake-user-accounts","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-12-12T10:51:07.868Z","last_posted_at":"2023-12-12T10:51:08.401Z","bumped":true,"bumped_at":"2023-12-12T10:51:08.401Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["email"],"tags_descriptions":{},"category_id":55,"has_accepted_answer":false},{"id":145744,"title":"Generate User API Keys for testing","fancy_title":"Generate User API Keys for testing","slug":"generate-user-api-keys-for-testing","posts_count":4,"reply_count":1,"highest_post_number":4,"created_at":"2020-03-26T21:31:38.269Z","last_posted_at":"2022-08-07T15:27:18.788Z","bumped":true,"bumped_at":"2022-08-07T15:27:18.788Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":58298,"title":"Build a sandbox to test changes before making them live","fancy_title":"Build a sandbox to test changes before making them live","slug":"build-a-sandbox-to-test-changes-before-making-them-live","posts_count":21,"reply_count":13,"highest_post_number":21,"created_at":"2017-03-03T13:34:07.921Z","last_posted_at":"2022-02-16T23:18:54.680Z","bumped":true,"bumped_at":"2022-02-16T23:18:54.680Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":55,"has_accepted_answer":false},{"id":118296,"title":"Migrate from tests-passed to stable","fancy_title":"Migrate from tests-passed to stable","slug":"migrate-from-tests-passed-to-stable","posts_count":12,"reply_count":7,"highest_post_number":12,"created_at":"2019-05-21T10:42:51.017Z","last_posted_at":"2019-06-21T15:55:53.072Z","bumped":true,"bumped_at":"2019-05-22T15:55:47.651Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":288705,"title":"502 Bad Gateway after online rebuild of tests-passed Production just now","fancy_title":"502 Bad Gateway after online rebuild of tests-passed Production just now","slug":"502-bad-gateway-after-online-rebuild-of-tests-passed-production-just-now","posts_count":6,"reply_count":1,"highest_post_number":6,"created_at":"2023-12-14T17:11:26.387Z","last_posted_at":"2024-01-13T17:37:27.535Z","bumped":true,"bumped_at":"2023-12-14T17:36:57.066Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":247546,"title":"Trouble on adding a simple unit test for Youtube oneboxing","fancy_title":"Trouble on adding a simple unit test for Youtube oneboxing","slug":"trouble-on-adding-a-simple-unit-test-for-youtube-oneboxing","posts_count":10,"reply_count":5,"highest_post_number":10,"created_at":"2022-12-02T20:49:55.713Z","last_posted_at":"2023-01-05T17:47:40.629Z","bumped":true,"bumped_at":"2022-12-06T17:47:28.771Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["onebox","testing"],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":274165,"title":"Strange QUnit behaviour?: test failing because setting value doesn't survive","fancy_title":"Strange QUnit behaviour?: test failing because setting value doesn’t survive","slug":"strange-qunit-behaviour-test-failing-because-setting-value-doesnt-survive","posts_count":5,"reply_count":2,"highest_post_number":5,"created_at":"2023-08-07T10:05:22.298Z","last_posted_at":"2023-09-06T11:19:24.509Z","bumped":true,"bumped_at":"2023-08-07T11:24:27.150Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":249167,"title":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","fancy_title":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","slug":"is-it-possible-to-override-the-site-object-with-own-fixture-during-front-end-tests-of-a-plugin","posts_count":4,"reply_count":0,"highest_post_number":4,"created_at":"2022-12-16T18:32:29.079Z","last_posted_at":"2022-12-28T21:57:56.070Z","bumped":true,"bumped_at":"2022-12-28T21:57:56.070Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":286355,"title":"Acceptance tests failing on Github Actions","fancy_title":"Acceptance tests failing on Github Actions","slug":"acceptance-tests-failing-on-github-actions","posts_count":6,"reply_count":0,"highest_post_number":6,"created_at":"2023-11-22T18:46:46.128Z","last_posted_at":"2023-11-24T13:59:40.100Z","bumped":true,"bumped_at":"2023-11-24T13:59:40.100Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":91312,"title":"There was a problem sending the test email","fancy_title":"There was a problem sending the test email","slug":"there-was-a-problem-sending-the-test-email","posts_count":8,"reply_count":5,"highest_post_number":8,"created_at":"2018-07-01T17:12:48.366Z","last_posted_at":"2018-08-01T09:28:21.935Z","bumped":true,"bumped_at":"2018-07-02T09:28:16.014Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":287022,"title":"Strange migration error in tests during GH workflow","fancy_title":"Strange migration error in tests during GH workflow","slug":"strange-migration-error-in-tests-during-gh-workflow","posts_count":6,"reply_count":3,"highest_post_number":6,"created_at":"2023-11-29T23:18:11.686Z","last_posted_at":"2023-12-31T15:09:05.633Z","bumped":true,"bumped_at":"2023-12-01T15:08:31.257Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":238372,"title":"Smtp doctor test using port 465 even though its configured to use 2525","fancy_title":"Smtp doctor test using port 465 even though its configured to use 2525","slug":"smtp-doctor-test-using-port-465-even-though-its-configured-to-use-2525","posts_count":12,"reply_count":7,"highest_post_number":12,"created_at":"2022-09-07T20:22:10.976Z","last_posted_at":"2022-10-09T00:21:55.656Z","bumped":true,"bumped_at":"2022-09-09T00:21:48.272Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":146326,"title":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","fancy_title":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","slug":"sidekiq-not-running-sidekiq-heartbeat-test-failed-restarting","posts_count":16,"reply_count":9,"highest_post_number":16,"created_at":"2020-03-31T18:51:44.061Z","last_posted_at":"2020-06-10T01:39:28.366Z","bumped":true,"bumped_at":"2020-05-11T01:39:26.054Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["unsupported-install"],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":255406,"title":"Tests for plugin that requires a plugin","fancy_title":"Tests for plugin that requires a plugin","slug":"tests-for-plugin-that-requires-a-plugin","posts_count":3,"reply_count":0,"highest_post_number":3,"created_at":"2023-02-16T22:12:10.344Z","last_posted_at":"2023-02-28T16:10:36.498Z","bumped":true,"bumped_at":"2023-02-28T20:21:06.122Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":246069,"title":"Need to restart Ember in order to test front-end changes","fancy_title":"Need to restart Ember in order to test front-end changes","slug":"need-to-restart-ember-in-order-to-test-front-end-changes","posts_count":5,"reply_count":1,"highest_post_number":5,"created_at":"2022-11-18T17:03:45.900Z","last_posted_at":"2022-11-18T18:17:53.648Z","bumped":true,"bumped_at":"2022-11-18T18:17:53.648Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":227090,"title":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","fancy_title":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","slug":"bitnami-discourse-vm-on-virtualbox-smtp-mail-server-for-testing","posts_count":3,"reply_count":0,"highest_post_number":3,"created_at":"2022-05-15T18:02:33.136Z","last_posted_at":"2022-05-16T09:10:43.794Z","bumped":true,"bumped_at":"2022-05-16T09:10:43.794Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["unsupported-install"],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":145781,"title":"Test emails sent but","fancy_title":"Test emails sent but","slug":"test-emails-sent-but","posts_count":5,"reply_count":2,"highest_post_number":5,"created_at":"2020-03-27T07:14:10.116Z","last_posted_at":"2020-04-29T02:52:31.927Z","bumped":true,"bumped_at":"2020-03-30T02:52:29.062Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":137496,"title":"Sidekiq heartbeat test failed, restarting","fancy_title":"Sidekiq heartbeat test failed, restarting","slug":"sidekiq-heartbeat-test-failed-restarting","posts_count":13,"reply_count":8,"highest_post_number":13,"created_at":"2020-01-01T11:20:33.492Z","last_posted_at":"2020-02-11T23:09:42.375Z","bumped":true,"bumped_at":"2020-01-12T23:09:39.730Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":212684,"title":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","fancy_title":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","slug":"mailgun-discourse-sandbox-subdomains-are-for-test-purposes-only-please-add-your-own-domain","posts_count":4,"reply_count":2,"highest_post_number":5,"created_at":"2021-12-20T13:52:10.405Z","last_posted_at":"2022-01-19T15:27:11.368Z","bumped":true,"bumped_at":"2021-12-20T15:26:24.911Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":102035,"title":"Test admin features without having to install Discourse","fancy_title":"Test admin features without having to install Discourse","slug":"test-admin-features-without-having-to-install-discourse","posts_count":6,"reply_count":2,"highest_post_number":6,"created_at":"2018-11-14T15:20:59.484Z","last_posted_at":"2021-09-09T07:35:18.270Z","bumped":true,"bumped_at":"2021-09-09T07:35:18.270Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":121299,"title":"Install discourse with a staging (test) ssl certificate","fancy_title":"Install discourse with a staging (test) ssl certificate","slug":"install-discourse-with-a-staging-test-ssl-certificate","posts_count":5,"reply_count":1,"highest_post_number":5,"created_at":"2019-06-25T17:12:34.552Z","last_posted_at":"2023-04-01T03:25:32.925Z","bumped":true,"bumped_at":"2019-09-04T15:15:31.153Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":118568,"title":"Problem testing Badge Query from Data Explorer","fancy_title":"Problem testing Badge Query from Data Explorer","slug":"problem-testing-badge-query-from-data-explorer","posts_count":5,"reply_count":1,"highest_post_number":6,"created_at":"2019-05-24T00:14:35.814Z","last_posted_at":"2019-05-24T00:46:42.560Z","bumped":true,"bumped_at":"2019-05-24T00:46:42.560Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["sql-triggered-badge"],"tags_descriptions":{"sql-triggered-badge":"SQL queries for custom triggered badges"},"category_id":148,"has_accepted_answer":true},{"id":115912,"title":"New iOS mobile app beta available for testing","fancy_title":"New iOS mobile app beta available for testing","slug":"new-ios-mobile-app-beta-available-for-testing","posts_count":49,"reply_count":31,"highest_post_number":49,"created_at":"2019-04-25T01:24:56.608Z","last_posted_at":"2019-05-31T17:31:02.516Z","bumped":true,"bumped_at":"2020-01-21T17:07:37.817Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":224560,"title":"502 Bad Gateway after trying to rebuild test-passed branch","fancy_title":"502 Bad Gateway after trying to rebuild test-passed branch","slug":"502-bad-gateway-after-trying-to-rebuild-test-passed-branch","posts_count":6,"reply_count":1,"highest_post_number":6,"created_at":"2022-04-17T21:46:04.598Z","last_posted_at":"2022-04-17T22:15:37.255Z","bumped":true,"bumped_at":"2022-04-17T22:15:37.255Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":false},{"id":173019,"title":"Forum offline due to failed rebuilds on Tests-Pass","fancy_title":"Forum offline due to failed rebuilds on Tests-Pass","slug":"forum-offline-due-to-failed-rebuilds-on-tests-pass","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2020-12-11T18:44:17.952Z","last_posted_at":"2020-12-11T19:12:11.141Z","bumped":true,"bumped_at":"2020-12-11T19:12:11.141Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":false},{"id":196501,"title":"[A/B Testing] Changing parent CSS class based on experiment variable","fancy_title":"[A/B Testing] Changing parent CSS class based on experiment variable","slug":"a-b-testing-changing-parent-css-class-based-on-experiment-variable","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2021-07-08T19:58:55.503Z","last_posted_at":"2021-07-15T18:19:21.092Z","bumped":true,"bumped_at":"2021-07-15T18:19:21.092Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":282856,"title":"Code-level performance testing","fancy_title":"Code-level performance testing","slug":"code-level-performance-testing","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2023-10-20T03:44:49.568Z","last_posted_at":"2023-10-23T23:29:59.741Z","bumped":true,"bumped_at":"2023-10-23T23:29:59.741Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":272112,"title":"Running core tests in docker environment","fancy_title":"Running core tests in docker environment","slug":"running-core-tests-in-docker-environment","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-07-19T12:45:57.266Z","last_posted_at":"2023-07-19T12:45:57.446Z","bumped":true,"bumped_at":"2023-07-19T12:45:57.446Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["docker","spec","testing"],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":87295,"title":"Email test from the console?","fancy_title":"Email test from the console?","slug":"email-test-from-the-console","posts_count":4,"reply_count":1,"highest_post_number":4,"created_at":"2018-05-11T22:03:23.101Z","last_posted_at":"2018-05-12T00:55:41.843Z","bumped":true,"bumped_at":"2018-05-12T00:55:41.843Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":59577,"title":"Plugin QUnit tests are not running as part of rake qunit:test","fancy_title":"Plugin QUnit tests are not running as part of rake qunit:test","slug":"plugin-qunit-tests-are-not-running-as-part-of-rake-qunit-test","posts_count":8,"reply_count":5,"highest_post_number":8,"created_at":"2017-03-20T20:29:06.536Z","last_posted_at":"2017-07-17T18:26:45.188Z","bumped":true,"bumped_at":"2017-07-17T18:26:45.188Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":181856,"title":"How does the Discourse team do usability testing?","fancy_title":"How does the Discourse team do usability testing?","slug":"how-does-the-discourse-team-do-usability-testing","posts_count":5,"reply_count":2,"highest_post_number":6,"created_at":"2021-03-03T18:09:29.357Z","last_posted_at":"2021-03-04T15:23:46.571Z","bumped":true,"bumped_at":"2021-03-04T15:23:46.571Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":3,"has_accepted_answer":false},{"id":108110,"title":"Advice on writing Ruby tests for plugins","fancy_title":"Advice on writing Ruby tests for plugins","slug":"advice-on-writing-ruby-tests-for-plugins","posts_count":15,"reply_count":13,"highest_post_number":15,"created_at":"2019-02-01T09:52:27.051Z","last_posted_at":"2019-05-03T03:54:47.163Z","bumped":true,"bumped_at":"2019-05-03T03:54:47.163Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":57292,"title":"Acceptance test content is present on page","fancy_title":"Acceptance test content is present on page","slug":"acceptance-test-content-is-present-on-page","posts_count":7,"reply_count":3,"highest_post_number":7,"created_at":"2017-02-13T02:48:10.108Z","last_posted_at":"2017-02-14T05:55:31.520Z","bumped":true,"bumped_at":"2017-02-14T05:55:31.520Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":291039,"title":"Google Search Console/Schema Markup test tool \"Google Rich Results Test\": mobile-view instead of crawler-view","fancy_title":"Google Search Console/Schema Markup test tool “Google Rich Results Test”: mobile-view instead of crawler-view","slug":"google-search-console-schema-markup-test-tool-google-rich-results-test-mobile-view-instead-of-crawler-view","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2024-01-09T16:53:57.797Z","last_posted_at":"2024-01-09T17:17:09.039Z","bumped":true,"bumped_at":"2024-01-09T17:17:09.039Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":1,"has_accepted_answer":false},{"id":267737,"title":"Experiments with Model-Based Testing","fancy_title":"Experiments with Model-Based Testing","slug":"experiments-with-model-based-testing","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-06-08T22:35:51.784Z","last_posted_at":"2023-06-08T22:35:51.914Z","bumped":true,"bumped_at":"2023-06-08T22:35:51.914Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":233389,"title":"Qunit tests not deterministic in Plugin?","fancy_title":"Qunit tests not deterministic in Plugin?","slug":"qunit-tests-not-deterministic-in-plugin","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2022-07-20T08:48:28.757Z","last_posted_at":"2022-07-20T11:03:42.522Z","bumped":true,"bumped_at":"2022-07-20T11:03:42.522Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":135876,"title":"Issues migrating test database","fancy_title":"Issues migrating test database","slug":"issues-migrating-test-database","posts_count":9,"reply_count":1,"highest_post_number":9,"created_at":"2019-12-12T21:01:31.303Z","last_posted_at":"2021-03-02T16:44:20.120Z","bumped":true,"bumped_at":"2021-03-02T16:44:20.120Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":175413,"title":"Acceptance Test for Markdown Extension?","fancy_title":"Acceptance Test for Markdown Extension?","slug":"acceptance-test-for-markdown-extension","posts_count":3,"reply_count":0,"highest_post_number":4,"created_at":"2021-01-07T19:56:04.931Z","last_posted_at":"2021-01-10T11:26:16.888Z","bumped":true,"bumped_at":"2021-01-10T16:30:56.164Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":150786,"title":"Auto migrate test database schema","fancy_title":"Auto migrate test database schema","slug":"auto-migrate-test-database-schema","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2020-05-08T04:27:56.739Z","last_posted_at":"2020-05-08T13:15:45.247Z","bumped":true,"bumped_at":"2020-05-08T13:15:45.247Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":118639,"title":"Building Tests for New discourse_api Endpoints","fancy_title":"Building Tests for New discourse_api Endpoints","slug":"building-tests-for-new-discourse-api-endpoints","posts_count":20,"reply_count":9,"highest_post_number":21,"created_at":"2019-05-24T22:21:24.896Z","last_posted_at":"2019-10-02T21:13:35.140Z","bumped":true,"bumped_at":"2019-10-02T21:36:26.639Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":126025,"title":"Seed or API calls to create test users","fancy_title":"Seed or API calls to create test users","slug":"seed-or-api-calls-to-create-test-users","posts_count":7,"reply_count":5,"highest_post_number":7,"created_at":"2019-08-17T02:13:06.578Z","last_posted_at":"2019-08-19T11:50:36.137Z","bumped":true,"bumped_at":"2019-08-19T11:50:36.137Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":71118,"title":"Smoke-testing plugins during upgrade process","fancy_title":"Smoke-testing plugins during upgrade process","slug":"smoke-testing-plugins-during-upgrade-process","posts_count":22,"reply_count":17,"highest_post_number":22,"created_at":"2017-10-01T08:18:48.067Z","last_posted_at":"2017-10-02T23:44:59.852Z","bumped":true,"bumped_at":"2017-10-02T23:44:59.852Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["pr-welcome"],"tags_descriptions":{"pr-welcome":"You're welcome to submit a Github pull request that implements this"},"category_id":2,"has_accepted_answer":false},{"id":62797,"title":"QUnit tests won't pass in discourse_dev docker image","fancy_title":"QUnit tests won’t pass in discourse_dev docker image","slug":"qunit-tests-wont-pass-in-discourse-dev-docker-image","posts_count":20,"reply_count":11,"highest_post_number":21,"created_at":"2017-05-16T10:28:58.397Z","last_posted_at":"2017-07-25T15:50:27.716Z","bumped":true,"bumped_at":"2017-07-25T15:50:27.716Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":42947,"title":"Testing a Discourse API client on Travis-CI","fancy_title":"Testing a Discourse API client on Travis-CI","slug":"testing-a-discourse-api-client-on-travis-ci","posts_count":5,"reply_count":3,"highest_post_number":5,"created_at":"2016-04-21T19:15:00.130Z","last_posted_at":"2016-04-22T03:03:47.976Z","bumped":true,"bumped_at":"2016-04-22T03:03:47.976Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":197887,"title":"PostMenu's '_extraButtons' isn't reset in between acceptance tests","fancy_title":"PostMenu’s ‘_extraButtons’ isn’t reset in between acceptance tests","slug":"postmenus-extrabuttons-isnt-reset-in-between-acceptance-tests","posts_count":2,"reply_count":0,"highest_post_number":4,"created_at":"2021-07-22T16:57:05.803Z","last_posted_at":"2021-08-04T09:11:29.538Z","bumped":true,"bumped_at":"2021-08-04T09:11:29.538Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false}],"users":[],"categories":[],"tags":[],"groups":[],"grouped_search_result":{"more_posts":null,"more_users":null,"more_categories":null,"term":"testing","search_log_id":2089836,"more_full_page_results":true,"can_create_topic":false,"error":null,"post_ids":[218354,138484,1381521,311252,59431,1419473,722424,266441,582008,1421414,1204506,1339808,1211823,1408389,443129,1412219,1160803,725228,1240758,1197679,1103913,722608,679950,1033333,498559,596557,583541,569209,1090520,860593,960683,1389188,1329017,421687,272436,899205,530438,260691,1432567,1305996,1135474,672801,871105,747613,583961,621707,334186,288991,187533,966756],"user_ids":[],"category_ids":[],"tag_ids":[],"group_ids":[]}} \ No newline at end of file diff --git a/spec/fixtures/search_meta/search_with_categories.json b/spec/fixtures/search_meta/search_with_categories.json index 6f412e0f..fbb8e966 100644 --- a/spec/fixtures/search_meta/search_with_categories.json +++ b/spec/fixtures/search_meta/search_with_categories.json @@ -1 +1 @@ -{"posts":[{"id":218354,"name":"Robin Ward","username":"eviltrout","avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png","created_at":"2016-08-24T20:48:02.587Z","like_count":15,"blurb":"Automated tests are a great way to protect your code against future regressions. Many people are familiar with how to do this in our Rails codebase with http://rspec.info/ rspec , but the Javascript s...","post_number":1,"topic_title_headline":"Write acceptance tests and component tests for Ember code in Discourse","topic_id":49167},{"id":138484,"name":"Robin Ward","username":"eviltrout","avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png","created_at":"2015-08-27T21:32:26.407Z","like_count":29,"blurb":"Previous tutorial: https://meta.discourse.org/t/developing-discourse-plugins-part-5-add-an-admin-interface/31761 Developing Discourse Plugins - Part 5 - Add an admin interface Did you know that Discou...","post_number":1,"topic_title_headline":"Developing Discourse Plugins - Part 6 - Add acceptance tests","topic_id":32619},{"id":1381521,"name":"Alan Tan","username":"tgxworld","avatar_template":"/user_avatar/meta.discourse.org/tgxworld/{size}/106117_2.png","created_at":"2023-10-24T23:13:37.118Z","like_count":16,"blurb":"Writing automated tests for themes is an important part of the theme development process which can help ensure that the features being introduced by a theme continues to work well overtime with core D...","post_number":1,"topic_title_headline":"End-to-end system testing for themes and theme components","topic_id":281579},{"id":311252,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-07-26T14:09:58.126Z","like_count":17,"blurb":"Discourse has extensive frontend tests for core, plugins and themes. Once you have a functioning local development environment, those tests can be run locally in a number of different ways. Running te...","post_number":1,"topic_title_headline":"How to run Discourse core, plugin and theme QUnit test suites","topic_id":66857},{"id":59431,"name":"Erlend Sogge Heggen","username":"erlend_sh","avatar_template":"/user_avatar/meta.discourse.org/erlend_sh/{size}/119475_2.png","created_at":"2014-07-03T11:45:09.463Z","like_count":5,"blurb":"...view=1 to an URL, but the emulator has the added benefit of letting you select the screen profile of a specific device. I also tested all of the most popular online screen emulators, but unfortunately...","post_number":1,"topic_title_headline":"Test Discourse in mobile screen emulator","topic_id":17155},{"id":1419473,"name":"","username":"ToddZ","avatar_template":"/user_avatar/meta.discourse.org/toddz/{size}/328350_2.png","created_at":"2023-12-12T10:51:08.401Z","like_count":2,"blurb":"...amount of time troubleshooting inbound email because Discourse was rejecting every reply-by-email from my fake users. It had worked fine when I first tested several weeks ago… I finally realized that ...","post_number":1,"topic_title_headline":"Tip: when testing inbound email with fake user accounts…","topic_id":288363},{"id":722424,"name":"Falco","username":"Falco","avatar_template":"/user_avatar/meta.discourse.org/falco/{size}/179432_2.png","created_at":"2020-03-26T21:31:38.463Z","like_count":17,"blurb":"Continuing the discussion from https://meta.discourse.org/t/user-api-keys-specification/48536 User API keys specification : I created a small utility script in order to test User API keys locally. Fir...","post_number":1,"topic_title_headline":"Generate User API Keys for testing","topic_id":145744},{"id":266441,"name":"Andrew Waugh","username":"JagWaugh","avatar_template":"/user_avatar/meta.discourse.org/jagwaugh/{size}/69335_2.png","created_at":"2017-03-03T13:34:08.009Z","like_count":19,"blurb":"Regardless of if you're a moderator or an admin, you will no doubt at some time think about making some change to your live site and wonder if this will bring shame on you, and/or cause yourself an en...","post_number":1,"topic_title_headline":"Build a sandbox to test changes before making them live","topic_id":58298},{"id":582008,"name":"","username":"Wurzelseppi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/w/eada6e/{size}.png","created_at":"2019-05-21T10:42:51.099Z","like_count":0,"blurb":"Hi guys, just wanted to migrate from 2.3.0beta9 to stable release and got this error: What can I do here ? Caused by: PG::UndefinedColumn: ERROR: column \"email_private_messages\" of relation \"user_opti...","post_number":1,"topic_title_headline":"Migrate from tests-passed to stable","topic_id":118296},{"id":1421414,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-12-14T17:11:26.575Z","like_count":1,"blurb":"Restarting the server, restarting the container, console rebuilding doesn't help. Container is up as I can ./launcher enter app Got a bunch of these in logs, ideas on how to investigate redis failure?...","post_number":1,"topic_title_headline":"502 Bad Gateway after online rebuild of tests-passed Production just now","topic_id":288705},{"id":1204506,"name":"Coin-coin le Canapin","username":"Canapin","avatar_template":"/user_avatar/meta.discourse.org/canapin/{size}/119591_2.png","created_at":"2022-12-02T20:49:55.867Z","like_count":1,"blurb":"Hi! I want to add support of /shorts/ Youtube link. My modification of the YoutubeOnebox class works, but it is required that I add a test in https://github.com/discourse/discourse/blob/493d437e79f88a...","post_number":1,"topic_title_headline":"Trouble on adding a simple unit test for Youtube oneboxing","topic_id":247546},{"id":1339808,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-08-07T10:05:22.444Z","like_count":2,"blurb":"I have a strange issue with QUnit. This test is extremely simple and should be straightforward … but … A plugin setting is changing from those I have set up. https://github.com/paviliondev/discourse-l...","post_number":1,"topic_title_headline":"Strange QUnit behaviour?: test failing because setting value doesn’t survive","topic_id":274165},{"id":1211823,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2022-12-16T18:32:29.231Z","like_count":1,"blurb":"...presenting formatted location on User Card by merefield · Pull Request #73 · paviliondev/discourse-locations · GitHub I'm attempting to cover the change with a new Front End test. But the test fails t...","post_number":1,"topic_title_headline":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","topic_id":249167},{"id":1408389,"name":"Pierre Romera","username":"pirhoo","avatar_template":"/user_avatar/meta.discourse.org/pirhoo/{size}/120058_2.png","created_at":"2023-11-22T18:46:46.469Z","like_count":5,"blurb":"...m setting up a new plugin based on the https://github.com/discourse/discourse-plugin-skeleton/tree/main/assets skeleton you provided which already helped me a lot. I am now writing tests, both for the...","post_number":1,"topic_title_headline":"Acceptance tests failing on Github Actions","topic_id":286355},{"id":443129,"name":"JK Baseer","username":"JKBaseer","avatar_template":"/user_avatar/meta.discourse.org/jkbaseer/{size}/80471_2.png","created_at":"2018-07-01T17:12:48.446Z","like_count":0,"blurb":"...but still could myself. Background: I installed discourse using digitalocean oneclick installer. The website is running under http://forum.example.org forum.example.org without any problem except the ...","post_number":1,"topic_title_headline":"There was a problem sending the test email","topic_id":91312},{"id":1412219,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-11-29T23:18:11.815Z","like_count":1,"blurb":"I'm trying to create a foreign key relationship with the Topics table. The problem is it is failing in github workflow test environment during tests for the strangest reason, it is trying to access a ...","post_number":1,"topic_title_headline":"Strange migration error in tests during GH workflow","topic_id":287022},{"id":1160803,"name":"Bryan Joseph","username":"Bryan_Joseph","avatar_template":"/user_avatar/meta.discourse.org/bryan_joseph/{size}/273248_2.png","created_at":"2022-09-07T20:22:11.191Z","like_count":1,"blurb":"...SETTINGS ==================== DISCOURSE_HOSTNAME=url SMTP_ADDRESS=smtp.mailgun.org DEVELOPER_EMAILS=REDACTED SMTP_PASSWORD=REDACTED SMTP_PORT=2525 SMTP_USER_NAME=url LETSENCRYPT_ACCOUNT_EMAIL=REDACTED...","post_number":1,"topic_title_headline":"Smtp doctor test using port 465 even though its configured to use 2525","topic_id":238372},{"id":725228,"name":"james.network","username":"sunjam","avatar_template":"/user_avatar/meta.discourse.org/sunjam/{size}/175682_2.png","created_at":"2020-03-31T18:51:44.216Z","like_count":0,"blurb":"...Redis or updating it; it hasn't really been touched in the last 8+ months. I have not personally dealt with Redis before, but our Tests-Pass Discourse instance was setup using https://hub.docker.com/r...","post_number":1,"topic_title_headline":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","topic_id":146326},{"id":1240758,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2023-02-16T22:12:10.456Z","like_count":4,"blurb":"I see that the discourse-plugin-skeleton now has this: uses: discourse/.github/.github/workflows/discourse-plugin.yml@v1 so we don't have to keep updating stuff. But I have a plugin that requires the ...","post_number":1,"topic_title_headline":"Tests for plugin that requires a plugin","topic_id":255406},{"id":1197679,"name":"","username":"SilK","avatar_template":"/user_avatar/meta.discourse.org/silk/{size}/268124_2.png","created_at":"2022-11-18T17:03:46.056Z","like_count":0,"blurb":"...a new dev environment for working on plugins. Discourse is up to date with the main branch. I need to restart Ember in order to test changes made to the front end. This includes changes to Handlebars,...","post_number":1,"topic_title_headline":"Need to restart Ember in order to test front-end changes","topic_id":246069},{"id":1103913,"name":"Banibrata Dutta","username":"bdutta","avatar_template":"/user_avatar/meta.discourse.org/bdutta/{size}/259973_2.png","created_at":"2022-05-15T18:02:33.290Z","like_count":0,"blurb":"...only in a captive host-only testbed, so wondering if there is any local network SMTP daemon / service that I could start to complete the testing ? I'm happy with 100% command line mail client and serv...","post_number":1,"topic_title_headline":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","topic_id":227090},{"id":722608,"name":"Lona Lee","username":"Lona_Lee","avatar_template":"/user_avatar/meta.discourse.org/lona_lee/{size}/169072_2.png","created_at":"2020-03-27T07:14:10.239Z","like_count":1,"blurb":"Hello. I'm trying to get email setup working on my discourse instance. Done set-up properly and looks fine(no errors), so sent test emails. (logs confirmed : \"Admin\" - \"Emails\" - \"Sent\") However, I ha...","post_number":1,"topic_title_headline":"Test emails sent but","topic_id":145781},{"id":679950,"name":"Oleg Bovykin","username":"arrowcircle","avatar_template":"/user_avatar/meta.discourse.org/arrowcircle/{size}/100035_2.png","created_at":"2020-01-01T11:20:33.618Z","like_count":1,"blurb":"Hi! I found strange error in my admin page, that sidekiq is not running. I opened logs and found hundreds errors like: /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger...","post_number":1,"topic_title_headline":"Sidekiq heartbeat test failed, restarting","topic_id":137496},{"id":1033333,"name":"М. М.","username":"М_М","avatar_template":"/user_avatar/meta.discourse.org/м_м/{size}/243710_2.png","created_at":"2021-12-20T13:52:10.488Z","like_count":0,"blurb":"...user, the logs say like this Job exception: could not get 3xx (421: 421 Domain sandbox410fe5c7bb85483c941c05b4ec5f3495.mailgun.org is not allowed to send: Sandbox subdomains are for test purposes only...","post_number":1,"topic_title_headline":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","topic_id":212684},{"id":498559,"name":"","username":"desrocchi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/eb9ed0/{size}.png","created_at":"2018-11-14T15:20:59.607Z","like_count":0,"blurb":"Is there a way for me to see or test the admin options in the demo area? I am just a moderator on the platform we use but I would like to see which options could be of use without having to install th...","post_number":1,"topic_title_headline":"Test admin features without having to install Discourse","topic_id":102035},{"id":596557,"name":"Flaviu","username":"UnivacTwo","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/u/df705f/{size}.png","created_at":"2019-06-25T17:12:34.710Z","like_count":0,"blurb":"Let's encrypt has a limit of how many certificates can be generated in a week for the same domain. Unfortunately we reach this limit and we cannot generate a new certificate this week. We did a backup...","post_number":1,"topic_title_headline":"Install discourse with a staging (test) ssl certificate","topic_id":121299},{"id":583541,"name":"mark78","username":"Mark_Schmucker","avatar_template":"/user_avatar/meta.discourse.org/mark_schmucker/{size}/124810_2.png","created_at":"2019-05-24T00:14:35.895Z","like_count":0,"blurb":"Should I be able to run any Badge Query in https://meta.discourse.org/t/32566 Data Explorer ? I want to create a custom Badge Query using \"Appreciated\" as a starting point. I type the Appreciated quer...","post_number":1,"topic_title_headline":"Problem testing Badge Query from Data Explorer","topic_id":118568},{"id":569209,"name":"Penar Musaraj","username":"pmusaraj","avatar_template":"/user_avatar/meta.discourse.org/pmusaraj/{size}/119489_2.png","created_at":"2019-04-25T01:24:56.741Z","like_count":26,"blurb":"...device and installing the app via TestFlight: https://testflight.apple.com/join/NkdBQgmg testflight.apple.com https://testflight.apple.com/join/NkdBQgmg TestFlight - Apple Using TestFlight is a great ...","post_number":1,"topic_title_headline":"New iOS mobile app beta available for testing","topic_id":115912},{"id":1090520,"name":"Mac玩儿法","username":"waerfa","avatar_template":"/user_avatar/meta.discourse.org/waerfa/{size}/216044_2.png","created_at":"2022-04-17T21:46:04.755Z","like_count":0,"blurb":"...rebuild the container: git pull ./launcher rebuild app I got the fatal error which shows: FAILED -------------------- Pups::ExecError: cd /var/www/discourse & & git fetch --depth 1 origin tests-passed...","post_number":1,"topic_title_headline":"502 Bad Gateway after trying to rebuild test-passed branch","topic_id":224560},{"id":860593,"name":"james.network","username":"sunjam","avatar_template":"/user_avatar/meta.discourse.org/sunjam/{size}/175682_2.png","created_at":"2020-12-11T18:44:18.021Z","like_count":1,"blurb":"Continuing the discussion from https://meta.discourse.org/t/postgresql-13-update/172563/27 PostgreSQL 13 update : Run into trouble while updating 2.7.0beta1 Tests-Pass in order to remove some troubles...","post_number":1,"topic_title_headline":"Forum offline due to failed rebuilds on Tests-Pass","topic_id":173019},{"id":960683,"name":"","username":"daniyal","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/58f4c7/{size}.png","created_at":"2021-07-08T19:58:55.634Z","like_count":3,"blurb":"...which we want to experiment. An example would be to experiment different styles of topic list view. For this we are using Google Optimize A/B testing. Currently we plan to show theme without changes t...","post_number":1,"topic_title_headline":"[A/B Testing] Changing parent CSS class based on experiment variable","topic_id":196501},{"id":1389188,"name":"Angus McLeod","username":"angus","avatar_template":"/user_avatar/meta.discourse.org/angus/{size}/341715_2.png","created_at":"2023-10-20T03:44:49.737Z","like_count":7,"blurb":"I've been looking at the performance of the https://meta.discourse.org/t/activitypub-plugin/266794 ActivityPub plugin recently and considering the best ways to reliably test, and prove, performance fo...","post_number":1,"topic_title_headline":"Code-level performance testing","topic_id":282856},{"id":1329017,"name":"","username":"dodibi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/9fc348/{size}.png","created_at":"2023-07-19T12:45:57.446Z","like_count":0,"blurb":"Hello everyone! I'm currently facing some challenges while configuring my local environment to run discourse tests in a docker container. My main objective is to run the core tests with plugins attach...","post_number":1,"topic_title_headline":"Running core tests in docker environment","topic_id":272112},{"id":421687,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2018-05-11T22:03:23.232Z","like_count":0,"blurb":"Is there a way to initiate an email test from the Rails console? For a zillion reasons I would love to be able to send a test email without having to create an account. I've looked in config/routes.rb...","post_number":1,"topic_title_headline":"Email test from the console?","topic_id":87295},{"id":272436,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-03-20T20:29:06.608Z","like_count":1,"blurb":"It is my understanding that running rake qunit:test should run all of the qunit tests in Discourse, including those for any installed plugins. However, when I run the task in the docker development en...","post_number":1,"topic_title_headline":"Plugin QUnit tests are not running as part of rake qunit:test","topic_id":59577},{"id":899205,"name":"","username":"JQ331","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/j/41988e/{size}.png","created_at":"2021-03-03T18:09:29.452Z","like_count":6,"blurb":"I came across this https://blog.codinghorror.com/low-fi-usability-testing/ excellent article on how to do low-fi usability testing by @codinghorror . Usability testing (and user testing in general) is...","post_number":1,"topic_title_headline":"How does the Discourse team do usability testing?","topic_id":181856},{"id":530438,"name":"","username":"kleinfreund","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/k/a6a055/{size}.png","created_at":"2019-02-01T09:52:27.150Z","like_count":1,"blurb":"One can write tests for the backend of a plugin. For example, I created the following file in my plugin directory: spec/lib/route_store_spec.rb : require 'rails_helper' describe MyPlugin::RouteStore d...","post_number":1,"topic_title_headline":"Advice on writing Ruby tests for plugins","topic_id":108110},{"id":260691,"name":"Rimian Perkins","username":"rimian","avatar_template":"/user_avatar/meta.discourse.org/rimian/{size}/120658_2.png","created_at":"2017-02-13T02:48:10.177Z","like_count":0,"blurb":"What's the best way to (QUnit) assert an element on the page has some content in it? This passes: ok($.trim($('.foo').text()) == 'bar', 'content bar renders on page'); But isn't very practical. Is the...","post_number":1,"topic_title_headline":"Acceptance test content is present on page","topic_id":57292},{"id":1432567,"name":"Ayke","username":"rrit","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/{size}.png","created_at":"2024-01-09T16:53:57.916Z","like_count":0,"blurb":"Right now Discourse on meta.discourse.org serves the mobile-view instead of the crawler-view to the https://search.google.com/test/rich-results Google Rich Results Test . As there is no Schema Markup ...","post_number":1,"topic_title_headline":"Google Search Console/Schema Markup test tool “Google Rich Results Test”: mobile-view instead of crawler-view","topic_id":291039},{"id":1305996,"name":"Larry Diehl","username":"larrytheliquid","avatar_template":"/user_avatar/meta.discourse.org/larrytheliquid/{size}/310576_2.png","created_at":"2023-06-08T22:35:51.914Z","like_count":1,"blurb":"Hi Discourse Community! :slight_smile: I've been working on tech ( https://colimit.io Colimit ) that helps people apply https://en.wikipedia.org/wiki/Model-based_testing Model-based testing to test th...","post_number":1,"topic_title_headline":"Experiments with Model-Based Testing","topic_id":267737},{"id":1135474,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2022-07-20T08:48:28.868Z","like_count":0,"blurb":"...my case running: rake \"plugin:qunit[discourse-multilingual]\" with a branch installed. I'm declaring a function in my initializer (i'm extending I18n ) The tests sometimes (25%?) seem to run before the...","post_number":1,"topic_title_headline":"Qunit tests not deterministic in Plugin?","topic_id":233389},{"id":672801,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2019-12-12T21:01:31.303Z","like_count":0,"blurb":"Thanks, @Mittineague ! After a while, that made sense. I even wrote a spec, but even before I added my spec (and when I reverted to before I added any code), specs fail because: An error occurred whil...","post_number":1,"topic_title_headline":"Issues migrating test database","topic_id":135876},{"id":871105,"name":"","username":"Alteras","avatar_template":"/user_avatar/meta.discourse.org/alteras/{size}/179824_2.png","created_at":"2021-01-07T19:56:05.071Z","like_count":4,"blurb":"Hello! I'm currently working on a Markdown Extension/plugin that adds quite a number of BBCode tags, and I am looking to write QUnit Acceptance tests for them (I got really tired of constantly checkin...","post_number":1,"topic_title_headline":"Acceptance Test for Markdown Extension?","topic_id":175413},{"id":747613,"name":"","username":"xrav3nz","avatar_template":"/user_avatar/meta.discourse.org/xrav3nz/{size}/76894_2.png","created_at":"2020-05-08T04:27:56.920Z","like_count":8,"blurb":"...development - #2 by taylorthurlow - A May Of WTFs - Ruby on Rails Discussions Not sure if we have explored this before, but Rails can automatically maintain test databse schema with ActiveRecord::Migr...","post_number":1,"topic_title_headline":"Auto migrate test database schema","topic_id":150786},{"id":583961,"name":"Kim Miller","username":"kimardenmiller","avatar_template":"/user_avatar/meta.discourse.org/kimardenmiller/{size}/119631_2.png","created_at":"2019-05-24T22:21:24.996Z","like_count":3,"blurb":"Adding some polls API endpoints for PR to discourse_api, which work fine. Now I'm trying to understand how to create tests before submitting the PR, e.g.: require 'spec_helper' describe DiscourseApi::...","post_number":1,"topic_title_headline":"Building Tests for New discourse_api Endpoints","topic_id":118639},{"id":621707,"name":"Andrew Lank","username":"alank","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/a/c89c15/{size}.png","created_at":"2019-08-17T02:13:06.679Z","like_count":1,"blurb":"In my development and testing I'm running a Discourse instance (Docker Discourse from Bitnami) and it fulfills most of my API testing for our API service which talks to Discourse, however I now need t...","post_number":1,"topic_title_headline":"Seed or API calls to create test users","topic_id":126025},{"id":334186,"name":"Chris","username":"ChrisBeach","avatar_template":"/user_avatar/meta.discourse.org/chrisbeach/{size}/214628_2.png","created_at":"2017-10-01T08:18:48.148Z","like_count":1,"blurb":"...from the core team. I propose that on hitting the upgrade button, a new docker image is built in the background, and within it, acceptance tests of all plugins are run before the switch-over happens f...","post_number":1,"topic_title_headline":"Smoke-testing plugins during upgrade process","topic_id":71118},{"id":288991,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-05-16T10:28:58.496Z","like_count":1,"blurb":"I'm trying to use the docker image for tests both on my mac, and also https://meta.discourse.org/t/setting-up-plugin-continuous-integration-tests-on-travis-ci/59612 on travis . For a while now the qun...","post_number":1,"topic_title_headline":"QUnit tests won’t pass in discourse_dev docker image","topic_id":62797},{"id":187533,"name":"Sckott","username":"sckott","avatar_template":"/user_avatar/meta.discourse.org/sckott/{size}/115359_2.png","created_at":"2016-04-21T19:15:00.191Z","like_count":0,"blurb":"What's the best or fastest way to get Discourse installed on Travis for testing a client for the Discourse API ? It appears as though the discourse_api gem uses webmock so I think does not use a real ...","post_number":1,"topic_title_headline":"Testing a Discourse API client on Travis-CI","topic_id":42947},{"id":966756,"name":"Connor Parrish","username":"Connor_Parrish","avatar_template":"/user_avatar/meta.discourse.org/connor_parrish/{size}/225463_2.png","created_at":"2021-07-22T16:57:05.885Z","like_count":1,"blurb":"When you're conditionally adding a PostMenuButton using the plugin-api , the extra button is included in _extraButtons in between acceptance tests. When I run tests, if the tests where the button shou...","post_number":1,"topic_title_headline":"PostMenu’s ‘_extraButtons’ isn’t reset in between acceptance tests","topic_id":197887}],"topics":[{"id":49167,"title":"Write acceptance tests and component tests for Ember code in Discourse","fancy_title":"Write acceptance tests and component tests for Ember code in Discourse","slug":"write-acceptance-tests-and-component-tests-for-ember-code-in-discourse","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2016-08-24T20:48:02.492Z","last_posted_at":"2017-02-01T18:22:01.859Z","bumped":true,"bumped_at":"2017-02-01T18:22:01.859Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["tutorial","ember","testing"],"tags_descriptions":{},"category_id":56,"has_accepted_answer":false},{"id":32619,"title":"Developing Discourse Plugins - Part 6 - Add acceptance tests","fancy_title":"Developing Discourse Plugins - Part 6 - Add acceptance tests","slug":"developing-discourse-plugins-part-6-add-acceptance-tests","posts_count":33,"reply_count":26,"highest_post_number":38,"created_at":"2015-08-27T21:32:26.323Z","last_posted_at":"2022-06-02T11:06:38.274Z","bumped":true,"bumped_at":"2022-06-02T11:06:38.274Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["plugins","tutorial","plugin-guides","testing"],"tags_descriptions":{"plugins":""},"category_id":56,"has_accepted_answer":false},{"id":281579,"title":"End-to-end system testing for themes and theme components","fancy_title":"End-to-end system testing for themes and theme components","slug":"end-to-end-system-testing-for-themes-and-theme-components","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-10-24T23:13:37.118Z","last_posted_at":"2023-10-24T23:13:37.118Z","bumped":true,"bumped_at":"2023-11-13T23:20:01.596Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to","themes"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":66857,"title":"How to run Discourse core, plugin and theme QUnit test suites","fancy_title":"How to run Discourse core, plugin and theme QUnit test suites","slug":"how-to-run-discourse-core-plugin-and-theme-qunit-test-suites","posts_count":1,"reply_count":2,"highest_post_number":1,"created_at":"2017-07-26T14:09:58.032Z","last_posted_at":"2017-07-26T14:09:58.126Z","bumped":true,"bumped_at":"2023-09-04T17:56:33.079Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":17155,"title":"Test Discourse in mobile screen emulator","fancy_title":"Test Discourse in mobile screen emulator","slug":"test-discourse-in-mobile-screen-emulator","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2014-07-03T11:45:09.360Z","last_posted_at":"2014-10-12T22:19:24.137Z","bumped":true,"bumped_at":"2014-10-12T22:19:24.137Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":288363,"title":"Tip: when testing inbound email with fake user accounts...","fancy_title":"Tip: when testing inbound email with fake user accounts…","slug":"tip-when-testing-inbound-email-with-fake-user-accounts","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-12-12T10:51:07.868Z","last_posted_at":"2023-12-12T10:51:08.401Z","bumped":true,"bumped_at":"2023-12-12T10:51:08.401Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["email"],"tags_descriptions":{},"category_id":55,"has_accepted_answer":false},{"id":145744,"title":"Generate User API Keys for testing","fancy_title":"Generate User API Keys for testing","slug":"generate-user-api-keys-for-testing","posts_count":4,"reply_count":1,"highest_post_number":4,"created_at":"2020-03-26T21:31:38.269Z","last_posted_at":"2022-08-07T15:27:18.788Z","bumped":true,"bumped_at":"2022-08-07T15:27:18.788Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":58298,"title":"Build a sandbox to test changes before making them live","fancy_title":"Build a sandbox to test changes before making them live","slug":"build-a-sandbox-to-test-changes-before-making-them-live","posts_count":21,"reply_count":13,"highest_post_number":21,"created_at":"2017-03-03T13:34:07.921Z","last_posted_at":"2022-02-16T23:18:54.680Z","bumped":true,"bumped_at":"2022-02-16T23:18:54.680Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":55,"has_accepted_answer":false},{"id":118296,"title":"Migrate from tests-passed to stable","fancy_title":"Migrate from tests-passed to stable","slug":"migrate-from-tests-passed-to-stable","posts_count":12,"reply_count":7,"highest_post_number":12,"created_at":"2019-05-21T10:42:51.017Z","last_posted_at":"2019-06-21T15:55:53.072Z","bumped":true,"bumped_at":"2019-05-22T15:55:47.651Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":288705,"title":"502 Bad Gateway after online rebuild of tests-passed Production just now","fancy_title":"502 Bad Gateway after online rebuild of tests-passed Production just now","slug":"502-bad-gateway-after-online-rebuild-of-tests-passed-production-just-now","posts_count":6,"reply_count":1,"highest_post_number":6,"created_at":"2023-12-14T17:11:26.387Z","last_posted_at":"2024-01-13T17:37:27.535Z","bumped":true,"bumped_at":"2023-12-14T17:36:57.066Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":247546,"title":"Trouble on adding a simple unit test for Youtube oneboxing","fancy_title":"Trouble on adding a simple unit test for Youtube oneboxing","slug":"trouble-on-adding-a-simple-unit-test-for-youtube-oneboxing","posts_count":10,"reply_count":5,"highest_post_number":10,"created_at":"2022-12-02T20:49:55.713Z","last_posted_at":"2023-01-05T17:47:40.629Z","bumped":true,"bumped_at":"2022-12-06T17:47:28.771Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["onebox","testing"],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":274165,"title":"Strange QUnit behaviour?: test failing because setting value doesn't survive","fancy_title":"Strange QUnit behaviour?: test failing because setting value doesn’t survive","slug":"strange-qunit-behaviour-test-failing-because-setting-value-doesnt-survive","posts_count":5,"reply_count":2,"highest_post_number":5,"created_at":"2023-08-07T10:05:22.298Z","last_posted_at":"2023-09-06T11:19:24.509Z","bumped":true,"bumped_at":"2023-08-07T11:24:27.150Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":249167,"title":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","fancy_title":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","slug":"is-it-possible-to-override-the-site-object-with-own-fixture-during-front-end-tests-of-a-plugin","posts_count":4,"reply_count":0,"highest_post_number":4,"created_at":"2022-12-16T18:32:29.079Z","last_posted_at":"2022-12-28T21:57:56.070Z","bumped":true,"bumped_at":"2022-12-28T21:57:56.070Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":286355,"title":"Acceptance tests failing on Github Actions","fancy_title":"Acceptance tests failing on Github Actions","slug":"acceptance-tests-failing-on-github-actions","posts_count":6,"reply_count":0,"highest_post_number":6,"created_at":"2023-11-22T18:46:46.128Z","last_posted_at":"2023-11-24T13:59:40.100Z","bumped":true,"bumped_at":"2023-11-24T13:59:40.100Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":91312,"title":"There was a problem sending the test email","fancy_title":"There was a problem sending the test email","slug":"there-was-a-problem-sending-the-test-email","posts_count":8,"reply_count":5,"highest_post_number":8,"created_at":"2018-07-01T17:12:48.366Z","last_posted_at":"2018-08-01T09:28:21.935Z","bumped":true,"bumped_at":"2018-07-02T09:28:16.014Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":287022,"title":"Strange migration error in tests during GH workflow","fancy_title":"Strange migration error in tests during GH workflow","slug":"strange-migration-error-in-tests-during-gh-workflow","posts_count":6,"reply_count":3,"highest_post_number":6,"created_at":"2023-11-29T23:18:11.686Z","last_posted_at":"2023-12-31T15:09:05.633Z","bumped":true,"bumped_at":"2023-12-01T15:08:31.257Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":238372,"title":"Smtp doctor test using port 465 even though its configured to use 2525","fancy_title":"Smtp doctor test using port 465 even though its configured to use 2525","slug":"smtp-doctor-test-using-port-465-even-though-its-configured-to-use-2525","posts_count":12,"reply_count":7,"highest_post_number":12,"created_at":"2022-09-07T20:22:10.976Z","last_posted_at":"2022-10-09T00:21:55.656Z","bumped":true,"bumped_at":"2022-09-09T00:21:48.272Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":146326,"title":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","fancy_title":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","slug":"sidekiq-not-running-sidekiq-heartbeat-test-failed-restarting","posts_count":16,"reply_count":9,"highest_post_number":16,"created_at":"2020-03-31T18:51:44.061Z","last_posted_at":"2020-06-10T01:39:28.366Z","bumped":true,"bumped_at":"2020-05-11T01:39:26.054Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["unsupported-install"],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":255406,"title":"Tests for plugin that requires a plugin","fancy_title":"Tests for plugin that requires a plugin","slug":"tests-for-plugin-that-requires-a-plugin","posts_count":3,"reply_count":0,"highest_post_number":3,"created_at":"2023-02-16T22:12:10.344Z","last_posted_at":"2023-02-28T16:10:36.498Z","bumped":true,"bumped_at":"2023-02-28T20:21:06.122Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":246069,"title":"Need to restart Ember in order to test front-end changes","fancy_title":"Need to restart Ember in order to test front-end changes","slug":"need-to-restart-ember-in-order-to-test-front-end-changes","posts_count":5,"reply_count":1,"highest_post_number":5,"created_at":"2022-11-18T17:03:45.900Z","last_posted_at":"2022-11-18T18:17:53.648Z","bumped":true,"bumped_at":"2022-11-18T18:17:53.648Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":227090,"title":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","fancy_title":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","slug":"bitnami-discourse-vm-on-virtualbox-smtp-mail-server-for-testing","posts_count":3,"reply_count":0,"highest_post_number":3,"created_at":"2022-05-15T18:02:33.136Z","last_posted_at":"2022-05-16T09:10:43.794Z","bumped":true,"bumped_at":"2022-05-16T09:10:43.794Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["unsupported-install"],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":145781,"title":"Test emails sent but","fancy_title":"Test emails sent but","slug":"test-emails-sent-but","posts_count":5,"reply_count":2,"highest_post_number":5,"created_at":"2020-03-27T07:14:10.116Z","last_posted_at":"2020-04-29T02:52:31.927Z","bumped":true,"bumped_at":"2020-03-30T02:52:29.062Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":137496,"title":"Sidekiq heartbeat test failed, restarting","fancy_title":"Sidekiq heartbeat test failed, restarting","slug":"sidekiq-heartbeat-test-failed-restarting","posts_count":13,"reply_count":8,"highest_post_number":13,"created_at":"2020-01-01T11:20:33.492Z","last_posted_at":"2020-02-11T23:09:42.375Z","bumped":true,"bumped_at":"2020-01-12T23:09:39.730Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":212684,"title":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","fancy_title":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","slug":"mailgun-discourse-sandbox-subdomains-are-for-test-purposes-only-please-add-your-own-domain","posts_count":4,"reply_count":2,"highest_post_number":5,"created_at":"2021-12-20T13:52:10.405Z","last_posted_at":"2022-01-19T15:27:11.368Z","bumped":true,"bumped_at":"2021-12-20T15:26:24.911Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":102035,"title":"Test admin features without having to install Discourse","fancy_title":"Test admin features without having to install Discourse","slug":"test-admin-features-without-having-to-install-discourse","posts_count":6,"reply_count":2,"highest_post_number":6,"created_at":"2018-11-14T15:20:59.484Z","last_posted_at":"2021-09-09T07:35:18.270Z","bumped":true,"bumped_at":"2021-09-09T07:35:18.270Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":121299,"title":"Install discourse with a staging (test) ssl certificate","fancy_title":"Install discourse with a staging (test) ssl certificate","slug":"install-discourse-with-a-staging-test-ssl-certificate","posts_count":5,"reply_count":1,"highest_post_number":5,"created_at":"2019-06-25T17:12:34.552Z","last_posted_at":"2023-04-01T03:25:32.925Z","bumped":true,"bumped_at":"2019-09-04T15:15:31.153Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":118568,"title":"Problem testing Badge Query from Data Explorer","fancy_title":"Problem testing Badge Query from Data Explorer","slug":"problem-testing-badge-query-from-data-explorer","posts_count":5,"reply_count":1,"highest_post_number":6,"created_at":"2019-05-24T00:14:35.814Z","last_posted_at":"2019-05-24T00:46:42.560Z","bumped":true,"bumped_at":"2019-05-24T00:46:42.560Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["sql-triggered-badge"],"tags_descriptions":{"sql-triggered-badge":"SQL queries for custom triggered badges"},"category_id":148,"has_accepted_answer":true},{"id":115912,"title":"New iOS mobile app beta available for testing","fancy_title":"New iOS mobile app beta available for testing","slug":"new-ios-mobile-app-beta-available-for-testing","posts_count":49,"reply_count":31,"highest_post_number":49,"created_at":"2019-04-25T01:24:56.608Z","last_posted_at":"2019-05-31T17:31:02.516Z","bumped":true,"bumped_at":"2020-01-21T17:07:37.817Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":224560,"title":"502 Bad Gateway after trying to rebuild test-passed branch","fancy_title":"502 Bad Gateway after trying to rebuild test-passed branch","slug":"502-bad-gateway-after-trying-to-rebuild-test-passed-branch","posts_count":6,"reply_count":1,"highest_post_number":6,"created_at":"2022-04-17T21:46:04.598Z","last_posted_at":"2022-04-17T22:15:37.255Z","bumped":true,"bumped_at":"2022-04-17T22:15:37.255Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":false},{"id":173019,"title":"Forum offline due to failed rebuilds on Tests-Pass","fancy_title":"Forum offline due to failed rebuilds on Tests-Pass","slug":"forum-offline-due-to-failed-rebuilds-on-tests-pass","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2020-12-11T18:44:17.952Z","last_posted_at":"2020-12-11T19:12:11.141Z","bumped":true,"bumped_at":"2020-12-11T19:12:11.141Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":false},{"id":196501,"title":"[A/B Testing] Changing parent CSS class based on experiment variable","fancy_title":"[A/B Testing] Changing parent CSS class based on experiment variable","slug":"a-b-testing-changing-parent-css-class-based-on-experiment-variable","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2021-07-08T19:58:55.503Z","last_posted_at":"2021-07-15T18:19:21.092Z","bumped":true,"bumped_at":"2021-07-15T18:19:21.092Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":282856,"title":"Code-level performance testing","fancy_title":"Code-level performance testing","slug":"code-level-performance-testing","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2023-10-20T03:44:49.568Z","last_posted_at":"2023-10-23T23:29:59.741Z","bumped":true,"bumped_at":"2023-10-23T23:29:59.741Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":272112,"title":"Running core tests in docker environment","fancy_title":"Running core tests in docker environment","slug":"running-core-tests-in-docker-environment","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-07-19T12:45:57.266Z","last_posted_at":"2023-07-19T12:45:57.446Z","bumped":true,"bumped_at":"2023-07-19T12:45:57.446Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["docker","spec","testing"],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":87295,"title":"Email test from the console?","fancy_title":"Email test from the console?","slug":"email-test-from-the-console","posts_count":4,"reply_count":1,"highest_post_number":4,"created_at":"2018-05-11T22:03:23.101Z","last_posted_at":"2018-05-12T00:55:41.843Z","bumped":true,"bumped_at":"2018-05-12T00:55:41.843Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":59577,"title":"Plugin QUnit tests are not running as part of rake qunit:test","fancy_title":"Plugin QUnit tests are not running as part of rake qunit:test","slug":"plugin-qunit-tests-are-not-running-as-part-of-rake-qunit-test","posts_count":8,"reply_count":5,"highest_post_number":8,"created_at":"2017-03-20T20:29:06.536Z","last_posted_at":"2017-07-17T18:26:45.188Z","bumped":true,"bumped_at":"2017-07-17T18:26:45.188Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":181856,"title":"How does the Discourse team do usability testing?","fancy_title":"How does the Discourse team do usability testing?","slug":"how-does-the-discourse-team-do-usability-testing","posts_count":5,"reply_count":2,"highest_post_number":6,"created_at":"2021-03-03T18:09:29.357Z","last_posted_at":"2021-03-04T15:23:46.571Z","bumped":true,"bumped_at":"2021-03-04T15:23:46.571Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":3,"has_accepted_answer":false},{"id":108110,"title":"Advice on writing Ruby tests for plugins","fancy_title":"Advice on writing Ruby tests for plugins","slug":"advice-on-writing-ruby-tests-for-plugins","posts_count":15,"reply_count":13,"highest_post_number":15,"created_at":"2019-02-01T09:52:27.051Z","last_posted_at":"2019-05-03T03:54:47.163Z","bumped":true,"bumped_at":"2019-05-03T03:54:47.163Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":57292,"title":"Acceptance test content is present on page","fancy_title":"Acceptance test content is present on page","slug":"acceptance-test-content-is-present-on-page","posts_count":7,"reply_count":3,"highest_post_number":7,"created_at":"2017-02-13T02:48:10.108Z","last_posted_at":"2017-02-14T05:55:31.520Z","bumped":true,"bumped_at":"2017-02-14T05:55:31.520Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":291039,"title":"Google Search Console/Schema Markup test tool \"Google Rich Results Test\": mobile-view instead of crawler-view","fancy_title":"Google Search Console/Schema Markup test tool “Google Rich Results Test”: mobile-view instead of crawler-view","slug":"google-search-console-schema-markup-test-tool-google-rich-results-test-mobile-view-instead-of-crawler-view","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2024-01-09T16:53:57.797Z","last_posted_at":"2024-01-09T17:17:09.039Z","bumped":true,"bumped_at":"2024-01-09T17:17:09.039Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":1,"has_accepted_answer":false},{"id":267737,"title":"Experiments with Model-Based Testing","fancy_title":"Experiments with Model-Based Testing","slug":"experiments-with-model-based-testing","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-06-08T22:35:51.784Z","last_posted_at":"2023-06-08T22:35:51.914Z","bumped":true,"bumped_at":"2023-06-08T22:35:51.914Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":233389,"title":"Qunit tests not deterministic in Plugin?","fancy_title":"Qunit tests not deterministic in Plugin?","slug":"qunit-tests-not-deterministic-in-plugin","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2022-07-20T08:48:28.757Z","last_posted_at":"2022-07-20T11:03:42.522Z","bumped":true,"bumped_at":"2022-07-20T11:03:42.522Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":135876,"title":"Issues migrating test database","fancy_title":"Issues migrating test database","slug":"issues-migrating-test-database","posts_count":9,"reply_count":1,"highest_post_number":9,"created_at":"2019-12-12T21:01:31.303Z","last_posted_at":"2021-03-02T16:44:20.120Z","bumped":true,"bumped_at":"2021-03-02T16:44:20.120Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":175413,"title":"Acceptance Test for Markdown Extension?","fancy_title":"Acceptance Test for Markdown Extension?","slug":"acceptance-test-for-markdown-extension","posts_count":3,"reply_count":0,"highest_post_number":4,"created_at":"2021-01-07T19:56:04.931Z","last_posted_at":"2021-01-10T11:26:16.888Z","bumped":true,"bumped_at":"2021-01-10T16:30:56.164Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":150786,"title":"Auto migrate test database schema","fancy_title":"Auto migrate test database schema","slug":"auto-migrate-test-database-schema","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2020-05-08T04:27:56.739Z","last_posted_at":"2020-05-08T13:15:45.247Z","bumped":true,"bumped_at":"2020-05-08T13:15:45.247Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":118639,"title":"Building Tests for New discourse_api Endpoints","fancy_title":"Building Tests for New discourse_api Endpoints","slug":"building-tests-for-new-discourse-api-endpoints","posts_count":20,"reply_count":9,"highest_post_number":21,"created_at":"2019-05-24T22:21:24.896Z","last_posted_at":"2019-10-02T21:13:35.140Z","bumped":true,"bumped_at":"2019-10-02T21:36:26.639Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":126025,"title":"Seed or API calls to create test users","fancy_title":"Seed or API calls to create test users","slug":"seed-or-api-calls-to-create-test-users","posts_count":7,"reply_count":5,"highest_post_number":7,"created_at":"2019-08-17T02:13:06.578Z","last_posted_at":"2019-08-19T11:50:36.137Z","bumped":true,"bumped_at":"2019-08-19T11:50:36.137Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":71118,"title":"Smoke-testing plugins during upgrade process","fancy_title":"Smoke-testing plugins during upgrade process","slug":"smoke-testing-plugins-during-upgrade-process","posts_count":22,"reply_count":17,"highest_post_number":22,"created_at":"2017-10-01T08:18:48.067Z","last_posted_at":"2017-10-02T23:44:59.852Z","bumped":true,"bumped_at":"2017-10-02T23:44:59.852Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["pr-welcome"],"tags_descriptions":{"pr-welcome":"You're welcome to submit a Github pull request that implements this"},"category_id":2,"has_accepted_answer":false},{"id":62797,"title":"QUnit tests won't pass in discourse_dev docker image","fancy_title":"QUnit tests won’t pass in discourse_dev docker image","slug":"qunit-tests-wont-pass-in-discourse-dev-docker-image","posts_count":20,"reply_count":11,"highest_post_number":21,"created_at":"2017-05-16T10:28:58.397Z","last_posted_at":"2017-07-25T15:50:27.716Z","bumped":true,"bumped_at":"2017-07-25T15:50:27.716Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":42947,"title":"Testing a Discourse API client on Travis-CI","fancy_title":"Testing a Discourse API client on Travis-CI","slug":"testing-a-discourse-api-client-on-travis-ci","posts_count":5,"reply_count":3,"highest_post_number":5,"created_at":"2016-04-21T19:15:00.130Z","last_posted_at":"2016-04-22T03:03:47.976Z","bumped":true,"bumped_at":"2016-04-22T03:03:47.976Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":197887,"title":"PostMenu's '_extraButtons' isn't reset in between acceptance tests","fancy_title":"PostMenu’s ‘_extraButtons’ isn’t reset in between acceptance tests","slug":"postmenus-extrabuttons-isnt-reset-in-between-acceptance-tests","posts_count":2,"reply_count":0,"highest_post_number":4,"created_at":"2021-07-22T16:57:05.803Z","last_posted_at":"2021-08-04T09:11:29.538Z","bumped":true,"bumped_at":"2021-08-04T09:11:29.538Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false}],"users":[],"categories":[],"tags":[],"groups":[],"grouped_search_result":{"more_posts":null,"more_users":null,"more_categories":null,"term":"testing","search_log_id":2089836,"more_full_page_results":true,"can_create_topic":false,"error":null,"post_ids":[218354,138484,1381521,311252,59431,1419473,722424,266441,582008,1421414,1204506,1339808,1211823,1408389,443129,1412219,1160803,725228,1240758,1197679,1103913,722608,679950,1033333,498559,596557,583541,569209,1090520,860593,960683,1389188,1329017,421687,272436,899205,530438,260691,1432567,1305996,1135474,672801,871105,747613,583961,621707,334186,288991,187533,966756],"user_ids":[],"category_ids":[],"tag_ids":[],"group_ids":[],"extra":{"categories":[{"id":67,"name":"announcements","color":"ED207B","text_color":"FFFFFF","slug":"announcements","topic_count":312,"post_count":3973,"position":0,"description":"The place for all Discourse announcements.","description_text":"The place for all Discourse announcements.","description_excerpt":"The place for all Discourse announcements.","topic_url":"/t/about-the-announcements-category/68629","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_enabled":true,"activity_pub_username":"announcements","activity_pub_name":"Announcements","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["new-feature","security"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":2,"handle":"announcements@meta.discourse.org","name":"Announcements"},"activity_pub_username":"announcements","activity_pub_name":"Announcements","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Note","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":13,"name":"blog","color":"8080ff","text_color":"FFFFFF","slug":"blog","topic_count":170,"post_count":1477,"position":1,"description":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","description_text":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","description_excerpt":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","topic_url":"/t/about-the-blog-category/5250","read_restricted":false,"permission":null,"parent_category_id":67,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"created","sort_ascending":false,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":10,"name":"documentation","color":"00A94F","text_color":"FFFFFF","slug":"documentation","topic_count":2,"post_count":2,"position":2,"description":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","description_text":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","description_excerpt":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","topic_url":"/t/about-the-documentation-category/2629","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"top","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Article","activity_pub_publication_type":"full_topic","activity_pub_username":"documentation","activity_pub_enabled":true,"activity_pub_name":"Documentation"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":31769,"handle":"documentation@meta.discourse.org","name":"Documentation"},"activity_pub_username":"documentation","activity_pub_name":"Documentation","activity_pub_default_visibility":"public","activity_pub_publication_type":"full_topic","activity_pub_post_object_type":"Article","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":53,"name":"admins","color":"F15D22","text_color":"FFFFFF","slug":"admins","topic_count":222,"post_count":1703,"position":3,"description":"Guides for Discourse admins and community managers with admin access.","description_text":"Guides for Discourse admins and community managers with admin access.","description_excerpt":"Guides for Discourse admins and community managers with admin access.","topic_url":"/t/about-the-admins-category/56207","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["hosted-support","migrations"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":125,"name":"moderators","color":"40d0e2","text_color":"FFFFFF","slug":"moderators","topic_count":16,"post_count":71,"position":4,"description":"Documentation for moderators and community managers using Discourse.","description_text":"Documentation for moderators and community managers using Discourse.","description_excerpt":"Documentation for moderators and community managers using Discourse.","topic_url":"/t/about-the-moderators-category/238588","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":126,"name":"users","color":"0088CC","text_color":"FFFFFF","slug":"users","topic_count":53,"post_count":184,"position":5,"description":"Documentation for all members of communities running Discourse.","description_text":"Documentation for all members of communities running Discourse.","description_excerpt":"Documentation for all members of communities running Discourse.","topic_url":"/t/about-the-users-category/238917","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":55,"name":"sysadmin","color":"E9DD00","text_color":"FFFFFF","slug":"sysadmin","topic_count":175,"post_count":2803,"position":6,"description":"Documentation for self-hosters and Discourse system administrators.","description_text":"Documentation for self-hosters and Discourse system administrators.","description_excerpt":"Documentation for self-hosters and Discourse system administrators.","topic_url":"/t/about-the-sysadmin-category/56209","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":["migrations"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":127,"name":"theme developers","color":"92278F","text_color":"FFFFFF","slug":"theme-developers","topic_count":41,"post_count":215,"position":7,"description":"Documentation for developing themes and components that can be installed by admins.","description_text":"Documentation for developing themes and components that can be installed by admins.","description_excerpt":"Documentation for developing themes and components that can be installed by admins.","topic_url":"/t/about-the-theme-developers-category/239285","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":56,"name":"developers","color":"00A94F","text_color":"FFFFFF","slug":"devs","topic_count":96,"post_count":1185,"position":8,"description":"Documentation for developing features, plugins, or integrations with Discourse.","description_text":"Documentation for developing features, plugins, or integrations with Discourse.","description_excerpt":"Documentation for developing features, plugins, or integrations with Discourse.","topic_url":"/t/about-the-developers-category/56210","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Article","activity_pub_enabled":true,"activity_pub_username":"developer-docs","activity_pub_name":"Developer Documentation","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":35545,"handle":"developer-docs@meta.discourse.org","name":"Developer Documentation"},"activity_pub_username":"developer-docs","activity_pub_name":"Developer Documentation","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Article","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":6,"name":"support","color":"CEA9A9","text_color":"FFFFFF","slug":"support","topic_count":15841,"post_count":103687,"position":9,"description":"The category for general support questions on using your Discourse site.","description_text":"The category for general support questions on using your Discourse site.","description_excerpt":"The category for general support questions on using your Discourse site.","topic_url":"/t/about-the-support-category/389","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"\u003e Before asking, did you search first? Press 🔍 at the upper right to search.","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_accepted_answers":"true","enable_unassigned_filter":"false","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":21,"name":"wordpress","color":"F9CFCF","text_color":"FFFFFF","slug":"wordpress","topic_count":732,"post_count":5110,"position":10,"description":"Support for the official Discourse WordPress plugin at \u003ca href=\"https://github.com/discourse/wp-discourse\" class=\"inline-onebox\"\u003eGitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog\u003c/a\u003e","description_text":"Support for the official Discourse WordPress plugin at GitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog","description_excerpt":"Support for the official Discourse WordPress plugin at \u003ca href=\"https://github.com/discourse/wp-discourse\" class=\"inline-onebox\"\u003eGitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog\u003c/a\u003e","topic_url":"/t/about-the-wordpress-category/12282","read_restricted":false,"permission":null,"parent_category_id":6,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":1,"name":"bug","color":"e9dd00","text_color":"000000","slug":"bug","topic_count":5153,"post_count":35973,"position":11,"description":"A bug report means \u003cstrong\u003esomething is broken, preventing normal/typical use of Discourse\u003c/strong\u003e. Do be sure to search prior to submitting bugs. \u003ca href=\"https://meta.discourse.org/t/how-to-write-a-good-bug-report/183671/\"\u003eInclude repro steps\u003c/a\u003e, and only describe one bug per topic please.","description_text":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","description_excerpt":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. \u003ca href=\"https://meta.discourse.org/t/how-to-write-a-good-bug-report/183671/\"\u003eInclude repro steps\u003c/a\u003e, and only describe one bug per topic please.","topic_url":"/t/about-the-bug-category/2","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_accepted_answers":null,"enable_unassigned_filter":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":9,"name":"ux","color":"5F497A","text_color":"FFFFFF","slug":"ux","topic_count":2700,"post_count":18225,"position":12,"description":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","description_text":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","description_excerpt":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","topic_url":"/t/about-the-ux-category/2628","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":2,"name":"feature","color":"0E76BD","text_color":"FFFFFF","slug":"feature","topic_count":6997,"post_count":58067,"position":13,"description":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","description_text":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","description_excerpt":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","topic_url":"/t/about-the-feature-category/11","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_username":"feature","activity_pub_name":"Feature Requests","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post","activity_pub_enabled":true},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":1,"handle":"feature@meta.discourse.org","name":"Feature Requests"},"activity_pub_username":"feature","activity_pub_name":"Feature Requests","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Note","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":148,"name":"data \u0026 reporting","color":"D24899","text_color":"FFFFFF","slug":"data-reporting","topic_count":631,"post_count":3056,"position":14,"description":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","description_text":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","description_excerpt":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","topic_url":"/t/about-the-data-reporting-category/274664","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","enable_accepted_answers":"true","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":24,"name":"sso","color":"d47711","text_color":"FFFFFF","slug":"sso","topic_count":493,"post_count":2546,"position":15,"description":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the \u003ca href=\"https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045\"\u003eofficial documentation on DiscourseConnect SSO\u003c/a\u003e.","description_text":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the official documentation on DiscourseConnect SSO.","description_excerpt":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the \u003ca href=\"https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045\"\u003eofficial documentation on DiscourseConnect SSO\u003c/a\u003e.","topic_url":"/t/about-the-sso-category/13110","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":65,"name":"community","color":"12A89D","text_color":"FFFFFF","slug":"community","topic_count":866,"post_count":8844,"position":16,"description":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","description_text":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","description_excerpt":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","topic_url":"/t/about-the-community-category/67750","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":7,"name":"dev","color":"292929","text_color":"fff","slug":"dev","topic_count":3467,"post_count":20210,"position":17,"description":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","description_text":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","description_excerpt":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","topic_url":"/t/about-the-dev-category/1026","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":27,"name":"translations","color":"808281","text_color":"FFFFFF","slug":"translations","topic_count":297,"post_count":1832,"position":18,"description":"This category is for discussion about localizing Discourse.","description_text":"This category is for discussion about localizing Discourse.","description_excerpt":"This category is for discussion about localizing Discourse.","topic_url":"/t/about-the-translations-category/14549","read_restricted":false,"permission":null,"parent_category_id":7,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":14,"name":"marketplace","color":"8C6238","text_color":"FFFFFF","slug":"marketplace","topic_count":1162,"post_count":5865,"position":19,"description":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","description_text":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","description_excerpt":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","topic_url":"/t/about-the-marketplace-category/5425","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"What would you like done?\n\nWhen do you need it done?\n\nWhat is your budget, in $ USD that you can offer for this task?\n\n\u003c!-- We encourage caution and due diligence when engaging with potential contractors or clients. Verify their credentials, check previous work, and ensure a transparent and legitimate transaction. Always remember, your safety and security in the marketplace is your responsibility. --\u003e","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["delivered"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":22,"name":"plugin","color":"F7941D","text_color":"FFFFFF","slug":"plugin","topic_count":315,"post_count":10429,"position":20,"description":"A directory of Discourse plugins, both official and third-party.","description_text":"A directory of Discourse plugins, both official and third-party.","description_excerpt":"A directory of Discourse plugins, both official and third-party.","topic_url":"/t/about-the-plugin-category/12648","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"| | | |\n| - | - | - |\n| :information_source: | **Summary** | In a few words, what does this plugin do? |\n| :hammer_and_wrench: | **Repository Link** | \u003c\u003e |\n| :open_book: | **Install Guide** | [How to install plugins in Discourse](https://meta.discourse.org/t/install-plugins-in-discourse/19157) |\n\n\u003cbr\u003e \n\n### Features\n \nDescribe the major features of the plugin\n \n### Configuration\n \nInclude detailed steps on how to configure the plugin (include screenshots where necessary)\n \n### CHANGELOG\n- Add new bullets when major features are committed here\n \n### TODO\n- Add any #pr-welcome TODO tasks here","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":5,"name":"extras","color":"25AAE2","text_color":"FFFFFF","slug":"extras","topic_count":90,"post_count":985,"position":21,"description":"A directory of all extensions \u0026amp; integrations for Discourse which are \u003cem\u003enot\u003c/em\u003e Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","description_text":"A directory of all extensions \u0026amp; integrations for Discourse which are not Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","description_excerpt":"A directory of all extensions \u0026amp; integrations for Discourse which are not Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","topic_url":"/t/about-the-extras-category/28","read_restricted":false,"permission":null,"parent_category_id":22,"notification_level":1,"topic_template":"","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":61,"name":"theme","color":"E43D30","text_color":"FFFFFF","slug":"theme","topic_count":66,"post_count":2138,"position":22,"description":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","description_text":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","description_excerpt":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","topic_url":"/t/about-the-theme-category/60925","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"||||\n|-|-|-|\n| :information_source: | **Summary** | ADD SHORT SUMMARY \n| :eyeglasses:|**Preview**| PREVIEW_LINK |\n| :hammer_and_wrench:|**Repository**| REPOSITORY_LINK |\n| :question:|**Install Guide**|[How to install a theme or theme component](https://meta.discourse.org/t/how-do-i-install-a-theme-or-theme-component/63682)|\n| :open_book:|**New to Discourse Themes?**| [Beginner’s guide to using Discourse Themes](https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966)\n\n\u003c!-- Describe this theme in one or two sentences --\u003e\n\nShort description...\n\n\u003c!-- Add screenshots (if applicable) --\u003e\n\nScreenshots...\n\n\u003c!-- Add more details and explain the settings (if applicable) --\u003e\n\nDetailed description...\n","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"none","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["color-palette"],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":120,"name":"theme-component","color":"1dedf8","text_color":"FFFFFF","slug":"theme-component","topic_count":300,"post_count":7516,"position":23,"description":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","description_text":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","description_excerpt":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","topic_url":"/t/about-the-theme-component-category/232731","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"| | | |\n| - | - | - |\n| :information_source: | **Summary** | ADD SHORT SUMMARY |\n| :eyeglasses: |**Preview**| PREVIEW_LINK |\n| :hammer_and_wrench: | **Repository**| REPOSITORY_LINK |\n| :question: | **Install Guide** | [How to install a theme or theme component](https://meta.discourse.org/t/how-do-i-install-a-theme-or-theme-component/63682) |\n| :open_book: | **New to Discourse Themes?** | [Beginner’s guide to using Discourse Themes](https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966) |\n\n\u003c!-- Fill in \"repoName\" and \"repoURL\" for the automatic install button --\u003e\n\n[wrap=theme-install-button repoName=\"Component's name\" repoUrl=\"GitHub repository link\"]\nInstall this theme component\n[/wrap]\n\n\u003c!-- Describe this theme/component in one or two sentences --\u003e\n\nShort description...\n\n\u003c!-- Add screenshots (if applicable) --\u003e\n\nScreenshots...\n\n\u003c!-- Add more details and explain the settings (if applicable) --\u003e\n\nDetailed description...","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"none","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":31,"name":"installation","color":"997E7E","text_color":"FFFFFF","slug":"installation","topic_count":3426,"post_count":29611,"position":24,"description":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","description_text":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","description_excerpt":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","topic_url":"/t/about-the-installation-category/21019","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":106,"name":"migration","color":"652D90","text_color":"FFFFFF","slug":"migration","topic_count":215,"post_count":1558,"position":25,"description":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","description_text":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","description_excerpt":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","topic_url":"/t/about-the-migration-category/196969","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Migration"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":8,"name":"hosting","color":"00AEEF","text_color":"FFFFFF","slug":"hosting","topic_count":501,"post_count":4117,"position":26,"description":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","description_text":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","description_excerpt":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","topic_url":"/t/about-the-hosting-category/2626","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post","enable_accepted_answers":"true"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":30,"name":"releases","color":"BF1E2E","text_color":"FFFFFF","slug":"releases","topic_count":23,"post_count":104,"position":27,"description":"Outlining each official release of Discourse, and plans for future releases.","description_text":"Outlining each official release of Discourse, and plans for future releases.","description_excerpt":"Outlining each official release of Discourse, and plans for future releases.","topic_url":"/t/about-the-releases-category/20857","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"created","sort_ascending":false,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":3,"name":"site feedback","color":"888","text_color":"FFFFFF","slug":"site-feedback","topic_count":417,"post_count":3220,"position":28,"description":"Discussion about \u003ca href=\"http://meta.discourse.org\"\u003emeta.discourse.org\u003c/a\u003e itself - the organization of this forum, how it works, and how we can improve this site.","description_text":"Discussion about meta.discourse.org itself - the organization of this forum, how it works, and how we can improve this site.","description_excerpt":"Discussion about \u003ca href=\"http://meta.discourse.org\"\u003emeta.discourse.org\u003c/a\u003e itself - the organization of this forum, how it works, and how we can improve this site.","topic_url":"/t/about-the-site-feedback-category/24","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":152,"name":"theme feedback","color":"ED207B","text_color":"FFFFFF","slug":"theme-feedback","topic_count":11,"post_count":44,"position":29,"description":"This is the category to gather all the UX reports for \u003ca href=\"https://meta.discourse.org/t/we-have-a-new-default-theme-here-on-meta/284692\"\u003eour new theme on meta\u003c/a\u003e. It also uses the new Form Templates feature that I’ve been wanting to try out.","description_text":"This is the category to gather all the UX reports for our new theme on meta. It also uses the new Form Templates feature that I’ve been wanting to try out.","description_excerpt":"This is the category to gather all the UX reports for \u003ca href=\"https://meta.discourse.org/t/we-have-a-new-default-theme-here-on-meta/284692\"\u003eour new theme on meta\u003c/a\u003e. It also uses the new Form Templates feature that I’ve been wanting to try out.","topic_url":"/t/about-the-theme-feedback-category/284904","read_restricted":false,"permission":null,"parent_category_id":3,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[1],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":157,"name":"forum summaries","color":"72e9a7","text_color":"FFFFFF","slug":"forum-summaries","topic_count":4,"post_count":36,"position":30,"description":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summaries deliver the essence of the Discourse community’s heartbeat, right at your fingertips.","description_text":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summaries deliver the essence of the Discourse community’s heartbeat, right at your fingertips.","description_excerpt":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summarie\u0026hellip;","topic_url":"/t/about-the-forum-summaries-category/291765","read_restricted":false,"permission":null,"parent_category_id":3,"notification_level":0,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":35,"name":"praise","color":"9EB83B","text_color":"FFFFFF","slug":"praise","topic_count":294,"post_count":1091,"position":31,"description":"Have something nice to say about Discourse?","description_text":"Have something nice to say about Discourse?","description_excerpt":"Have something nice to say about Discourse?","topic_url":"/t/about-the-praise-category/30010","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":true,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":63,"name":"comparison","color":"F1592A","text_color":"FFFFFF","slug":"comparison","topic_count":11,"post_count":137,"position":32,"description":"Topics comparing Discourse to other platforms.","description_text":"Topics comparing Discourse to other platforms.","description_excerpt":"Topics comparing Discourse to other platforms.","topic_url":"/t/about-the-comparison-category/65736","read_restricted":false,"permission":null,"parent_category_id":35,"notification_level":1,"topic_template":"### About PLATFORM_NAME\n\nA blurb about the platform being compared to Discourse. \n\nhttp://link.to.website\n\n ### Previous discussions:\n\nlinks to previous discussions related\n\n### Importer status\n\nIs there an official Discourse importer? Where is it? ","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":105,"name":"community support program","color":"92278F","text_color":"FFFFFF","slug":"support-program","topic_count":4,"post_count":27,"position":33,"description":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","description_text":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","description_excerpt":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","topic_url":"/t/about-the-community-support-program-category/193906","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":124,"name":"General","color":"25AAE2","text_color":"FFFFFF","slug":"general","topic_count":155,"post_count":1408,"position":35,"description":"Create topics here that don’t fit into any other existing category.","description_text":"Create topics here that don’t fit into any other existing category.","description_excerpt":"Create topics here that don’t fit into any other existing category.","topic_url":"/t/about-the-general-category/237517","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":17,"name":"Uncategorized","color":"AB9364","text_color":"FFFFFF","slug":"uncategorized","topic_count":0,"post_count":0,"position":77,"description":"Topics that don't need a category, or don't fit into any other existing category.","description_text":"Topics that don't need a category, or don't fit into any other existing category.","description_excerpt":"Topics that don't need a category, or don't fit into any other existing category.","topic_url":"/t/","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false}]}}} \ No newline at end of file +{"posts":[{"id":218354,"name":"Robin Ward","username":"eviltrout","avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png","created_at":"2016-08-24T20:48:02.587Z","like_count":15,"blurb":"Automated tests are a great way to protect your code against future regressions. Many people are familiar with how to do this in our Rails codebase with http://rspec.info/ rspec , but the Javascript s...","post_number":1,"topic_title_headline":"Write acceptance tests and component tests for Ember code in Discourse","topic_id":49167},{"id":138484,"name":"Robin Ward","username":"eviltrout","avatar_template":"/user_avatar/meta.discourse.org/eviltrout/{size}/5275_2.png","created_at":"2015-08-27T21:32:26.407Z","like_count":29,"blurb":"Previous tutorial: https://meta.discourse.org/t/developing-discourse-plugins-part-5-add-an-admin-interface/31761 Developing Discourse Plugins - Part 5 - Add an admin interface Did you know that Discou...","post_number":1,"topic_title_headline":"Developing Discourse Plugins - Part 6 - Add acceptance tests","topic_id":32619},{"id":1381521,"name":"Alan Tan","username":"tgxworld","avatar_template":"/user_avatar/meta.discourse.org/tgxworld/{size}/106117_2.png","created_at":"2023-10-24T23:13:37.118Z","like_count":16,"blurb":"Writing automated tests for themes is an important part of the theme development process which can help ensure that the features being introduced by a theme continues to work well overtime with core D...","post_number":1,"topic_title_headline":"End-to-end system testing for themes and theme components","topic_id":281579},{"id":311252,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-07-26T14:09:58.126Z","like_count":17,"blurb":"Discourse has extensive frontend tests for core, plugins and themes. Once you have a functioning local development environment, those tests can be run locally in a number of different ways. Running te...","post_number":1,"topic_title_headline":"How to run Discourse core, plugin and theme QUnit test suites","topic_id":66857},{"id":59431,"name":"Erlend Sogge Heggen","username":"erlend_sh","avatar_template":"/user_avatar/meta.discourse.org/erlend_sh/{size}/119475_2.png","created_at":"2014-07-03T11:45:09.463Z","like_count":5,"blurb":"...view=1 to an URL, but the emulator has the added benefit of letting you select the screen profile of a specific device. I also tested all of the most popular online screen emulators, but unfortunately...","post_number":1,"topic_title_headline":"Test Discourse in mobile screen emulator","topic_id":17155},{"id":1419473,"name":"","username":"ToddZ","avatar_template":"/user_avatar/meta.discourse.org/toddz/{size}/328350_2.png","created_at":"2023-12-12T10:51:08.401Z","like_count":2,"blurb":"...amount of time troubleshooting inbound email because Discourse was rejecting every reply-by-email from my fake users. It had worked fine when I first tested several weeks ago… I finally realized that ...","post_number":1,"topic_title_headline":"Tip: when testing inbound email with fake user accounts…","topic_id":288363},{"id":722424,"name":"Falco","username":"Falco","avatar_template":"/user_avatar/meta.discourse.org/falco/{size}/179432_2.png","created_at":"2020-03-26T21:31:38.463Z","like_count":17,"blurb":"Continuing the discussion from https://meta.discourse.org/t/user-api-keys-specification/48536 User API keys specification : I created a small utility script in order to test User API keys locally. Fir...","post_number":1,"topic_title_headline":"Generate User API Keys for testing","topic_id":145744},{"id":266441,"name":"Andrew Waugh","username":"JagWaugh","avatar_template":"/user_avatar/meta.discourse.org/jagwaugh/{size}/69335_2.png","created_at":"2017-03-03T13:34:08.009Z","like_count":19,"blurb":"Regardless of if you're a moderator or an admin, you will no doubt at some time think about making some change to your live site and wonder if this will bring shame on you, and/or cause yourself an en...","post_number":1,"topic_title_headline":"Build a sandbox to test changes before making them live","topic_id":58298},{"id":582008,"name":"","username":"Wurzelseppi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/w/eada6e/{size}.png","created_at":"2019-05-21T10:42:51.099Z","like_count":0,"blurb":"Hi guys, just wanted to migrate from 2.3.0beta9 to stable release and got this error: What can I do here ? Caused by: PG::UndefinedColumn: ERROR: column \"email_private_messages\" of relation \"user_opti...","post_number":1,"topic_title_headline":"Migrate from tests-passed to stable","topic_id":118296},{"id":1421414,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-12-14T17:11:26.575Z","like_count":1,"blurb":"Restarting the server, restarting the container, console rebuilding doesn't help. Container is up as I can ./launcher enter app Got a bunch of these in logs, ideas on how to investigate redis failure?...","post_number":1,"topic_title_headline":"502 Bad Gateway after online rebuild of tests-passed Production just now","topic_id":288705},{"id":1204506,"name":"Coin-coin le Canapin","username":"Canapin","avatar_template":"/user_avatar/meta.discourse.org/canapin/{size}/119591_2.png","created_at":"2022-12-02T20:49:55.867Z","like_count":1,"blurb":"Hi! I want to add support of /shorts/ Youtube link. My modification of the YoutubeOnebox class works, but it is required that I add a test in https://github.com/discourse/discourse/blob/493d437e79f88a...","post_number":1,"topic_title_headline":"Trouble on adding a simple unit test for Youtube oneboxing","topic_id":247546},{"id":1339808,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-08-07T10:05:22.444Z","like_count":2,"blurb":"I have a strange issue with QUnit. This test is extremely simple and should be straightforward … but … A plugin setting is changing from those I have set up. https://github.com/paviliondev/discourse-l...","post_number":1,"topic_title_headline":"Strange QUnit behaviour?: test failing because setting value doesn’t survive","topic_id":274165},{"id":1211823,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2022-12-16T18:32:29.231Z","like_count":1,"blurb":"...presenting formatted location on User Card by merefield · Pull Request #73 · paviliondev/discourse-locations · GitHub I'm attempting to cover the change with a new Front End test. But the test fails t...","post_number":1,"topic_title_headline":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","topic_id":249167},{"id":1408389,"name":"Pierre Romera","username":"pirhoo","avatar_template":"/user_avatar/meta.discourse.org/pirhoo/{size}/120058_2.png","created_at":"2023-11-22T18:46:46.469Z","like_count":5,"blurb":"...m setting up a new plugin based on the https://github.com/discourse/discourse-plugin-skeleton/tree/main/assets skeleton you provided which already helped me a lot. I am now writing tests, both for the...","post_number":1,"topic_title_headline":"Acceptance tests failing on Github Actions","topic_id":286355},{"id":443129,"name":"JK Baseer","username":"JKBaseer","avatar_template":"/user_avatar/meta.discourse.org/jkbaseer/{size}/80471_2.png","created_at":"2018-07-01T17:12:48.446Z","like_count":0,"blurb":"...but still could myself. Background: I installed discourse using digitalocean oneclick installer. The website is running under http://forum.example.org forum.example.org without any problem except the ...","post_number":1,"topic_title_headline":"There was a problem sending the test email","topic_id":91312},{"id":1412219,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2023-11-29T23:18:11.815Z","like_count":1,"blurb":"I'm trying to create a foreign key relationship with the Topics table. The problem is it is failing in github workflow test environment during tests for the strangest reason, it is trying to access a ...","post_number":1,"topic_title_headline":"Strange migration error in tests during GH workflow","topic_id":287022},{"id":1160803,"name":"Bryan Joseph","username":"Bryan_Joseph","avatar_template":"/user_avatar/meta.discourse.org/bryan_joseph/{size}/273248_2.png","created_at":"2022-09-07T20:22:11.191Z","like_count":1,"blurb":"...SETTINGS ==================== DISCOURSE_HOSTNAME=url SMTP_ADDRESS=smtp.mailgun.org DEVELOPER_EMAILS=REDACTED SMTP_PASSWORD=REDACTED SMTP_PORT=2525 SMTP_USER_NAME=url LETSENCRYPT_ACCOUNT_EMAIL=REDACTED...","post_number":1,"topic_title_headline":"Smtp doctor test using port 465 even though its configured to use 2525","topic_id":238372},{"id":725228,"name":"james.network","username":"sunjam","avatar_template":"/user_avatar/meta.discourse.org/sunjam/{size}/175682_2.png","created_at":"2020-03-31T18:51:44.216Z","like_count":0,"blurb":"...Redis or updating it; it hasn't really been touched in the last 8+ months. I have not agentlly dealt with Redis before, but our Tests-Pass Discourse instance was setup using https://hub.docker.com/r...","post_number":1,"topic_title_headline":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","topic_id":146326},{"id":1240758,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2023-02-16T22:12:10.456Z","like_count":4,"blurb":"I see that the discourse-plugin-skeleton now has this: uses: discourse/.github/.github/workflows/discourse-plugin.yml@v1 so we don't have to keep updating stuff. But I have a plugin that requires the ...","post_number":1,"topic_title_headline":"Tests for plugin that requires a plugin","topic_id":255406},{"id":1197679,"name":"","username":"SilK","avatar_template":"/user_avatar/meta.discourse.org/silk/{size}/268124_2.png","created_at":"2022-11-18T17:03:46.056Z","like_count":0,"blurb":"...a new dev environment for working on plugins. Discourse is up to date with the main branch. I need to restart Ember in order to test changes made to the front end. This includes changes to Handlebars,...","post_number":1,"topic_title_headline":"Need to restart Ember in order to test front-end changes","topic_id":246069},{"id":1103913,"name":"Banibrata Dutta","username":"bdutta","avatar_template":"/user_avatar/meta.discourse.org/bdutta/{size}/259973_2.png","created_at":"2022-05-15T18:02:33.290Z","like_count":0,"blurb":"...only in a captive host-only testbed, so wondering if there is any local network SMTP daemon / service that I could start to complete the testing ? I'm happy with 100% command line mail client and serv...","post_number":1,"topic_title_headline":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","topic_id":227090},{"id":722608,"name":"Lona Lee","username":"Lona_Lee","avatar_template":"/user_avatar/meta.discourse.org/lona_lee/{size}/169072_2.png","created_at":"2020-03-27T07:14:10.239Z","like_count":1,"blurb":"Hello. I'm trying to get email setup working on my discourse instance. Done set-up properly and looks fine(no errors), so sent test emails. (logs confirmed : \"Admin\" - \"Emails\" - \"Sent\") However, I ha...","post_number":1,"topic_title_headline":"Test emails sent but","topic_id":145781},{"id":679950,"name":"Oleg Bovykin","username":"arrowcircle","avatar_template":"/user_avatar/meta.discourse.org/arrowcircle/{size}/100035_2.png","created_at":"2020-01-01T11:20:33.618Z","like_count":1,"blurb":"Hi! I found strange error in my admin page, that sidekiq is not running. I opened logs and found hundreds errors like: /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/logster-2.5.1/lib/logster/logger...","post_number":1,"topic_title_headline":"Sidekiq heartbeat test failed, restarting","topic_id":137496},{"id":1033333,"name":"М. М.","username":"М_М","avatar_template":"/user_avatar/meta.discourse.org/м_м/{size}/243710_2.png","created_at":"2021-12-20T13:52:10.488Z","like_count":0,"blurb":"...user, the logs say like this Job exception: could not get 3xx (421: 421 Domain sandbox410fe5c7bb85483c941c05b4ec5f3495.mailgun.org is not allowed to send: Sandbox subdomains are for test purposes only...","post_number":1,"topic_title_headline":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","topic_id":212684},{"id":498559,"name":"","username":"desrocchi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/eb9ed0/{size}.png","created_at":"2018-11-14T15:20:59.607Z","like_count":0,"blurb":"Is there a way for me to see or test the admin options in the demo area? I am just a moderator on the platform we use but I would like to see which options could be of use without having to install th...","post_number":1,"topic_title_headline":"Test admin features without having to install Discourse","topic_id":102035},{"id":596557,"name":"Flaviu","username":"UnivacTwo","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/u/df705f/{size}.png","created_at":"2019-06-25T17:12:34.710Z","like_count":0,"blurb":"Let's encrypt has a limit of how many certificates can be generated in a week for the same domain. Unfortunately we reach this limit and we cannot generate a new certificate this week. We did a backup...","post_number":1,"topic_title_headline":"Install discourse with a staging (test) ssl certificate","topic_id":121299},{"id":583541,"name":"mark78","username":"Mark_Schmucker","avatar_template":"/user_avatar/meta.discourse.org/mark_schmucker/{size}/124810_2.png","created_at":"2019-05-24T00:14:35.895Z","like_count":0,"blurb":"Should I be able to run any Badge Query in https://meta.discourse.org/t/32566 Data Explorer ? I want to create a custom Badge Query using \"Appreciated\" as a starting point. I type the Appreciated quer...","post_number":1,"topic_title_headline":"Problem testing Badge Query from Data Explorer","topic_id":118568},{"id":569209,"name":"Penar Musaraj","username":"pmusaraj","avatar_template":"/user_avatar/meta.discourse.org/pmusaraj/{size}/119489_2.png","created_at":"2019-04-25T01:24:56.741Z","like_count":26,"blurb":"...device and installing the app via TestFlight: https://testflight.apple.com/join/NkdBQgmg testflight.apple.com https://testflight.apple.com/join/NkdBQgmg TestFlight - Apple Using TestFlight is a great ...","post_number":1,"topic_title_headline":"New iOS mobile app beta available for testing","topic_id":115912},{"id":1090520,"name":"Mac玩儿法","username":"waerfa","avatar_template":"/user_avatar/meta.discourse.org/waerfa/{size}/216044_2.png","created_at":"2022-04-17T21:46:04.755Z","like_count":0,"blurb":"...rebuild the container: git pull ./launcher rebuild app I got the fatal error which shows: FAILED -------------------- Pups::ExecError: cd /var/www/discourse & & git fetch --depth 1 origin tests-passed...","post_number":1,"topic_title_headline":"502 Bad Gateway after trying to rebuild test-passed branch","topic_id":224560},{"id":860593,"name":"james.network","username":"sunjam","avatar_template":"/user_avatar/meta.discourse.org/sunjam/{size}/175682_2.png","created_at":"2020-12-11T18:44:18.021Z","like_count":1,"blurb":"Continuing the discussion from https://meta.discourse.org/t/postgresql-13-update/172563/27 PostgreSQL 13 update : Run into trouble while updating 2.7.0beta1 Tests-Pass in order to remove some troubles...","post_number":1,"topic_title_headline":"Forum offline due to failed rebuilds on Tests-Pass","topic_id":173019},{"id":960683,"name":"","username":"daniyal","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/58f4c7/{size}.png","created_at":"2021-07-08T19:58:55.634Z","like_count":3,"blurb":"...which we want to experiment. An example would be to experiment different styles of topic list view. For this we are using Google Optimize A/B testing. Currently we plan to show theme without changes t...","post_number":1,"topic_title_headline":"[A/B Testing] Changing parent CSS class based on experiment variable","topic_id":196501},{"id":1389188,"name":"Angus McLeod","username":"angus","avatar_template":"/user_avatar/meta.discourse.org/angus/{size}/341715_2.png","created_at":"2023-10-20T03:44:49.737Z","like_count":7,"blurb":"I've been looking at the performance of the https://meta.discourse.org/t/activitypub-plugin/266794 ActivityPub plugin recently and considering the best ways to reliably test, and prove, performance fo...","post_number":1,"topic_title_headline":"Code-level performance testing","topic_id":282856},{"id":1329017,"name":"","username":"dodibi","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/d/9fc348/{size}.png","created_at":"2023-07-19T12:45:57.446Z","like_count":0,"blurb":"Hello everyone! I'm currently facing some challenges while configuring my local environment to run discourse tests in a docker container. My main objective is to run the core tests with plugins attach...","post_number":1,"topic_title_headline":"Running core tests in docker environment","topic_id":272112},{"id":421687,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2018-05-11T22:03:23.232Z","like_count":0,"blurb":"Is there a way to initiate an email test from the Rails console? For a zillion reasons I would love to be able to send a test email without having to create an account. I've looked in config/routes.rb...","post_number":1,"topic_title_headline":"Email test from the console?","topic_id":87295},{"id":272436,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-03-20T20:29:06.608Z","like_count":1,"blurb":"It is my understanding that running rake qunit:test should run all of the qunit tests in Discourse, including those for any installed plugins. However, when I run the task in the docker development en...","post_number":1,"topic_title_headline":"Plugin QUnit tests are not running as part of rake qunit:test","topic_id":59577},{"id":899205,"name":"","username":"JQ331","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/j/41988e/{size}.png","created_at":"2021-03-03T18:09:29.452Z","like_count":6,"blurb":"I came across this https://blog.codinghorror.com/low-fi-usability-testing/ excellent article on how to do low-fi usability testing by @codinghorror . Usability testing (and user testing in general) is...","post_number":1,"topic_title_headline":"How does the Discourse team do usability testing?","topic_id":181856},{"id":530438,"name":"","username":"kleinfreund","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/k/a6a055/{size}.png","created_at":"2019-02-01T09:52:27.150Z","like_count":1,"blurb":"One can write tests for the backend of a plugin. For example, I created the following file in my plugin directory: spec/lib/route_store_spec.rb : require 'rails_helper' describe MyPlugin::RouteStore d...","post_number":1,"topic_title_headline":"Advice on writing Ruby tests for plugins","topic_id":108110},{"id":260691,"name":"Rimian Perkins","username":"rimian","avatar_template":"/user_avatar/meta.discourse.org/rimian/{size}/120658_2.png","created_at":"2017-02-13T02:48:10.177Z","like_count":0,"blurb":"What's the best way to (QUnit) assert an element on the page has some content in it? This passes: ok($.trim($('.foo').text()) == 'bar', 'content bar renders on page'); But isn't very practical. Is the...","post_number":1,"topic_title_headline":"Acceptance test content is present on page","topic_id":57292},{"id":1432567,"name":"Ayke","username":"rrit","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/r/b5ac83/{size}.png","created_at":"2024-01-09T16:53:57.916Z","like_count":0,"blurb":"Right now Discourse on meta.discourse.org serves the mobile-view instead of the crawler-view to the https://search.google.com/test/rich-results Google Rich Results Test . As there is no Schema Markup ...","post_number":1,"topic_title_headline":"Google Search Console/Schema Markup test tool “Google Rich Results Test”: mobile-view instead of crawler-view","topic_id":291039},{"id":1305996,"name":"Larry Diehl","username":"larrytheliquid","avatar_template":"/user_avatar/meta.discourse.org/larrytheliquid/{size}/310576_2.png","created_at":"2023-06-08T22:35:51.914Z","like_count":1,"blurb":"Hi Discourse Community! :slight_smile: I've been working on tech ( https://colimit.io Colimit ) that helps people apply https://en.wikipedia.org/wiki/Model-based_testing Model-based testing to test th...","post_number":1,"topic_title_headline":"Experiments with Model-Based Testing","topic_id":267737},{"id":1135474,"name":"Robert","username":"merefield","avatar_template":"/user_avatar/meta.discourse.org/merefield/{size}/176214_2.png","created_at":"2022-07-20T08:48:28.868Z","like_count":0,"blurb":"...my case running: rake \"plugin:qunit[discourse-multilingual]\" with a branch installed. I'm declaring a function in my initializer (i'm extending I18n ) The tests sometimes (25%?) seem to run before the...","post_number":1,"topic_title_headline":"Qunit tests not deterministic in Plugin?","topic_id":233389},{"id":672801,"name":"Jay Pfaffman","username":"pfaffman","avatar_template":"/user_avatar/meta.discourse.org/pfaffman/{size}/120154_2.png","created_at":"2019-12-12T21:01:31.303Z","like_count":0,"blurb":"Thanks, @Mittineague ! After a while, that made sense. I even wrote a spec, but even before I added my spec (and when I reverted to before I added any code), specs fail because: An error occurred whil...","post_number":1,"topic_title_headline":"Issues migrating test database","topic_id":135876},{"id":871105,"name":"","username":"Alteras","avatar_template":"/user_avatar/meta.discourse.org/alteras/{size}/179824_2.png","created_at":"2021-01-07T19:56:05.071Z","like_count":4,"blurb":"Hello! I'm currently working on a Markdown Extension/plugin that adds quite a number of BBCode tags, and I am looking to write QUnit Acceptance tests for them (I got really tired of constantly checkin...","post_number":1,"topic_title_headline":"Acceptance Test for Markdown Extension?","topic_id":175413},{"id":747613,"name":"","username":"xrav3nz","avatar_template":"/user_avatar/meta.discourse.org/xrav3nz/{size}/76894_2.png","created_at":"2020-05-08T04:27:56.920Z","like_count":8,"blurb":"...development - #2 by taylorthurlow - A May Of WTFs - Ruby on Rails Discussions Not sure if we have explored this before, but Rails can automatically maintain test databse schema with ActiveRecord::Migr...","post_number":1,"topic_title_headline":"Auto migrate test database schema","topic_id":150786},{"id":583961,"name":"Kim Miller","username":"kimardenmiller","avatar_template":"/user_avatar/meta.discourse.org/kimardenmiller/{size}/119631_2.png","created_at":"2019-05-24T22:21:24.996Z","like_count":3,"blurb":"Adding some polls API endpoints for PR to discourse_api, which work fine. Now I'm trying to understand how to create tests before submitting the PR, e.g.: require 'spec_helper' describe DiscourseApi::...","post_number":1,"topic_title_headline":"Building Tests for New discourse_api Endpoints","topic_id":118639},{"id":621707,"name":"Andrew Lank","username":"alank","avatar_template":"https://avatars.discourse-cdn.com/v4/letter/a/c89c15/{size}.png","created_at":"2019-08-17T02:13:06.679Z","like_count":1,"blurb":"In my development and testing I'm running a Discourse instance (Docker Discourse from Bitnami) and it fulfills most of my API testing for our API service which talks to Discourse, however I now need t...","post_number":1,"topic_title_headline":"Seed or API calls to create test users","topic_id":126025},{"id":334186,"name":"Chris","username":"ChrisBeach","avatar_template":"/user_avatar/meta.discourse.org/chrisbeach/{size}/214628_2.png","created_at":"2017-10-01T08:18:48.148Z","like_count":1,"blurb":"...from the core team. I propose that on hitting the upgrade button, a new docker image is built in the background, and within it, acceptance tests of all plugins are run before the switch-over happens f...","post_number":1,"topic_title_headline":"Smoke-testing plugins during upgrade process","topic_id":71118},{"id":288991,"name":"David Taylor","username":"david","avatar_template":"/user_avatar/meta.discourse.org/david/{size}/157490_2.png","created_at":"2017-05-16T10:28:58.496Z","like_count":1,"blurb":"I'm trying to use the docker image for tests both on my mac, and also https://meta.discourse.org/t/setting-up-plugin-continuous-integration-tests-on-travis-ci/59612 on travis . For a while now the qun...","post_number":1,"topic_title_headline":"QUnit tests won’t pass in discourse_dev docker image","topic_id":62797},{"id":187533,"name":"Sckott","username":"sckott","avatar_template":"/user_avatar/meta.discourse.org/sckott/{size}/115359_2.png","created_at":"2016-04-21T19:15:00.191Z","like_count":0,"blurb":"What's the best or fastest way to get Discourse installed on Travis for testing a client for the Discourse API ? It appears as though the discourse_api gem uses webmock so I think does not use a real ...","post_number":1,"topic_title_headline":"Testing a Discourse API client on Travis-CI","topic_id":42947},{"id":966756,"name":"Connor Parrish","username":"Connor_Parrish","avatar_template":"/user_avatar/meta.discourse.org/connor_parrish/{size}/225463_2.png","created_at":"2021-07-22T16:57:05.885Z","like_count":1,"blurb":"When you're conditionally adding a PostMenuButton using the plugin-api , the extra button is included in _extraButtons in between acceptance tests. When I run tests, if the tests where the button shou...","post_number":1,"topic_title_headline":"PostMenu’s ‘_extraButtons’ isn’t reset in between acceptance tests","topic_id":197887}],"topics":[{"id":49167,"title":"Write acceptance tests and component tests for Ember code in Discourse","fancy_title":"Write acceptance tests and component tests for Ember code in Discourse","slug":"write-acceptance-tests-and-component-tests-for-ember-code-in-discourse","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2016-08-24T20:48:02.492Z","last_posted_at":"2017-02-01T18:22:01.859Z","bumped":true,"bumped_at":"2017-02-01T18:22:01.859Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["tutorial","ember","testing"],"tags_descriptions":{},"category_id":56,"has_accepted_answer":false},{"id":32619,"title":"Developing Discourse Plugins - Part 6 - Add acceptance tests","fancy_title":"Developing Discourse Plugins - Part 6 - Add acceptance tests","slug":"developing-discourse-plugins-part-6-add-acceptance-tests","posts_count":33,"reply_count":26,"highest_post_number":38,"created_at":"2015-08-27T21:32:26.323Z","last_posted_at":"2022-06-02T11:06:38.274Z","bumped":true,"bumped_at":"2022-06-02T11:06:38.274Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["plugins","tutorial","plugin-guides","testing"],"tags_descriptions":{"plugins":""},"category_id":56,"has_accepted_answer":false},{"id":281579,"title":"End-to-end system testing for themes and theme components","fancy_title":"End-to-end system testing for themes and theme components","slug":"end-to-end-system-testing-for-themes-and-theme-components","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-10-24T23:13:37.118Z","last_posted_at":"2023-10-24T23:13:37.118Z","bumped":true,"bumped_at":"2023-11-13T23:20:01.596Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to","themes"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":66857,"title":"How to run Discourse core, plugin and theme QUnit test suites","fancy_title":"How to run Discourse core, plugin and theme QUnit test suites","slug":"how-to-run-discourse-core-plugin-and-theme-qunit-test-suites","posts_count":1,"reply_count":2,"highest_post_number":1,"created_at":"2017-07-26T14:09:58.032Z","last_posted_at":"2017-07-26T14:09:58.126Z","bumped":true,"bumped_at":"2023-09-04T17:56:33.079Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":17155,"title":"Test Discourse in mobile screen emulator","fancy_title":"Test Discourse in mobile screen emulator","slug":"test-discourse-in-mobile-screen-emulator","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2014-07-03T11:45:09.360Z","last_posted_at":"2014-10-12T22:19:24.137Z","bumped":true,"bumped_at":"2014-10-12T22:19:24.137Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":288363,"title":"Tip: when testing inbound email with fake user accounts...","fancy_title":"Tip: when testing inbound email with fake user accounts…","slug":"tip-when-testing-inbound-email-with-fake-user-accounts","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-12-12T10:51:07.868Z","last_posted_at":"2023-12-12T10:51:08.401Z","bumped":true,"bumped_at":"2023-12-12T10:51:08.401Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["email"],"tags_descriptions":{},"category_id":55,"has_accepted_answer":false},{"id":145744,"title":"Generate User API Keys for testing","fancy_title":"Generate User API Keys for testing","slug":"generate-user-api-keys-for-testing","posts_count":4,"reply_count":1,"highest_post_number":4,"created_at":"2020-03-26T21:31:38.269Z","last_posted_at":"2022-08-07T15:27:18.788Z","bumped":true,"bumped_at":"2022-08-07T15:27:18.788Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":56,"has_accepted_answer":false},{"id":58298,"title":"Build a sandbox to test changes before making them live","fancy_title":"Build a sandbox to test changes before making them live","slug":"build-a-sandbox-to-test-changes-before-making-them-live","posts_count":21,"reply_count":13,"highest_post_number":21,"created_at":"2017-03-03T13:34:07.921Z","last_posted_at":"2022-02-16T23:18:54.680Z","bumped":true,"bumped_at":"2022-02-16T23:18:54.680Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["how-to"],"tags_descriptions":{"how-to":"How to guides contain steps to follow to solve a specific problem"},"category_id":55,"has_accepted_answer":false},{"id":118296,"title":"Migrate from tests-passed to stable","fancy_title":"Migrate from tests-passed to stable","slug":"migrate-from-tests-passed-to-stable","posts_count":12,"reply_count":7,"highest_post_number":12,"created_at":"2019-05-21T10:42:51.017Z","last_posted_at":"2019-06-21T15:55:53.072Z","bumped":true,"bumped_at":"2019-05-22T15:55:47.651Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":288705,"title":"502 Bad Gateway after online rebuild of tests-passed Production just now","fancy_title":"502 Bad Gateway after online rebuild of tests-passed Production just now","slug":"502-bad-gateway-after-online-rebuild-of-tests-passed-production-just-now","posts_count":6,"reply_count":1,"highest_post_number":6,"created_at":"2023-12-14T17:11:26.387Z","last_posted_at":"2024-01-13T17:37:27.535Z","bumped":true,"bumped_at":"2023-12-14T17:36:57.066Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":247546,"title":"Trouble on adding a simple unit test for Youtube oneboxing","fancy_title":"Trouble on adding a simple unit test for Youtube oneboxing","slug":"trouble-on-adding-a-simple-unit-test-for-youtube-oneboxing","posts_count":10,"reply_count":5,"highest_post_number":10,"created_at":"2022-12-02T20:49:55.713Z","last_posted_at":"2023-01-05T17:47:40.629Z","bumped":true,"bumped_at":"2022-12-06T17:47:28.771Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["onebox","testing"],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":274165,"title":"Strange QUnit behaviour?: test failing because setting value doesn't survive","fancy_title":"Strange QUnit behaviour?: test failing because setting value doesn’t survive","slug":"strange-qunit-behaviour-test-failing-because-setting-value-doesnt-survive","posts_count":5,"reply_count":2,"highest_post_number":5,"created_at":"2023-08-07T10:05:22.298Z","last_posted_at":"2023-09-06T11:19:24.509Z","bumped":true,"bumped_at":"2023-08-07T11:24:27.150Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":249167,"title":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","fancy_title":"Is it possible to override the Site object with own fixture during Front End tests of a Plugin?","slug":"is-it-possible-to-override-the-site-object-with-own-fixture-during-front-end-tests-of-a-plugin","posts_count":4,"reply_count":0,"highest_post_number":4,"created_at":"2022-12-16T18:32:29.079Z","last_posted_at":"2022-12-28T21:57:56.070Z","bumped":true,"bumped_at":"2022-12-28T21:57:56.070Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":286355,"title":"Acceptance tests failing on Github Actions","fancy_title":"Acceptance tests failing on Github Actions","slug":"acceptance-tests-failing-on-github-actions","posts_count":6,"reply_count":0,"highest_post_number":6,"created_at":"2023-11-22T18:46:46.128Z","last_posted_at":"2023-11-24T13:59:40.100Z","bumped":true,"bumped_at":"2023-11-24T13:59:40.100Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":91312,"title":"There was a problem sending the test email","fancy_title":"There was a problem sending the test email","slug":"there-was-a-problem-sending-the-test-email","posts_count":8,"reply_count":5,"highest_post_number":8,"created_at":"2018-07-01T17:12:48.366Z","last_posted_at":"2018-08-01T09:28:21.935Z","bumped":true,"bumped_at":"2018-07-02T09:28:16.014Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":287022,"title":"Strange migration error in tests during GH workflow","fancy_title":"Strange migration error in tests during GH workflow","slug":"strange-migration-error-in-tests-during-gh-workflow","posts_count":6,"reply_count":3,"highest_post_number":6,"created_at":"2023-11-29T23:18:11.686Z","last_posted_at":"2023-12-31T15:09:05.633Z","bumped":true,"bumped_at":"2023-12-01T15:08:31.257Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":238372,"title":"Smtp doctor test using port 465 even though its configured to use 2525","fancy_title":"Smtp doctor test using port 465 even though its configured to use 2525","slug":"smtp-doctor-test-using-port-465-even-though-its-configured-to-use-2525","posts_count":12,"reply_count":7,"highest_post_number":12,"created_at":"2022-09-07T20:22:10.976Z","last_posted_at":"2022-10-09T00:21:55.656Z","bumped":true,"bumped_at":"2022-09-09T00:21:48.272Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":146326,"title":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","fancy_title":"Sidekiq not running. Sidekiq heartbeat test failed, restarting","slug":"sidekiq-not-running-sidekiq-heartbeat-test-failed-restarting","posts_count":16,"reply_count":9,"highest_post_number":16,"created_at":"2020-03-31T18:51:44.061Z","last_posted_at":"2020-06-10T01:39:28.366Z","bumped":true,"bumped_at":"2020-05-11T01:39:26.054Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["unsupported-install"],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":255406,"title":"Tests for plugin that requires a plugin","fancy_title":"Tests for plugin that requires a plugin","slug":"tests-for-plugin-that-requires-a-plugin","posts_count":3,"reply_count":0,"highest_post_number":3,"created_at":"2023-02-16T22:12:10.344Z","last_posted_at":"2023-02-28T16:10:36.498Z","bumped":true,"bumped_at":"2023-02-28T20:21:06.122Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":246069,"title":"Need to restart Ember in order to test front-end changes","fancy_title":"Need to restart Ember in order to test front-end changes","slug":"need-to-restart-ember-in-order-to-test-front-end-changes","posts_count":5,"reply_count":1,"highest_post_number":5,"created_at":"2022-11-18T17:03:45.900Z","last_posted_at":"2022-11-18T18:17:53.648Z","bumped":true,"bumped_at":"2022-11-18T18:17:53.648Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":true},{"id":227090,"title":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","fancy_title":"Bitnami Discourse VM on Virtualbox + SMTP mail-server for testing","slug":"bitnami-discourse-vm-on-virtualbox-smtp-mail-server-for-testing","posts_count":3,"reply_count":0,"highest_post_number":3,"created_at":"2022-05-15T18:02:33.136Z","last_posted_at":"2022-05-16T09:10:43.794Z","bumped":true,"bumped_at":"2022-05-16T09:10:43.794Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["unsupported-install"],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":145781,"title":"Test emails sent but","fancy_title":"Test emails sent but","slug":"test-emails-sent-but","posts_count":5,"reply_count":2,"highest_post_number":5,"created_at":"2020-03-27T07:14:10.116Z","last_posted_at":"2020-04-29T02:52:31.927Z","bumped":true,"bumped_at":"2020-03-30T02:52:29.062Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":137496,"title":"Sidekiq heartbeat test failed, restarting","fancy_title":"Sidekiq heartbeat test failed, restarting","slug":"sidekiq-heartbeat-test-failed-restarting","posts_count":13,"reply_count":8,"highest_post_number":13,"created_at":"2020-01-01T11:20:33.492Z","last_posted_at":"2020-02-11T23:09:42.375Z","bumped":true,"bumped_at":"2020-01-12T23:09:39.730Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":212684,"title":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","fancy_title":"MailGun & Discourse: Sandbox subdomains are for test purposes only. Please add your own domain","slug":"mailgun-discourse-sandbox-subdomains-are-for-test-purposes-only-please-add-your-own-domain","posts_count":4,"reply_count":2,"highest_post_number":5,"created_at":"2021-12-20T13:52:10.405Z","last_posted_at":"2022-01-19T15:27:11.368Z","bumped":true,"bumped_at":"2021-12-20T15:26:24.911Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":102035,"title":"Test admin features without having to install Discourse","fancy_title":"Test admin features without having to install Discourse","slug":"test-admin-features-without-having-to-install-discourse","posts_count":6,"reply_count":2,"highest_post_number":6,"created_at":"2018-11-14T15:20:59.484Z","last_posted_at":"2021-09-09T07:35:18.270Z","bumped":true,"bumped_at":"2021-09-09T07:35:18.270Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":121299,"title":"Install discourse with a staging (test) ssl certificate","fancy_title":"Install discourse with a staging (test) ssl certificate","slug":"install-discourse-with-a-staging-test-ssl-certificate","posts_count":5,"reply_count":1,"highest_post_number":5,"created_at":"2019-06-25T17:12:34.552Z","last_posted_at":"2023-04-01T03:25:32.925Z","bumped":true,"bumped_at":"2019-09-04T15:15:31.153Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":true},{"id":118568,"title":"Problem testing Badge Query from Data Explorer","fancy_title":"Problem testing Badge Query from Data Explorer","slug":"problem-testing-badge-query-from-data-explorer","posts_count":5,"reply_count":1,"highest_post_number":6,"created_at":"2019-05-24T00:14:35.814Z","last_posted_at":"2019-05-24T00:46:42.560Z","bumped":true,"bumped_at":"2019-05-24T00:46:42.560Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":true,"archived":false,"bookmarked":null,"liked":null,"tags":["sql-triggered-badge"],"tags_descriptions":{"sql-triggered-badge":"SQL queries for custom triggered badges"},"category_id":148,"has_accepted_answer":true},{"id":115912,"title":"New iOS mobile app beta available for testing","fancy_title":"New iOS mobile app beta available for testing","slug":"new-ios-mobile-app-beta-available-for-testing","posts_count":49,"reply_count":31,"highest_post_number":49,"created_at":"2019-04-25T01:24:56.608Z","last_posted_at":"2019-05-31T17:31:02.516Z","bumped":true,"bumped_at":"2020-01-21T17:07:37.817Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":224560,"title":"502 Bad Gateway after trying to rebuild test-passed branch","fancy_title":"502 Bad Gateway after trying to rebuild test-passed branch","slug":"502-bad-gateway-after-trying-to-rebuild-test-passed-branch","posts_count":6,"reply_count":1,"highest_post_number":6,"created_at":"2022-04-17T21:46:04.598Z","last_posted_at":"2022-04-17T22:15:37.255Z","bumped":true,"bumped_at":"2022-04-17T22:15:37.255Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":false},{"id":173019,"title":"Forum offline due to failed rebuilds on Tests-Pass","fancy_title":"Forum offline due to failed rebuilds on Tests-Pass","slug":"forum-offline-due-to-failed-rebuilds-on-tests-pass","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2020-12-11T18:44:17.952Z","last_posted_at":"2020-12-11T19:12:11.141Z","bumped":true,"bumped_at":"2020-12-11T19:12:11.141Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":31,"has_accepted_answer":false},{"id":196501,"title":"[A/B Testing] Changing parent CSS class based on experiment variable","fancy_title":"[A/B Testing] Changing parent CSS class based on experiment variable","slug":"a-b-testing-changing-parent-css-class-based-on-experiment-variable","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2021-07-08T19:58:55.503Z","last_posted_at":"2021-07-15T18:19:21.092Z","bumped":true,"bumped_at":"2021-07-15T18:19:21.092Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":282856,"title":"Code-level performance testing","fancy_title":"Code-level performance testing","slug":"code-level-performance-testing","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2023-10-20T03:44:49.568Z","last_posted_at":"2023-10-23T23:29:59.741Z","bumped":true,"bumped_at":"2023-10-23T23:29:59.741Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":272112,"title":"Running core tests in docker environment","fancy_title":"Running core tests in docker environment","slug":"running-core-tests-in-docker-environment","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-07-19T12:45:57.266Z","last_posted_at":"2023-07-19T12:45:57.446Z","bumped":true,"bumped_at":"2023-07-19T12:45:57.446Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["docker","spec","testing"],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":87295,"title":"Email test from the console?","fancy_title":"Email test from the console?","slug":"email-test-from-the-console","posts_count":4,"reply_count":1,"highest_post_number":4,"created_at":"2018-05-11T22:03:23.101Z","last_posted_at":"2018-05-12T00:55:41.843Z","bumped":true,"bumped_at":"2018-05-12T00:55:41.843Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":59577,"title":"Plugin QUnit tests are not running as part of rake qunit:test","fancy_title":"Plugin QUnit tests are not running as part of rake qunit:test","slug":"plugin-qunit-tests-are-not-running-as-part-of-rake-qunit-test","posts_count":8,"reply_count":5,"highest_post_number":8,"created_at":"2017-03-20T20:29:06.536Z","last_posted_at":"2017-07-17T18:26:45.188Z","bumped":true,"bumped_at":"2017-07-17T18:26:45.188Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":181856,"title":"How does the Discourse team do usability testing?","fancy_title":"How does the Discourse team do usability testing?","slug":"how-does-the-discourse-team-do-usability-testing","posts_count":5,"reply_count":2,"highest_post_number":6,"created_at":"2021-03-03T18:09:29.357Z","last_posted_at":"2021-03-04T15:23:46.571Z","bumped":true,"bumped_at":"2021-03-04T15:23:46.571Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":3,"has_accepted_answer":false},{"id":108110,"title":"Advice on writing Ruby tests for plugins","fancy_title":"Advice on writing Ruby tests for plugins","slug":"advice-on-writing-ruby-tests-for-plugins","posts_count":15,"reply_count":13,"highest_post_number":15,"created_at":"2019-02-01T09:52:27.051Z","last_posted_at":"2019-05-03T03:54:47.163Z","bumped":true,"bumped_at":"2019-05-03T03:54:47.163Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":57292,"title":"Acceptance test content is present on page","fancy_title":"Acceptance test content is present on page","slug":"acceptance-test-content-is-present-on-page","posts_count":7,"reply_count":3,"highest_post_number":7,"created_at":"2017-02-13T02:48:10.108Z","last_posted_at":"2017-02-14T05:55:31.520Z","bumped":true,"bumped_at":"2017-02-14T05:55:31.520Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":291039,"title":"Google Search Console/Schema Markup test tool \"Google Rich Results Test\": mobile-view instead of crawler-view","fancy_title":"Google Search Console/Schema Markup test tool “Google Rich Results Test”: mobile-view instead of crawler-view","slug":"google-search-console-schema-markup-test-tool-google-rich-results-test-mobile-view-instead-of-crawler-view","posts_count":3,"reply_count":1,"highest_post_number":3,"created_at":"2024-01-09T16:53:57.797Z","last_posted_at":"2024-01-09T17:17:09.039Z","bumped":true,"bumped_at":"2024-01-09T17:17:09.039Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":1,"has_accepted_answer":false},{"id":267737,"title":"Experiments with Model-Based Testing","fancy_title":"Experiments with Model-Based Testing","slug":"experiments-with-model-based-testing","posts_count":1,"reply_count":0,"highest_post_number":1,"created_at":"2023-06-08T22:35:51.784Z","last_posted_at":"2023-06-08T22:35:51.914Z","bumped":true,"bumped_at":"2023-06-08T22:35:51.914Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":233389,"title":"Qunit tests not deterministic in Plugin?","fancy_title":"Qunit tests not deterministic in Plugin?","slug":"qunit-tests-not-deterministic-in-plugin","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2022-07-20T08:48:28.757Z","last_posted_at":"2022-07-20T11:03:42.522Z","bumped":true,"bumped_at":"2022-07-20T11:03:42.522Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":135876,"title":"Issues migrating test database","fancy_title":"Issues migrating test database","slug":"issues-migrating-test-database","posts_count":9,"reply_count":1,"highest_post_number":9,"created_at":"2019-12-12T21:01:31.303Z","last_posted_at":"2021-03-02T16:44:20.120Z","bumped":true,"bumped_at":"2021-03-02T16:44:20.120Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":175413,"title":"Acceptance Test for Markdown Extension?","fancy_title":"Acceptance Test for Markdown Extension?","slug":"acceptance-test-for-markdown-extension","posts_count":3,"reply_count":0,"highest_post_number":4,"created_at":"2021-01-07T19:56:04.931Z","last_posted_at":"2021-01-10T11:26:16.888Z","bumped":true,"bumped_at":"2021-01-10T16:30:56.164Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":150786,"title":"Auto migrate test database schema","fancy_title":"Auto migrate test database schema","slug":"auto-migrate-test-database-schema","posts_count":2,"reply_count":0,"highest_post_number":2,"created_at":"2020-05-08T04:27:56.739Z","last_posted_at":"2020-05-08T13:15:45.247Z","bumped":true,"bumped_at":"2020-05-08T13:15:45.247Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":118639,"title":"Building Tests for New discourse_api Endpoints","fancy_title":"Building Tests for New discourse_api Endpoints","slug":"building-tests-for-new-discourse-api-endpoints","posts_count":20,"reply_count":9,"highest_post_number":21,"created_at":"2019-05-24T22:21:24.896Z","last_posted_at":"2019-10-02T21:13:35.140Z","bumped":true,"bumped_at":"2019-10-02T21:36:26.639Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":126025,"title":"Seed or API calls to create test users","fancy_title":"Seed or API calls to create test users","slug":"seed-or-api-calls-to-create-test-users","posts_count":7,"reply_count":5,"highest_post_number":7,"created_at":"2019-08-17T02:13:06.578Z","last_posted_at":"2019-08-19T11:50:36.137Z","bumped":true,"bumped_at":"2019-08-19T11:50:36.137Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":71118,"title":"Smoke-testing plugins during upgrade process","fancy_title":"Smoke-testing plugins during upgrade process","slug":"smoke-testing-plugins-during-upgrade-process","posts_count":22,"reply_count":17,"highest_post_number":22,"created_at":"2017-10-01T08:18:48.067Z","last_posted_at":"2017-10-02T23:44:59.852Z","bumped":true,"bumped_at":"2017-10-02T23:44:59.852Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":["pr-welcome"],"tags_descriptions":{"pr-welcome":"You're welcome to submit a Github pull request that implements this"},"category_id":2,"has_accepted_answer":false},{"id":62797,"title":"QUnit tests won't pass in discourse_dev docker image","fancy_title":"QUnit tests won’t pass in discourse_dev docker image","slug":"qunit-tests-wont-pass-in-discourse-dev-docker-image","posts_count":20,"reply_count":11,"highest_post_number":21,"created_at":"2017-05-16T10:28:58.397Z","last_posted_at":"2017-07-25T15:50:27.716Z","bumped":true,"bumped_at":"2017-07-25T15:50:27.716Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":42947,"title":"Testing a Discourse API client on Travis-CI","fancy_title":"Testing a Discourse API client on Travis-CI","slug":"testing-a-discourse-api-client-on-travis-ci","posts_count":5,"reply_count":3,"highest_post_number":5,"created_at":"2016-04-21T19:15:00.130Z","last_posted_at":"2016-04-22T03:03:47.976Z","bumped":true,"bumped_at":"2016-04-22T03:03:47.976Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false},{"id":197887,"title":"PostMenu's '_extraButtons' isn't reset in between acceptance tests","fancy_title":"PostMenu’s ‘_extraButtons’ isn’t reset in between acceptance tests","slug":"postmenus-extrabuttons-isnt-reset-in-between-acceptance-tests","posts_count":2,"reply_count":0,"highest_post_number":4,"created_at":"2021-07-22T16:57:05.803Z","last_posted_at":"2021-08-04T09:11:29.538Z","bumped":true,"bumped_at":"2021-08-04T09:11:29.538Z","archetype":"regular","unseen":false,"pinned":false,"unpinned":null,"visible":true,"closed":false,"archived":false,"bookmarked":null,"liked":null,"tags":[],"tags_descriptions":{},"category_id":7,"has_accepted_answer":false}],"users":[],"categories":[],"tags":[],"groups":[],"grouped_search_result":{"more_posts":null,"more_users":null,"more_categories":null,"term":"testing","search_log_id":2089836,"more_full_page_results":true,"can_create_topic":false,"error":null,"post_ids":[218354,138484,1381521,311252,59431,1419473,722424,266441,582008,1421414,1204506,1339808,1211823,1408389,443129,1412219,1160803,725228,1240758,1197679,1103913,722608,679950,1033333,498559,596557,583541,569209,1090520,860593,960683,1389188,1329017,421687,272436,899205,530438,260691,1432567,1305996,1135474,672801,871105,747613,583961,621707,334186,288991,187533,966756],"user_ids":[],"category_ids":[],"tag_ids":[],"group_ids":[],"extra":{"categories":[{"id":67,"name":"announcements","color":"ED207B","text_color":"FFFFFF","slug":"announcements","topic_count":312,"post_count":3973,"position":0,"description":"The place for all Discourse announcements.","description_text":"The place for all Discourse announcements.","description_excerpt":"The place for all Discourse announcements.","topic_url":"/t/about-the-announcements-category/68629","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_enabled":true,"activity_pub_username":"announcements","activity_pub_name":"Announcements","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["new-feature","security"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":2,"handle":"announcements@meta.discourse.org","name":"Announcements"},"activity_pub_username":"announcements","activity_pub_name":"Announcements","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Note","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":13,"name":"blog","color":"8080ff","text_color":"FFFFFF","slug":"blog","topic_count":170,"post_count":1477,"position":1,"description":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","description_text":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","description_excerpt":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","topic_url":"/t/about-the-blog-category/5250","read_restricted":false,"permission":null,"parent_category_id":67,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"created","sort_ascending":false,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":10,"name":"documentation","color":"00A94F","text_color":"FFFFFF","slug":"documentation","topic_count":2,"post_count":2,"position":2,"description":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","description_text":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","description_excerpt":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","topic_url":"/t/about-the-documentation-category/2629","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"top","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Article","activity_pub_publication_type":"full_topic","activity_pub_username":"documentation","activity_pub_enabled":true,"activity_pub_name":"Documentation"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":31769,"handle":"documentation@meta.discourse.org","name":"Documentation"},"activity_pub_username":"documentation","activity_pub_name":"Documentation","activity_pub_default_visibility":"public","activity_pub_publication_type":"full_topic","activity_pub_post_object_type":"Article","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":53,"name":"admins","color":"F15D22","text_color":"FFFFFF","slug":"admins","topic_count":222,"post_count":1703,"position":3,"description":"Guides for Discourse admins and community managers with admin access.","description_text":"Guides for Discourse admins and community managers with admin access.","description_excerpt":"Guides for Discourse admins and community managers with admin access.","topic_url":"/t/about-the-admins-category/56207","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["hosted-support","migrations"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":125,"name":"moderators","color":"40d0e2","text_color":"FFFFFF","slug":"moderators","topic_count":16,"post_count":71,"position":4,"description":"Documentation for moderators and community managers using Discourse.","description_text":"Documentation for moderators and community managers using Discourse.","description_excerpt":"Documentation for moderators and community managers using Discourse.","topic_url":"/t/about-the-moderators-category/238588","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":126,"name":"users","color":"0088CC","text_color":"FFFFFF","slug":"users","topic_count":53,"post_count":184,"position":5,"description":"Documentation for all members of communities running Discourse.","description_text":"Documentation for all members of communities running Discourse.","description_excerpt":"Documentation for all members of communities running Discourse.","topic_url":"/t/about-the-users-category/238917","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":55,"name":"sysadmin","color":"E9DD00","text_color":"FFFFFF","slug":"sysadmin","topic_count":175,"post_count":2803,"position":6,"description":"Documentation for self-hosters and Discourse system administrators.","description_text":"Documentation for self-hosters and Discourse system administrators.","description_excerpt":"Documentation for self-hosters and Discourse system administrators.","topic_url":"/t/about-the-sysadmin-category/56209","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":["migrations"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":127,"name":"theme developers","color":"92278F","text_color":"FFFFFF","slug":"theme-developers","topic_count":41,"post_count":215,"position":7,"description":"Documentation for developing themes and components that can be installed by admins.","description_text":"Documentation for developing themes and components that can be installed by admins.","description_excerpt":"Documentation for developing themes and components that can be installed by admins.","topic_url":"/t/about-the-theme-developers-category/239285","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":56,"name":"developers","color":"00A94F","text_color":"FFFFFF","slug":"devs","topic_count":96,"post_count":1185,"position":8,"description":"Documentation for developing features, plugins, or integrations with Discourse.","description_text":"Documentation for developing features, plugins, or integrations with Discourse.","description_excerpt":"Documentation for developing features, plugins, or integrations with Discourse.","topic_url":"/t/about-the-developers-category/56210","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Article","activity_pub_enabled":true,"activity_pub_username":"developer-docs","activity_pub_name":"Developer Documentation","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":35545,"handle":"developer-docs@meta.discourse.org","name":"Developer Documentation"},"activity_pub_username":"developer-docs","activity_pub_name":"Developer Documentation","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Article","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":6,"name":"support","color":"CEA9A9","text_color":"FFFFFF","slug":"support","topic_count":15841,"post_count":103687,"position":9,"description":"The category for general support questions on using your Discourse site.","description_text":"The category for general support questions on using your Discourse site.","description_excerpt":"The category for general support questions on using your Discourse site.","topic_url":"/t/about-the-support-category/389","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"\u003e Before asking, did you search first? Press 🔍 at the upper right to search.","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_accepted_answers":"true","enable_unassigned_filter":"false","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":21,"name":"wordpress","color":"F9CFCF","text_color":"FFFFFF","slug":"wordpress","topic_count":732,"post_count":5110,"position":10,"description":"Support for the official Discourse WordPress plugin at \u003ca href=\"https://github.com/discourse/wp-discourse\" class=\"inline-onebox\"\u003eGitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog\u003c/a\u003e","description_text":"Support for the official Discourse WordPress plugin at GitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog","description_excerpt":"Support for the official Discourse WordPress plugin at \u003ca href=\"https://github.com/discourse/wp-discourse\" class=\"inline-onebox\"\u003eGitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog\u003c/a\u003e","topic_url":"/t/about-the-wordpress-category/12282","read_restricted":false,"permission":null,"parent_category_id":6,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":1,"name":"bug","color":"e9dd00","text_color":"000000","slug":"bug","topic_count":5153,"post_count":35973,"position":11,"description":"A bug report means \u003cstrong\u003esomething is broken, preventing normal/typical use of Discourse\u003c/strong\u003e. Do be sure to search prior to submitting bugs. \u003ca href=\"https://meta.discourse.org/t/how-to-write-a-good-bug-report/183671/\"\u003eInclude repro steps\u003c/a\u003e, and only describe one bug per topic please.","description_text":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","description_excerpt":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. \u003ca href=\"https://meta.discourse.org/t/how-to-write-a-good-bug-report/183671/\"\u003eInclude repro steps\u003c/a\u003e, and only describe one bug per topic please.","topic_url":"/t/about-the-bug-category/2","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_accepted_answers":null,"enable_unassigned_filter":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":9,"name":"ux","color":"5F497A","text_color":"FFFFFF","slug":"ux","topic_count":2700,"post_count":18225,"position":12,"description":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","description_text":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","description_excerpt":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","topic_url":"/t/about-the-ux-category/2628","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":2,"name":"feature","color":"0E76BD","text_color":"FFFFFF","slug":"feature","topic_count":6997,"post_count":58067,"position":13,"description":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","description_text":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","description_excerpt":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","topic_url":"/t/about-the-feature-category/11","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_username":"feature","activity_pub_name":"Feature Requests","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post","activity_pub_enabled":true},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":1,"handle":"feature@meta.discourse.org","name":"Feature Requests"},"activity_pub_username":"feature","activity_pub_name":"Feature Requests","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Note","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":148,"name":"data \u0026 reporting","color":"D24899","text_color":"FFFFFF","slug":"data-reporting","topic_count":631,"post_count":3056,"position":14,"description":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","description_text":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","description_excerpt":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","topic_url":"/t/about-the-data-reporting-category/274664","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","enable_accepted_answers":"true","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":24,"name":"sso","color":"d47711","text_color":"FFFFFF","slug":"sso","topic_count":493,"post_count":2546,"position":15,"description":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the \u003ca href=\"https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045\"\u003eofficial documentation on DiscourseConnect SSO\u003c/a\u003e.","description_text":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the official documentation on DiscourseConnect SSO.","description_excerpt":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the \u003ca href=\"https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045\"\u003eofficial documentation on DiscourseConnect SSO\u003c/a\u003e.","topic_url":"/t/about-the-sso-category/13110","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":65,"name":"community","color":"12A89D","text_color":"FFFFFF","slug":"community","topic_count":866,"post_count":8844,"position":16,"description":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","description_text":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","description_excerpt":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","topic_url":"/t/about-the-community-category/67750","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":7,"name":"dev","color":"292929","text_color":"fff","slug":"dev","topic_count":3467,"post_count":20210,"position":17,"description":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","description_text":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","description_excerpt":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","topic_url":"/t/about-the-dev-category/1026","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":27,"name":"translations","color":"808281","text_color":"FFFFFF","slug":"translations","topic_count":297,"post_count":1832,"position":18,"description":"This category is for discussion about localizing Discourse.","description_text":"This category is for discussion about localizing Discourse.","description_excerpt":"This category is for discussion about localizing Discourse.","topic_url":"/t/about-the-translations-category/14549","read_restricted":false,"permission":null,"parent_category_id":7,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":14,"name":"marketplace","color":"8C6238","text_color":"FFFFFF","slug":"marketplace","topic_count":1162,"post_count":5865,"position":19,"description":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","description_text":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","description_excerpt":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","topic_url":"/t/about-the-marketplace-category/5425","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"What would you like done?\n\nWhen do you need it done?\n\nWhat is your budget, in $ USD that you can offer for this task?\n\n\u003c!-- We encourage caution and due diligence when engaging with potential contractors or clients. Verify their credentials, check previous work, and ensure a transparent and legitimate transaction. Always remember, your safety and security in the marketplace is your responsibility. --\u003e","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["delivered"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":22,"name":"plugin","color":"F7941D","text_color":"FFFFFF","slug":"plugin","topic_count":315,"post_count":10429,"position":20,"description":"A directory of Discourse plugins, both official and third-party.","description_text":"A directory of Discourse plugins, both official and third-party.","description_excerpt":"A directory of Discourse plugins, both official and third-party.","topic_url":"/t/about-the-plugin-category/12648","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"| | | |\n| - | - | - |\n| :information_source: | **Summary** | In a few words, what does this plugin do? |\n| :hammer_and_wrench: | **Repository Link** | \u003c\u003e |\n| :open_book: | **Install Guide** | [How to install plugins in Discourse](https://meta.discourse.org/t/install-plugins-in-discourse/19157) |\n\n\u003cbr\u003e \n\n### Features\n \nDescribe the major features of the plugin\n \n### Configuration\n \nInclude detailed steps on how to configure the plugin (include screenshots where necessary)\n \n### CHANGELOG\n- Add new bullets when major features are committed here\n \n### TODO\n- Add any #pr-welcome TODO tasks here","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":5,"name":"extras","color":"25AAE2","text_color":"FFFFFF","slug":"extras","topic_count":90,"post_count":985,"position":21,"description":"A directory of all extensions \u0026amp; integrations for Discourse which are \u003cem\u003enot\u003c/em\u003e Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","description_text":"A directory of all extensions \u0026amp; integrations for Discourse which are not Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","description_excerpt":"A directory of all extensions \u0026amp; integrations for Discourse which are not Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","topic_url":"/t/about-the-extras-category/28","read_restricted":false,"permission":null,"parent_category_id":22,"notification_level":1,"topic_template":"","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":61,"name":"theme","color":"E43D30","text_color":"FFFFFF","slug":"theme","topic_count":66,"post_count":2138,"position":22,"description":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","description_text":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","description_excerpt":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","topic_url":"/t/about-the-theme-category/60925","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"||||\n|-|-|-|\n| :information_source: | **Summary** | ADD SHORT SUMMARY \n| :eyeglasses:|**Preview**| PREVIEW_LINK |\n| :hammer_and_wrench:|**Repository**| REPOSITORY_LINK |\n| :question:|**Install Guide**|[How to install a theme or theme component](https://meta.discourse.org/t/how-do-i-install-a-theme-or-theme-component/63682)|\n| :open_book:|**New to Discourse Themes?**| [Beginner’s guide to using Discourse Themes](https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966)\n\n\u003c!-- Describe this theme in one or two sentences --\u003e\n\nShort description...\n\n\u003c!-- Add screenshots (if applicable) --\u003e\n\nScreenshots...\n\n\u003c!-- Add more details and explain the settings (if applicable) --\u003e\n\nDetailed description...\n","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"none","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["color-palette"],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":120,"name":"theme-component","color":"1dedf8","text_color":"FFFFFF","slug":"theme-component","topic_count":300,"post_count":7516,"position":23,"description":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","description_text":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","description_excerpt":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","topic_url":"/t/about-the-theme-component-category/232731","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"| | | |\n| - | - | - |\n| :information_source: | **Summary** | ADD SHORT SUMMARY |\n| :eyeglasses: |**Preview**| PREVIEW_LINK |\n| :hammer_and_wrench: | **Repository**| REPOSITORY_LINK |\n| :question: | **Install Guide** | [How to install a theme or theme component](https://meta.discourse.org/t/how-do-i-install-a-theme-or-theme-component/63682) |\n| :open_book: | **New to Discourse Themes?** | [Beginner’s guide to using Discourse Themes](https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966) |\n\n\u003c!-- Fill in \"repoName\" and \"repoURL\" for the automatic install button --\u003e\n\n[wrap=theme-install-button repoName=\"Component's name\" repoUrl=\"GitHub repository link\"]\nInstall this theme component\n[/wrap]\n\n\u003c!-- Describe this theme/component in one or two sentences --\u003e\n\nShort description...\n\n\u003c!-- Add screenshots (if applicable) --\u003e\n\nScreenshots...\n\n\u003c!-- Add more details and explain the settings (if applicable) --\u003e\n\nDetailed description...","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"none","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":31,"name":"installation","color":"997E7E","text_color":"FFFFFF","slug":"installation","topic_count":3426,"post_count":29611,"position":24,"description":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","description_text":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","description_excerpt":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","topic_url":"/t/about-the-installation-category/21019","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":106,"name":"migration","color":"652D90","text_color":"FFFFFF","slug":"migration","topic_count":215,"post_count":1558,"position":25,"description":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","description_text":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","description_excerpt":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","topic_url":"/t/about-the-migration-category/196969","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Migration"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":8,"name":"hosting","color":"00AEEF","text_color":"FFFFFF","slug":"hosting","topic_count":501,"post_count":4117,"position":26,"description":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","description_text":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","description_excerpt":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","topic_url":"/t/about-the-hosting-category/2626","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post","enable_accepted_answers":"true"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":30,"name":"releases","color":"BF1E2E","text_color":"FFFFFF","slug":"releases","topic_count":23,"post_count":104,"position":27,"description":"Outlining each official release of Discourse, and plans for future releases.","description_text":"Outlining each official release of Discourse, and plans for future releases.","description_excerpt":"Outlining each official release of Discourse, and plans for future releases.","topic_url":"/t/about-the-releases-category/20857","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"created","sort_ascending":false,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":3,"name":"site feedback","color":"888","text_color":"FFFFFF","slug":"site-feedback","topic_count":417,"post_count":3220,"position":28,"description":"Discussion about \u003ca href=\"http://meta.discourse.org\"\u003emeta.discourse.org\u003c/a\u003e itself - the organization of this forum, how it works, and how we can improve this site.","description_text":"Discussion about meta.discourse.org itself - the organization of this forum, how it works, and how we can improve this site.","description_excerpt":"Discussion about \u003ca href=\"http://meta.discourse.org\"\u003emeta.discourse.org\u003c/a\u003e itself - the organization of this forum, how it works, and how we can improve this site.","topic_url":"/t/about-the-site-feedback-category/24","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":152,"name":"theme feedback","color":"ED207B","text_color":"FFFFFF","slug":"theme-feedback","topic_count":11,"post_count":44,"position":29,"description":"This is the category to gather all the UX reports for \u003ca href=\"https://meta.discourse.org/t/we-have-a-new-default-theme-here-on-meta/284692\"\u003eour new theme on meta\u003c/a\u003e. It also uses the new Form Templates feature that I’ve been wanting to try out.","description_text":"This is the category to gather all the UX reports for our new theme on meta. It also uses the new Form Templates feature that I’ve been wanting to try out.","description_excerpt":"This is the category to gather all the UX reports for \u003ca href=\"https://meta.discourse.org/t/we-have-a-new-default-theme-here-on-meta/284692\"\u003eour new theme on meta\u003c/a\u003e. It also uses the new Form Templates feature that I’ve been wanting to try out.","topic_url":"/t/about-the-theme-feedback-category/284904","read_restricted":false,"permission":null,"parent_category_id":3,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[1],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":157,"name":"forum summaries","color":"72e9a7","text_color":"FFFFFF","slug":"forum-summaries","topic_count":4,"post_count":36,"position":30,"description":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summaries deliver the essence of the Discourse community’s heartbeat, right at your fingertips.","description_text":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summaries deliver the essence of the Discourse community’s heartbeat, right at your fingertips.","description_excerpt":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summarie\u0026hellip;","topic_url":"/t/about-the-forum-summaries-category/291765","read_restricted":false,"permission":null,"parent_category_id":3,"notification_level":0,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":35,"name":"praise","color":"9EB83B","text_color":"FFFFFF","slug":"praise","topic_count":294,"post_count":1091,"position":31,"description":"Have something nice to say about Discourse?","description_text":"Have something nice to say about Discourse?","description_excerpt":"Have something nice to say about Discourse?","topic_url":"/t/about-the-praise-category/30010","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":true,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":63,"name":"comparison","color":"F1592A","text_color":"FFFFFF","slug":"comparison","topic_count":11,"post_count":137,"position":32,"description":"Topics comparing Discourse to other platforms.","description_text":"Topics comparing Discourse to other platforms.","description_excerpt":"Topics comparing Discourse to other platforms.","topic_url":"/t/about-the-comparison-category/65736","read_restricted":false,"permission":null,"parent_category_id":35,"notification_level":1,"topic_template":"### About PLATFORM_NAME\n\nA blurb about the platform being compared to Discourse. \n\nhttp://link.to.website\n\n ### Previous discussions:\n\nlinks to previous discussions related\n\n### Importer status\n\nIs there an official Discourse importer? Where is it? ","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":105,"name":"community support program","color":"92278F","text_color":"FFFFFF","slug":"support-program","topic_count":4,"post_count":27,"position":33,"description":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","description_text":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","description_excerpt":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","topic_url":"/t/about-the-community-support-program-category/193906","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":124,"name":"General","color":"25AAE2","text_color":"FFFFFF","slug":"general","topic_count":155,"post_count":1408,"position":35,"description":"Create topics here that don’t fit into any other existing category.","description_text":"Create topics here that don’t fit into any other existing category.","description_excerpt":"Create topics here that don’t fit into any other existing category.","topic_url":"/t/about-the-general-category/237517","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":17,"name":"Uncategorized","color":"AB9364","text_color":"FFFFFF","slug":"uncategorized","topic_count":0,"post_count":0,"position":77,"description":"Topics that don't need a category, or don't fit into any other existing category.","description_text":"Topics that don't need a category, or don't fit into any other existing category.","description_excerpt":"Topics that don't need a category, or don't fit into any other existing category.","topic_url":"/t/","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false}]}}} \ No newline at end of file diff --git a/spec/fixtures/search_meta/site.json b/spec/fixtures/search_meta/site.json index 9651e0ce..78f5fe39 100644 --- a/spec/fixtures/search_meta/site.json +++ b/spec/fixtures/search_meta/site.json @@ -1 +1 @@ -{"default_archetype":"regular","notification_types":{"mentioned":1,"replied":2,"quoted":3,"edited":4,"liked":5,"private_message":6,"invited_to_private_message":7,"invitee_accepted":8,"posted":9,"moved_post":10,"linked":11,"granted_badge":12,"invited_to_topic":13,"custom":14,"group_mentioned":15,"group_message_summary":16,"watching_first_post":17,"topic_reminder":18,"liked_consolidated":19,"post_approved":20,"code_review_commit_approved":21,"membership_request_accepted":22,"membership_request_consolidated":23,"bookmark_reminder":24,"reaction":25,"votes_released":26,"event_reminder":27,"event_invitation":28,"chat_mention":29,"chat_message":30,"chat_invitation":31,"chat_group_mention":32,"chat_quoted":33,"assigned":34,"question_answer_user_commented":35,"watching_category_or_tag":36,"new_features":37,"admin_problems":38,"following":800,"following_created_topic":801,"following_replied":802,"circles_activity":900},"post_types":{"regular":1,"moderator_action":2,"small_action":3,"whisper":4},"user_tips":{"first_notification":1,"topic_timeline":2,"post_menu":3,"topic_notification_levels":4,"suggested_topics":5,"admin_guide":6},"trust_levels":{"newuser":0,"basic":1,"member":2,"regular":3,"leader":4},"groups":[{"id":183,"name":"ai-personas","flair_url":null,"flair_bg_color":"","flair_color":""},{"id":160,"name":"community-moderators","flair_url":null,"flair_bg_color":"","flair_color":""},{"id":184,"name":"glimmer-search-menu","flair_url":null,"flair_bg_color":"","flair_color":""},{"id":157,"name":"new-new-testers","flair_url":null,"flair_bg_color":"","flair_color":""},{"id":48,"name":"plugin_authors","flair_url":"fa-plug","flair_bg_color":"dddddd","flair_color":"111111"},{"id":148,"name":"support-advocates","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/3X/e/4/e4038d4d9848de2eabab38e17b8bdb69da154024.svg","flair_bg_color":"FFFFFF","flair_color":""},{"id":151,"name":"support-enthusiasts","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/3X/1/3/13f5d8d7e56be8a6a1ea3de009b985a548aec8d4.svg","flair_bg_color":"FFFFFF","flair_color":""},{"id":142,"name":"support-experts","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/3X/e/2/e250ec403580530d19e6a9ed42d0d525a51a9dbe.svg","flair_bg_color":"FFFFFF","flair_color":""},{"id":118,"name":"support-explorers","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/4X/d/1/b/d1ba0acf09b9d01f87f9e05bbee1dc5b0e316d5f.png","flair_bg_color":"dddddd","flair_color":"111111"},{"id":47,"name":"team","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/3X/e/b/ebee30bd98aef20357e4a177a5a1e45b877ce088.svg","flair_bg_color":"","flair_color":"111"},{"id":73,"name":"theme_authors","flair_url":"fa-paint-brush","flair_bg_color":"ddd","flair_color":"111"},{"id":84,"name":"theme_creator","flair_url":"fa-palette","flair_bg_color":"ddd","flair_color":"111"},{"id":50,"name":"translators","flair_url":"fa-globe","flair_bg_color":"ddd","flair_color":"111"}],"filters":["latest","unread","new","unseen","top","read","posted","bookmarks","hot"],"periods":["all","yearly","quarterly","monthly","weekly","daily"],"top_menu_items":["latest","unread","new","unseen","top","read","posted","bookmarks","hot","categories"],"anonymous_top_menu_items":["latest","top","categories","hot","categories","top"],"uncategorized_category_id":17,"user_field_max_length":2048,"post_action_types":[{"id":2,"name_key":"like","name":"Like","description":"Like this post","short_description":"Like this post","is_flag":false,"is_custom_flag":false},{"id":3,"name_key":"off_topic","name":"Off-Topic","description":"This post is not relevant to the current discussion as defined by the title and first post, and should probably be moved elsewhere.","short_description":"Not relevant to the discussion","is_flag":true,"is_custom_flag":false},{"id":4,"name_key":"inappropriate","name":"Inappropriate","description":"This post contains content that a reasonable person would consider offensive, abusive, to be hateful conduct or a violation of \u003ca href=\"/guidelines\"\u003eour community guidelines\u003c/a\u003e.","short_description":"A violation of \u003ca href=\"/guidelines\"\u003eour community guidelines\u003c/a\u003e","is_flag":true,"is_custom_flag":false},{"id":8,"name_key":"spam","name":"Spam","description":"This post is an advertisement, or vandalism. It is not useful or relevant to the current topic.","short_description":"This is an advertisement or vandalism","is_flag":true,"is_custom_flag":false},{"id":6,"name_key":"notify_user","name":"Send @%{username} a message","description":"I want to talk to this person directly and personally about their post.","short_description":"I want to talk to this person directly and personally about their post.","is_flag":true,"is_custom_flag":true},{"id":10,"name_key":"illegal","name":"Illegal","description":"This post requires staff attention because I believe it contains content that is illegal.","short_description":"This is illegal","is_flag":true,"is_custom_flag":true},{"id":null,"name_key":null,"name":"Translation missing: en.post_action_types..title","description":"Translation missing: en.post_action_types.description","short_description":"Translation missing: en.post_action_types.short_description","is_flag":false,"is_custom_flag":false},{"id":7,"name_key":"notify_moderators","name":"Something Else","description":"This post requires staff attention for another reason not listed above.","short_description":"Requires staff attention for another reason","is_flag":true,"is_custom_flag":true}],"topic_flag_types":[{"id":4,"name_key":"inappropriate","name":"Inappropriate","description":"This topic contains content that a reasonable person would consider offensive, abusive, to be hateful conduct or a violation of \u003ca href=\"/guidelines\"\u003eour community guidelines\u003c/a\u003e.","short_description":"A violation of \u003ca href=\"/guidelines\"\u003eour community guidelines\u003c/a\u003e","is_flag":true,"is_custom_flag":false},{"id":8,"name_key":"spam","name":"Spam","description":"This topic is an advertisement. It is not useful or relevant to this site, but promotional in nature.","short_description":"This is an advertisement","is_flag":true,"is_custom_flag":false},{"id":10,"name_key":"illegal","name":"Illegal","description":"This topic requires staff attention because I believe it contains content that is illegal.","short_description":"This is illegal","is_flag":true,"is_custom_flag":true},{"id":null,"name_key":null,"name":"Translation missing: en.topic_flag_types..title","description":"Translation missing: en.topic_flag_types.description","short_description":"Translation missing: en.topic_flag_types.short_description","is_flag":false,"is_custom_flag":false},{"id":7,"name_key":"notify_moderators","name":"Something Else","description":"This topic requires general staff attention based on the \u003ca href=\"/guidelines\"\u003eguidelines\u003c/a\u003e, \u003ca href=\"/tos\"\u003eTOS\u003c/a\u003e, or for another reason not listed above.","short_description":"Requires staff attention for another reason","is_flag":true,"is_custom_flag":true}],"can_create_tag":false,"can_tag_topics":false,"can_tag_pms":false,"tags_filter_regexp":"[/\\?#\\[\\]@!\\$\u0026'\\(\\)\\*\\+,;=\\.%\\\\`^\\s|\\{\\}\"\u003c\u003e]+","top_tags":["how-to","chat","rest-api","sql-query","unsupported-install","email","pr-welcome","completed","ai","activity-summary","official","solved","sidebar","release-notes","server-resources","calendar-and-event","badges","topic-voting","docker","tags","advertising","invites","search","sql-triggered-badge","subscriptions","new-feature","chat-integration","wordpress","groups","themes","css"],"navigation_menu_site_top_tags":[{"name":"how-to","description":"How to guides contain steps to follow to solve a specific problem","pm_only":false},{"name":"chat","description":null,"pm_only":false},{"name":"rest-api","description":"Topics about making an external request to Discourse","pm_only":false},{"name":"sql-query","description":"SQL queries for the data-explorer","pm_only":false},{"name":"unsupported-install","description":null,"pm_only":false}],"topic_featured_link_allowed_category_ids":[149,120,122,157,121,153,127,9,136,138,61,83,96,56,132,123,144,104,10,27,137,25,124,22,134,24,110,105,143,119,145,135,129,7,30,89,131,150,6,155,31,20,133,55,102,148,147,95,152,151,128,154,21,139,108,69,3,14,125,63,2,126,115,106,1,118,117,17,53,13,5,65,8,67,35],"user_themes":[{"theme_id":272,"name":"Accessible contrast (dark)","default":false,"color_scheme_id":85},{"theme_id":271,"name":"Accessible contrast (light)","default":false,"color_scheme_id":84},{"theme_id":242,"name":"Air Theme","default":false,"color_scheme_id":82},{"theme_id":337,"name":"Central","default":false,"color_scheme_id":100},{"theme_id":335,"name":"CentralStaffOnly","default":false,"color_scheme_id":96},{"theme_id":31,"name":"Dark","default":false,"color_scheme_id":86},{"theme_id":140,"name":"Default","default":false,"color_scheme_id":34},{"theme_id":281,"name":"Default (full-width)","default":false,"color_scheme_id":34},{"theme_id":51,"name":"Discourse-classic","default":false,"color_scheme_id":34},{"theme_id":296,"name":"Fully","default":false,"color_scheme_id":null},{"theme_id":131,"name":"Ghost","default":false,"color_scheme_id":66},{"theme_id":80,"name":"Graceful","default":false,"color_scheme_id":54},{"theme_id":83,"name":"Grey Amber","default":false,"color_scheme_id":57},{"theme_id":232,"name":"Hidden Whispers","default":false,"color_scheme_id":null},{"theme_id":34,"name":"Material Design","default":false,"color_scheme_id":37},{"theme_id":331,"name":"Meta Branded","default":true,"color_scheme_id":34},{"theme_id":125,"name":"Minima Dark","default":false,"color_scheme_id":43},{"theme_id":299,"name":"redditish","default":false,"color_scheme_id":null},{"theme_id":30,"name":"Sam's Simple Theme","default":false,"color_scheme_id":null}],"user_color_schemes":[{"id":93,"name":"Central Dark","is_dark":true},{"id":95,"name":"Central Dark","is_dark":true},{"id":100,"name":"Central Light","is_dark":false},{"id":96,"name":"Central Light","is_dark":false},{"id":86,"name":"Dark","is_dark":true},{"id":34,"name":"Default Light","is_dark":false},{"id":87,"name":"Solarized Light","is_dark":false},{"id":85,"name":"WCAG Dark","is_dark":true},{"id":84,"name":"WCAG Light","is_dark":false}],"default_dark_color_scheme":null,"censored_regexp":[{"(?:\\P{L}|^)(EICARTESTCENSOR)(?=\\P{L}|$)":{"case_sensitive":false}}],"custom_emoji_translation":{},"watched_words_replace":{"(?:\\P{L}|^)(¯_\\(ツ\\)_/¯)(?=\\P{L}|$)":{"word":"¯_(ツ)_/¯","replacement":"¯\\_(ツ)_/¯","case_sensitive":false}},"watched_words_link":{"(?:\\P{L}|^)(Data Explorer)(?=\\P{L}|$)":{"word":"Data Explorer","replacement":"https://meta.discourse.org/t/32566?silent=true","case_sensitive":false},"(?:\\P{L}|^)(discourseconnect)(?=\\P{L}|$)":{"word":"discourseconnect","replacement":"https://meta.discourse.org/t/13045?silent=true","case_sensitive":false},"(?:\\P{L}|^)(discourse connect)(?=\\P{L}|$)":{"word":"discourse connect","replacement":"https://meta.discourse.org/t/13045?silent=true","case_sensitive":false},"(?:\\P{L}|^)(discourse sso)(?=\\P{L}|$)":{"word":"discourse sso","replacement":"https://meta.discourse.org/t/13045?silent=true","case_sensitive":false},"(?:\\P{L}|^)(official install)(?=\\P{L}|$)":{"word":"official install","replacement":"https://meta.discourse.org/t/142537?silent=true","case_sensitive":false},"(?:\\P{L}|^)(standard install)(?=\\P{L}|$)":{"word":"standard install","replacement":"https://meta.discourse.org/t/142537?silent=true","case_sensitive":false},"(?:\\P{L}|^)(safe mode)(?=\\P{L}|$)":{"word":"safe mode","replacement":"https://meta.discourse.org/t/53504?silent=true","case_sensitive":false}},"categories":[{"id":67,"name":"announcements","color":"ED207B","text_color":"FFFFFF","slug":"announcements","topic_count":312,"post_count":3973,"position":0,"description":"The place for all Discourse announcements.","description_text":"The place for all Discourse announcements.","description_excerpt":"The place for all Discourse announcements.","topic_url":"/t/about-the-announcements-category/68629","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_enabled":true,"activity_pub_username":"announcements","activity_pub_name":"Announcements","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["new-feature","security"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":2,"handle":"announcements@meta.discourse.org","name":"Announcements"},"activity_pub_username":"announcements","activity_pub_name":"Announcements","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Note","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":13,"name":"blog","color":"8080ff","text_color":"FFFFFF","slug":"blog","topic_count":170,"post_count":1477,"position":1,"description":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","description_text":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","description_excerpt":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","topic_url":"/t/about-the-blog-category/5250","read_restricted":false,"permission":null,"parent_category_id":67,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"created","sort_ascending":false,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":10,"name":"documentation","color":"00A94F","text_color":"FFFFFF","slug":"documentation","topic_count":2,"post_count":2,"position":2,"description":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","description_text":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","description_excerpt":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","topic_url":"/t/about-the-documentation-category/2629","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"top","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Article","activity_pub_publication_type":"full_topic","activity_pub_username":"documentation","activity_pub_enabled":true,"activity_pub_name":"Documentation"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":31769,"handle":"documentation@meta.discourse.org","name":"Documentation"},"activity_pub_username":"documentation","activity_pub_name":"Documentation","activity_pub_default_visibility":"public","activity_pub_publication_type":"full_topic","activity_pub_post_object_type":"Article","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":53,"name":"admins","color":"F15D22","text_color":"FFFFFF","slug":"admins","topic_count":222,"post_count":1703,"position":3,"description":"Guides for Discourse admins and community managers with admin access.","description_text":"Guides for Discourse admins and community managers with admin access.","description_excerpt":"Guides for Discourse admins and community managers with admin access.","topic_url":"/t/about-the-admins-category/56207","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["hosted-support","migrations"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":125,"name":"moderators","color":"40d0e2","text_color":"FFFFFF","slug":"moderators","topic_count":16,"post_count":71,"position":4,"description":"Documentation for moderators and community managers using Discourse.","description_text":"Documentation for moderators and community managers using Discourse.","description_excerpt":"Documentation for moderators and community managers using Discourse.","topic_url":"/t/about-the-moderators-category/238588","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":126,"name":"users","color":"0088CC","text_color":"FFFFFF","slug":"users","topic_count":53,"post_count":184,"position":5,"description":"Documentation for all members of communities running Discourse.","description_text":"Documentation for all members of communities running Discourse.","description_excerpt":"Documentation for all members of communities running Discourse.","topic_url":"/t/about-the-users-category/238917","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":55,"name":"sysadmin","color":"E9DD00","text_color":"FFFFFF","slug":"sysadmin","topic_count":175,"post_count":2803,"position":6,"description":"Documentation for self-hosters and Discourse system administrators.","description_text":"Documentation for self-hosters and Discourse system administrators.","description_excerpt":"Documentation for self-hosters and Discourse system administrators.","topic_url":"/t/about-the-sysadmin-category/56209","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":["migrations"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":127,"name":"theme developers","color":"92278F","text_color":"FFFFFF","slug":"theme-developers","topic_count":41,"post_count":215,"position":7,"description":"Documentation for developing themes and components that can be installed by admins.","description_text":"Documentation for developing themes and components that can be installed by admins.","description_excerpt":"Documentation for developing themes and components that can be installed by admins.","topic_url":"/t/about-the-theme-developers-category/239285","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":56,"name":"developers","color":"00A94F","text_color":"FFFFFF","slug":"devs","topic_count":96,"post_count":1185,"position":8,"description":"Documentation for developing features, plugins, or integrations with Discourse.","description_text":"Documentation for developing features, plugins, or integrations with Discourse.","description_excerpt":"Documentation for developing features, plugins, or integrations with Discourse.","topic_url":"/t/about-the-developers-category/56210","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Article","activity_pub_enabled":true,"activity_pub_username":"developer-docs","activity_pub_name":"Developer Documentation","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":35545,"handle":"developer-docs@meta.discourse.org","name":"Developer Documentation"},"activity_pub_username":"developer-docs","activity_pub_name":"Developer Documentation","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Article","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":6,"name":"support","color":"CEA9A9","text_color":"FFFFFF","slug":"support","topic_count":15841,"post_count":103687,"position":9,"description":"The category for general support questions on using your Discourse site.","description_text":"The category for general support questions on using your Discourse site.","description_excerpt":"The category for general support questions on using your Discourse site.","topic_url":"/t/about-the-support-category/389","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"\u003e Before asking, did you search first? Press 🔍 at the upper right to search.","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_accepted_answers":"true","enable_unassigned_filter":"false","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":21,"name":"wordpress","color":"F9CFCF","text_color":"FFFFFF","slug":"wordpress","topic_count":732,"post_count":5110,"position":10,"description":"Support for the official Discourse WordPress plugin at \u003ca href=\"https://github.com/discourse/wp-discourse\" class=\"inline-onebox\"\u003eGitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog\u003c/a\u003e","description_text":"Support for the official Discourse WordPress plugin at GitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog","description_excerpt":"Support for the official Discourse WordPress plugin at \u003ca href=\"https://github.com/discourse/wp-discourse\" class=\"inline-onebox\"\u003eGitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog\u003c/a\u003e","topic_url":"/t/about-the-wordpress-category/12282","read_restricted":false,"permission":null,"parent_category_id":6,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":1,"name":"bug","color":"e9dd00","text_color":"000000","slug":"bug","topic_count":5153,"post_count":35973,"position":11,"description":"A bug report means \u003cstrong\u003esomething is broken, preventing normal/typical use of Discourse\u003c/strong\u003e. Do be sure to search prior to submitting bugs. \u003ca href=\"https://meta.discourse.org/t/how-to-write-a-good-bug-report/183671/\"\u003eInclude repro steps\u003c/a\u003e, and only describe one bug per topic please.","description_text":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","description_excerpt":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. \u003ca href=\"https://meta.discourse.org/t/how-to-write-a-good-bug-report/183671/\"\u003eInclude repro steps\u003c/a\u003e, and only describe one bug per topic please.","topic_url":"/t/about-the-bug-category/2","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_accepted_answers":null,"enable_unassigned_filter":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":9,"name":"ux","color":"5F497A","text_color":"FFFFFF","slug":"ux","topic_count":2700,"post_count":18225,"position":12,"description":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","description_text":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","description_excerpt":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","topic_url":"/t/about-the-ux-category/2628","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":2,"name":"feature","color":"0E76BD","text_color":"FFFFFF","slug":"feature","topic_count":6997,"post_count":58067,"position":13,"description":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","description_text":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","description_excerpt":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","topic_url":"/t/about-the-feature-category/11","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_username":"feature","activity_pub_name":"Feature Requests","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post","activity_pub_enabled":true},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":1,"handle":"feature@meta.discourse.org","name":"Feature Requests"},"activity_pub_username":"feature","activity_pub_name":"Feature Requests","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Note","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":148,"name":"data \u0026 reporting","color":"D24899","text_color":"FFFFFF","slug":"data-reporting","topic_count":631,"post_count":3056,"position":14,"description":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","description_text":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","description_excerpt":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","topic_url":"/t/about-the-data-reporting-category/274664","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","enable_accepted_answers":"true","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":24,"name":"sso","color":"d47711","text_color":"FFFFFF","slug":"sso","topic_count":493,"post_count":2546,"position":15,"description":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the \u003ca href=\"https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045\"\u003eofficial documentation on DiscourseConnect SSO\u003c/a\u003e.","description_text":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the official documentation on DiscourseConnect SSO.","description_excerpt":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the \u003ca href=\"https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045\"\u003eofficial documentation on DiscourseConnect SSO\u003c/a\u003e.","topic_url":"/t/about-the-sso-category/13110","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":65,"name":"community","color":"12A89D","text_color":"FFFFFF","slug":"community","topic_count":866,"post_count":8844,"position":16,"description":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","description_text":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","description_excerpt":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","topic_url":"/t/about-the-community-category/67750","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":7,"name":"dev","color":"292929","text_color":"fff","slug":"dev","topic_count":3467,"post_count":20210,"position":17,"description":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","description_text":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","description_excerpt":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","topic_url":"/t/about-the-dev-category/1026","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":27,"name":"translations","color":"808281","text_color":"FFFFFF","slug":"translations","topic_count":297,"post_count":1832,"position":18,"description":"This category is for discussion about localizing Discourse.","description_text":"This category is for discussion about localizing Discourse.","description_excerpt":"This category is for discussion about localizing Discourse.","topic_url":"/t/about-the-translations-category/14549","read_restricted":false,"permission":null,"parent_category_id":7,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":14,"name":"marketplace","color":"8C6238","text_color":"FFFFFF","slug":"marketplace","topic_count":1162,"post_count":5865,"position":19,"description":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","description_text":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","description_excerpt":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","topic_url":"/t/about-the-marketplace-category/5425","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"What would you like done?\n\nWhen do you need it done?\n\nWhat is your budget, in $ USD that you can offer for this task?\n\n\u003c!-- We encourage caution and due diligence when engaging with potential contractors or clients. Verify their credentials, check previous work, and ensure a transparent and legitimate transaction. Always remember, your safety and security in the marketplace is your responsibility. --\u003e","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["delivered"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":22,"name":"plugin","color":"F7941D","text_color":"FFFFFF","slug":"plugin","topic_count":315,"post_count":10429,"position":20,"description":"A directory of Discourse plugins, both official and third-party.","description_text":"A directory of Discourse plugins, both official and third-party.","description_excerpt":"A directory of Discourse plugins, both official and third-party.","topic_url":"/t/about-the-plugin-category/12648","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"| | | |\n| - | - | - |\n| :information_source: | **Summary** | In a few words, what does this plugin do? |\n| :hammer_and_wrench: | **Repository Link** | \u003c\u003e |\n| :open_book: | **Install Guide** | [How to install plugins in Discourse](https://meta.discourse.org/t/install-plugins-in-discourse/19157) |\n\n\u003cbr\u003e \n\n### Features\n \nDescribe the major features of the plugin\n \n### Configuration\n \nInclude detailed steps on how to configure the plugin (include screenshots where necessary)\n \n### CHANGELOG\n- Add new bullets when major features are committed here\n \n### TODO\n- Add any #pr-welcome TODO tasks here","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":5,"name":"extras","color":"25AAE2","text_color":"FFFFFF","slug":"extras","topic_count":90,"post_count":985,"position":21,"description":"A directory of all extensions \u0026amp; integrations for Discourse which are \u003cem\u003enot\u003c/em\u003e Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","description_text":"A directory of all extensions \u0026amp; integrations for Discourse which are not Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","description_excerpt":"A directory of all extensions \u0026amp; integrations for Discourse which are not Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","topic_url":"/t/about-the-extras-category/28","read_restricted":false,"permission":null,"parent_category_id":22,"notification_level":1,"topic_template":"","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":61,"name":"theme","color":"E43D30","text_color":"FFFFFF","slug":"theme","topic_count":66,"post_count":2138,"position":22,"description":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","description_text":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","description_excerpt":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","topic_url":"/t/about-the-theme-category/60925","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"||||\n|-|-|-|\n| :information_source: | **Summary** | ADD SHORT SUMMARY \n| :eyeglasses:|**Preview**| PREVIEW_LINK |\n| :hammer_and_wrench:|**Repository**| REPOSITORY_LINK |\n| :question:|**Install Guide**|[How to install a theme or theme component](https://meta.discourse.org/t/how-do-i-install-a-theme-or-theme-component/63682)|\n| :open_book:|**New to Discourse Themes?**| [Beginner’s guide to using Discourse Themes](https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966)\n\n\u003c!-- Describe this theme in one or two sentences --\u003e\n\nShort description...\n\n\u003c!-- Add screenshots (if applicable) --\u003e\n\nScreenshots...\n\n\u003c!-- Add more details and explain the settings (if applicable) --\u003e\n\nDetailed description...\n","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"none","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["color-palette"],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":120,"name":"theme-component","color":"1dedf8","text_color":"FFFFFF","slug":"theme-component","topic_count":300,"post_count":7516,"position":23,"description":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","description_text":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","description_excerpt":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","topic_url":"/t/about-the-theme-component-category/232731","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"| | | |\n| - | - | - |\n| :information_source: | **Summary** | ADD SHORT SUMMARY |\n| :eyeglasses: |**Preview**| PREVIEW_LINK |\n| :hammer_and_wrench: | **Repository**| REPOSITORY_LINK |\n| :question: | **Install Guide** | [How to install a theme or theme component](https://meta.discourse.org/t/how-do-i-install-a-theme-or-theme-component/63682) |\n| :open_book: | **New to Discourse Themes?** | [Beginner’s guide to using Discourse Themes](https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966) |\n\n\u003c!-- Fill in \"repoName\" and \"repoURL\" for the automatic install button --\u003e\n\n[wrap=theme-install-button repoName=\"Component's name\" repoUrl=\"GitHub repository link\"]\nInstall this theme component\n[/wrap]\n\n\u003c!-- Describe this theme/component in one or two sentences --\u003e\n\nShort description...\n\n\u003c!-- Add screenshots (if applicable) --\u003e\n\nScreenshots...\n\n\u003c!-- Add more details and explain the settings (if applicable) --\u003e\n\nDetailed description...","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"none","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":31,"name":"installation","color":"997E7E","text_color":"FFFFFF","slug":"installation","topic_count":3426,"post_count":29611,"position":24,"description":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","description_text":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","description_excerpt":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","topic_url":"/t/about-the-installation-category/21019","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":106,"name":"migration","color":"652D90","text_color":"FFFFFF","slug":"migration","topic_count":215,"post_count":1558,"position":25,"description":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","description_text":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","description_excerpt":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","topic_url":"/t/about-the-migration-category/196969","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Migration"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":8,"name":"hosting","color":"00AEEF","text_color":"FFFFFF","slug":"hosting","topic_count":501,"post_count":4117,"position":26,"description":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","description_text":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","description_excerpt":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","topic_url":"/t/about-the-hosting-category/2626","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post","enable_accepted_answers":"true"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":30,"name":"releases","color":"BF1E2E","text_color":"FFFFFF","slug":"releases","topic_count":23,"post_count":104,"position":27,"description":"Outlining each official release of Discourse, and plans for future releases.","description_text":"Outlining each official release of Discourse, and plans for future releases.","description_excerpt":"Outlining each official release of Discourse, and plans for future releases.","topic_url":"/t/about-the-releases-category/20857","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"created","sort_ascending":false,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":3,"name":"site feedback","color":"888","text_color":"FFFFFF","slug":"site-feedback","topic_count":417,"post_count":3220,"position":28,"description":"Discussion about \u003ca href=\"http://meta.discourse.org\"\u003emeta.discourse.org\u003c/a\u003e itself - the organization of this forum, how it works, and how we can improve this site.","description_text":"Discussion about meta.discourse.org itself - the organization of this forum, how it works, and how we can improve this site.","description_excerpt":"Discussion about \u003ca href=\"http://meta.discourse.org\"\u003emeta.discourse.org\u003c/a\u003e itself - the organization of this forum, how it works, and how we can improve this site.","topic_url":"/t/about-the-site-feedback-category/24","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":152,"name":"theme feedback","color":"ED207B","text_color":"FFFFFF","slug":"theme-feedback","topic_count":11,"post_count":44,"position":29,"description":"This is the category to gather all the UX reports for \u003ca href=\"https://meta.discourse.org/t/we-have-a-new-default-theme-here-on-meta/284692\"\u003eour new theme on meta\u003c/a\u003e. It also uses the new Form Templates feature that I’ve been wanting to try out.","description_text":"This is the category to gather all the UX reports for our new theme on meta. It also uses the new Form Templates feature that I’ve been wanting to try out.","description_excerpt":"This is the category to gather all the UX reports for \u003ca href=\"https://meta.discourse.org/t/we-have-a-new-default-theme-here-on-meta/284692\"\u003eour new theme on meta\u003c/a\u003e. It also uses the new Form Templates feature that I’ve been wanting to try out.","topic_url":"/t/about-the-theme-feedback-category/284904","read_restricted":false,"permission":null,"parent_category_id":3,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[1],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":157,"name":"forum summaries","color":"72e9a7","text_color":"FFFFFF","slug":"forum-summaries","topic_count":4,"post_count":36,"position":30,"description":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summaries deliver the essence of the Discourse community’s heartbeat, right at your fingertips.","description_text":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summaries deliver the essence of the Discourse community’s heartbeat, right at your fingertips.","description_excerpt":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summarie\u0026hellip;","topic_url":"/t/about-the-forum-summaries-category/291765","read_restricted":false,"permission":null,"parent_category_id":3,"notification_level":0,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":35,"name":"praise","color":"9EB83B","text_color":"FFFFFF","slug":"praise","topic_count":294,"post_count":1091,"position":31,"description":"Have something nice to say about Discourse?","description_text":"Have something nice to say about Discourse?","description_excerpt":"Have something nice to say about Discourse?","topic_url":"/t/about-the-praise-category/30010","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":true,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":63,"name":"comparison","color":"F1592A","text_color":"FFFFFF","slug":"comparison","topic_count":11,"post_count":137,"position":32,"description":"Topics comparing Discourse to other platforms.","description_text":"Topics comparing Discourse to other platforms.","description_excerpt":"Topics comparing Discourse to other platforms.","topic_url":"/t/about-the-comparison-category/65736","read_restricted":false,"permission":null,"parent_category_id":35,"notification_level":1,"topic_template":"### About PLATFORM_NAME\n\nA blurb about the platform being compared to Discourse. \n\nhttp://link.to.website\n\n ### Previous discussions:\n\nlinks to previous discussions related\n\n### Importer status\n\nIs there an official Discourse importer? Where is it? ","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":105,"name":"community support program","color":"92278F","text_color":"FFFFFF","slug":"support-program","topic_count":4,"post_count":27,"position":33,"description":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","description_text":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","description_excerpt":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","topic_url":"/t/about-the-community-support-program-category/193906","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":124,"name":"General","color":"25AAE2","text_color":"FFFFFF","slug":"general","topic_count":155,"post_count":1408,"position":35,"description":"Create topics here that don’t fit into any other existing category.","description_text":"Create topics here that don’t fit into any other existing category.","description_excerpt":"Create topics here that don’t fit into any other existing category.","topic_url":"/t/about-the-general-category/237517","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":17,"name":"Uncategorized","color":"AB9364","text_color":"FFFFFF","slug":"uncategorized","topic_count":0,"post_count":0,"position":77,"description":"Topics that don't need a category, or don't fit into any other existing category.","description_text":"Topics that don't need a category, or don't fit into any other existing category.","description_excerpt":"Topics that don't need a category, or don't fit into any other existing category.","topic_url":"/t/","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false}],"markdown_additional_options":{"chat":{"limited_pretty_text_features":["anchor","bbcode-block","bbcode-inline","code","category-hashtag","censored","chat-transcript","discourse-local-dates","emoji","emojiShortcuts","inlineEmoji","html-img","hashtag-autocomplete","mentions","unicodeUsernames","onebox","quotes","spoiler-alert","table","text-post-process","upload-protocol","watched-words"],"limited_pretty_text_markdown_rules":["autolink","list","backticks","newline","code","fence","image","table","linkify","link","strikethrough","blockquote","emphasis","replacements"],"hashtag_configurations":{"topic-composer":["category","tag","channel"],"chat-composer":["channel","category","tag"]}}},"hashtag_configurations":{"topic-composer":["category","tag","channel"],"chat-composer":["channel","category","tag"]},"hashtag_icons":{"category":"folder","tag":"tag","channel":"comment"},"displayed_about_plugin_stat_groups":["chat_messages"],"anonymous_default_navigation_menu_tags":[{"name":"official","description":"This is a theme or plugin built by the Discourse team","pm_only":false},{"name":"release-notes","description":"Release notes for Discourse beta and stable branches. See https://meta.discourse.org/t/198215 for more details on the Discourse release process.","pm_only":false}],"anonymous_sidebar_sections":[{"id":56,"title":"Community","links":[{"id":274,"name":"Topics","value":"/latest","icon":"layer-group","external":false,"full_reload":false,"segment":"primary"},{"id":275,"name":"My Posts","value":"/my/activity","icon":"user","external":false,"full_reload":true,"segment":"primary"},{"id":276,"name":"Review","value":"/review","icon":"flag","external":false,"full_reload":false,"segment":"primary"},{"id":277,"name":"Admin","value":"/admin","icon":"wrench","external":false,"full_reload":false,"segment":"primary"},{"id":279,"name":"Users","value":"/u","icon":"users","external":false,"full_reload":false,"segment":"secondary"},{"id":280,"name":"About","value":"/about","icon":"info-circle","external":false,"full_reload":false,"segment":"secondary"},{"id":281,"name":"FAQ","value":"/faq","icon":"question-circle","external":false,"full_reload":false,"segment":"secondary"},{"id":282,"name":"Groups","value":"/g","icon":"user-friends","external":false,"full_reload":false,"segment":"secondary"},{"id":283,"name":"Badges","value":"/badges","icon":"certificate","external":false,"full_reload":false,"segment":"secondary"},{"id":287,"name":"Leaderboard","value":"/leaderboard/7","icon":"trophy","external":false,"full_reload":false,"segment":"secondary"},{"id":290,"name":"Global Leaderboard","value":"/leaderboard","icon":"trophy","external":false,"full_reload":false,"segment":"secondary"},{"id":291,"name":"Topic Filter","value":"/filter","icon":"filter","external":false,"full_reload":false,"segment":"secondary"}],"slug":"community","public":true,"section_type":"community"}],"tos_url":"/tos","privacy_policy_url":"https://www.discourse.org/privacy","activity_pub_enabled":true,"activity_pub_publishing_enabled":true,"activity_pub_host":"meta.discourse.org","docs_path":"docs","default_gamification_leaderboard_id":1,"hosting_tier":"enterprise","archetypes":[{"id":"regular","name":"Regular Topic","options":[]},{"id":"banner","name":"Banner Topic","options":[]}],"user_fields":[{"id":2,"name":"Pronouns","description":"Gender Pronouns (he/him, she/her, they/them, etc.)","field_type":"text","editable":true,"required":false,"show_on_profile":true,"show_on_user_card":true,"searchable":false,"position":1}],"auth_providers":[{"name":"facebook","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":580,"frame_height":400,"can_connect":true,"can_revoke":true,"icon":"fab-facebook"},{"name":"google_oauth2","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":850,"frame_height":500,"can_connect":true,"can_revoke":true,"icon":null},{"name":"github","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":null,"frame_height":null,"can_connect":true,"can_revoke":true,"icon":"fab-github"},{"name":"twitter","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":null,"frame_height":null,"can_connect":true,"can_revoke":true,"icon":"fab-twitter"},{"name":"discord","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":null,"frame_height":null,"can_connect":true,"can_revoke":true,"icon":"fab-discord"},{"name":"apple","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":null,"frame_height":null,"can_connect":true,"can_revoke":true,"icon":"fab-apple"}]} \ No newline at end of file +{"default_archetype":"regular","notification_types":{"mentioned":1,"replied":2,"quoted":3,"edited":4,"liked":5,"private_message":6,"invited_to_private_message":7,"invitee_accepted":8,"posted":9,"moved_post":10,"linked":11,"granted_badge":12,"invited_to_topic":13,"custom":14,"group_mentioned":15,"group_message_summary":16,"watching_first_post":17,"topic_reminder":18,"liked_consolidated":19,"post_approved":20,"code_review_commit_approved":21,"membership_request_accepted":22,"membership_request_consolidated":23,"bookmark_reminder":24,"reaction":25,"votes_released":26,"event_reminder":27,"event_invitation":28,"chat_mention":29,"chat_message":30,"chat_invitation":31,"chat_group_mention":32,"chat_quoted":33,"assigned":34,"question_answer_user_commented":35,"watching_category_or_tag":36,"new_features":37,"admin_problems":38,"following":800,"following_created_topic":801,"following_replied":802,"circles_activity":900},"post_types":{"regular":1,"moderator_action":2,"small_action":3,"whisper":4},"user_tips":{"first_notification":1,"topic_timeline":2,"post_menu":3,"topic_notification_levels":4,"suggested_topics":5,"admin_guide":6},"trust_levels":{"newuser":0,"basic":1,"member":2,"regular":3,"leader":4},"groups":[{"id":183,"name":"ai-agents","flair_url":null,"flair_bg_color":"","flair_color":""},{"id":160,"name":"community-moderators","flair_url":null,"flair_bg_color":"","flair_color":""},{"id":184,"name":"glimmer-search-menu","flair_url":null,"flair_bg_color":"","flair_color":""},{"id":157,"name":"new-new-testers","flair_url":null,"flair_bg_color":"","flair_color":""},{"id":48,"name":"plugin_authors","flair_url":"fa-plug","flair_bg_color":"dddddd","flair_color":"111111"},{"id":148,"name":"support-advocates","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/3X/e/4/e4038d4d9848de2eabab38e17b8bdb69da154024.svg","flair_bg_color":"FFFFFF","flair_color":""},{"id":151,"name":"support-enthusiasts","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/3X/1/3/13f5d8d7e56be8a6a1ea3de009b985a548aec8d4.svg","flair_bg_color":"FFFFFF","flair_color":""},{"id":142,"name":"support-experts","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/3X/e/2/e250ec403580530d19e6a9ed42d0d525a51a9dbe.svg","flair_bg_color":"FFFFFF","flair_color":""},{"id":118,"name":"support-explorers","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/4X/d/1/b/d1ba0acf09b9d01f87f9e05bbee1dc5b0e316d5f.png","flair_bg_color":"dddddd","flair_color":"111111"},{"id":47,"name":"team","flair_url":"https://d11a6trkgmumsb.cloudfront.net/original/3X/e/b/ebee30bd98aef20357e4a177a5a1e45b877ce088.svg","flair_bg_color":"","flair_color":"111"},{"id":73,"name":"theme_authors","flair_url":"fa-paint-brush","flair_bg_color":"ddd","flair_color":"111"},{"id":84,"name":"theme_creator","flair_url":"fa-palette","flair_bg_color":"ddd","flair_color":"111"},{"id":50,"name":"translators","flair_url":"fa-globe","flair_bg_color":"ddd","flair_color":"111"}],"filters":["latest","unread","new","unseen","top","read","posted","bookmarks","hot"],"periods":["all","yearly","quarterly","monthly","weekly","daily"],"top_menu_items":["latest","unread","new","unseen","top","read","posted","bookmarks","hot","categories"],"anonymous_top_menu_items":["latest","top","categories","hot","categories","top"],"uncategorized_category_id":17,"user_field_max_length":2048,"post_action_types":[{"id":2,"name_key":"like","name":"Like","description":"Like this post","short_description":"Like this post","is_flag":false,"is_custom_flag":false},{"id":3,"name_key":"off_topic","name":"Off-Topic","description":"This post is not relevant to the current discussion as defined by the title and first post, and should probably be moved elsewhere.","short_description":"Not relevant to the discussion","is_flag":true,"is_custom_flag":false},{"id":4,"name_key":"inappropriate","name":"Inappropriate","description":"This post contains content that a reasonable person would consider offensive, abusive, to be hateful conduct or a violation of \u003ca href=\"/guidelines\"\u003eour community guidelines\u003c/a\u003e.","short_description":"A violation of \u003ca href=\"/guidelines\"\u003eour community guidelines\u003c/a\u003e","is_flag":true,"is_custom_flag":false},{"id":8,"name_key":"spam","name":"Spam","description":"This post is an advertisement, or vandalism. It is not useful or relevant to the current topic.","short_description":"This is an advertisement or vandalism","is_flag":true,"is_custom_flag":false},{"id":6,"name_key":"notify_user","name":"Send @%{username} a message","description":"I want to talk to this person directly and agentlly about their post.","short_description":"I want to talk to this person directly and agentlly about their post.","is_flag":true,"is_custom_flag":true},{"id":10,"name_key":"illegal","name":"Illegal","description":"This post requires staff attention because I believe it contains content that is illegal.","short_description":"This is illegal","is_flag":true,"is_custom_flag":true},{"id":null,"name_key":null,"name":"Translation missing: en.post_action_types..title","description":"Translation missing: en.post_action_types.description","short_description":"Translation missing: en.post_action_types.short_description","is_flag":false,"is_custom_flag":false},{"id":7,"name_key":"notify_moderators","name":"Something Else","description":"This post requires staff attention for another reason not listed above.","short_description":"Requires staff attention for another reason","is_flag":true,"is_custom_flag":true}],"topic_flag_types":[{"id":4,"name_key":"inappropriate","name":"Inappropriate","description":"This topic contains content that a reasonable person would consider offensive, abusive, to be hateful conduct or a violation of \u003ca href=\"/guidelines\"\u003eour community guidelines\u003c/a\u003e.","short_description":"A violation of \u003ca href=\"/guidelines\"\u003eour community guidelines\u003c/a\u003e","is_flag":true,"is_custom_flag":false},{"id":8,"name_key":"spam","name":"Spam","description":"This topic is an advertisement. It is not useful or relevant to this site, but promotional in nature.","short_description":"This is an advertisement","is_flag":true,"is_custom_flag":false},{"id":10,"name_key":"illegal","name":"Illegal","description":"This topic requires staff attention because I believe it contains content that is illegal.","short_description":"This is illegal","is_flag":true,"is_custom_flag":true},{"id":null,"name_key":null,"name":"Translation missing: en.topic_flag_types..title","description":"Translation missing: en.topic_flag_types.description","short_description":"Translation missing: en.topic_flag_types.short_description","is_flag":false,"is_custom_flag":false},{"id":7,"name_key":"notify_moderators","name":"Something Else","description":"This topic requires general staff attention based on the \u003ca href=\"/guidelines\"\u003eguidelines\u003c/a\u003e, \u003ca href=\"/tos\"\u003eTOS\u003c/a\u003e, or for another reason not listed above.","short_description":"Requires staff attention for another reason","is_flag":true,"is_custom_flag":true}],"can_create_tag":false,"can_tag_topics":false,"can_tag_pms":false,"tags_filter_regexp":"[/\\?#\\[\\]@!\\$\u0026'\\(\\)\\*\\+,;=\\.%\\\\`^\\s|\\{\\}\"\u003c\u003e]+","top_tags":["how-to","chat","rest-api","sql-query","unsupported-install","email","pr-welcome","completed","ai","activity-summary","official","solved","sidebar","release-notes","server-resources","calendar-and-event","badges","topic-voting","docker","tags","advertising","invites","search","sql-triggered-badge","subscriptions","new-feature","chat-integration","wordpress","groups","themes","css"],"navigation_menu_site_top_tags":[{"name":"how-to","description":"How to guides contain steps to follow to solve a specific problem","pm_only":false},{"name":"chat","description":null,"pm_only":false},{"name":"rest-api","description":"Topics about making an external request to Discourse","pm_only":false},{"name":"sql-query","description":"SQL queries for the data-explorer","pm_only":false},{"name":"unsupported-install","description":null,"pm_only":false}],"topic_featured_link_allowed_category_ids":[149,120,122,157,121,153,127,9,136,138,61,83,96,56,132,123,144,104,10,27,137,25,124,22,134,24,110,105,143,119,145,135,129,7,30,89,131,150,6,155,31,20,133,55,102,148,147,95,152,151,128,154,21,139,108,69,3,14,125,63,2,126,115,106,1,118,117,17,53,13,5,65,8,67,35],"user_themes":[{"theme_id":272,"name":"Accessible contrast (dark)","default":false,"color_scheme_id":85},{"theme_id":271,"name":"Accessible contrast (light)","default":false,"color_scheme_id":84},{"theme_id":242,"name":"Air Theme","default":false,"color_scheme_id":82},{"theme_id":337,"name":"Central","default":false,"color_scheme_id":100},{"theme_id":335,"name":"CentralStaffOnly","default":false,"color_scheme_id":96},{"theme_id":31,"name":"Dark","default":false,"color_scheme_id":86},{"theme_id":140,"name":"Default","default":false,"color_scheme_id":34},{"theme_id":281,"name":"Default (full-width)","default":false,"color_scheme_id":34},{"theme_id":51,"name":"Discourse-classic","default":false,"color_scheme_id":34},{"theme_id":296,"name":"Fully","default":false,"color_scheme_id":null},{"theme_id":131,"name":"Ghost","default":false,"color_scheme_id":66},{"theme_id":80,"name":"Graceful","default":false,"color_scheme_id":54},{"theme_id":83,"name":"Grey Amber","default":false,"color_scheme_id":57},{"theme_id":232,"name":"Hidden Whispers","default":false,"color_scheme_id":null},{"theme_id":34,"name":"Material Design","default":false,"color_scheme_id":37},{"theme_id":331,"name":"Meta Branded","default":true,"color_scheme_id":34},{"theme_id":125,"name":"Minima Dark","default":false,"color_scheme_id":43},{"theme_id":299,"name":"redditish","default":false,"color_scheme_id":null},{"theme_id":30,"name":"Sam's Simple Theme","default":false,"color_scheme_id":null}],"user_color_schemes":[{"id":93,"name":"Central Dark","is_dark":true},{"id":95,"name":"Central Dark","is_dark":true},{"id":100,"name":"Central Light","is_dark":false},{"id":96,"name":"Central Light","is_dark":false},{"id":86,"name":"Dark","is_dark":true},{"id":34,"name":"Default Light","is_dark":false},{"id":87,"name":"Solarized Light","is_dark":false},{"id":85,"name":"WCAG Dark","is_dark":true},{"id":84,"name":"WCAG Light","is_dark":false}],"default_dark_color_scheme":null,"censored_regexp":[{"(?:\\P{L}|^)(EICARTESTCENSOR)(?=\\P{L}|$)":{"case_sensitive":false}}],"custom_emoji_translation":{},"watched_words_replace":{"(?:\\P{L}|^)(¯_\\(ツ\\)_/¯)(?=\\P{L}|$)":{"word":"¯_(ツ)_/¯","replacement":"¯\\_(ツ)_/¯","case_sensitive":false}},"watched_words_link":{"(?:\\P{L}|^)(Data Explorer)(?=\\P{L}|$)":{"word":"Data Explorer","replacement":"https://meta.discourse.org/t/32566?silent=true","case_sensitive":false},"(?:\\P{L}|^)(discourseconnect)(?=\\P{L}|$)":{"word":"discourseconnect","replacement":"https://meta.discourse.org/t/13045?silent=true","case_sensitive":false},"(?:\\P{L}|^)(discourse connect)(?=\\P{L}|$)":{"word":"discourse connect","replacement":"https://meta.discourse.org/t/13045?silent=true","case_sensitive":false},"(?:\\P{L}|^)(discourse sso)(?=\\P{L}|$)":{"word":"discourse sso","replacement":"https://meta.discourse.org/t/13045?silent=true","case_sensitive":false},"(?:\\P{L}|^)(official install)(?=\\P{L}|$)":{"word":"official install","replacement":"https://meta.discourse.org/t/142537?silent=true","case_sensitive":false},"(?:\\P{L}|^)(standard install)(?=\\P{L}|$)":{"word":"standard install","replacement":"https://meta.discourse.org/t/142537?silent=true","case_sensitive":false},"(?:\\P{L}|^)(safe mode)(?=\\P{L}|$)":{"word":"safe mode","replacement":"https://meta.discourse.org/t/53504?silent=true","case_sensitive":false}},"categories":[{"id":67,"name":"announcements","color":"ED207B","text_color":"FFFFFF","slug":"announcements","topic_count":312,"post_count":3973,"position":0,"description":"The place for all Discourse announcements.","description_text":"The place for all Discourse announcements.","description_excerpt":"The place for all Discourse announcements.","topic_url":"/t/about-the-announcements-category/68629","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_enabled":true,"activity_pub_username":"announcements","activity_pub_name":"Announcements","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["new-feature","security"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":2,"handle":"announcements@meta.discourse.org","name":"Announcements"},"activity_pub_username":"announcements","activity_pub_name":"Announcements","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Note","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":13,"name":"blog","color":"8080ff","text_color":"FFFFFF","slug":"blog","topic_count":170,"post_count":1477,"position":1,"description":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","description_text":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","description_excerpt":"Discussion topics generated from the official Discourse Blog. These topics are linked from the bottom of each blog entry where the blog comments would normally be.","topic_url":"/t/about-the-blog-category/5250","read_restricted":false,"permission":null,"parent_category_id":67,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"created","sort_ascending":false,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":10,"name":"documentation","color":"00A94F","text_color":"FFFFFF","slug":"documentation","topic_count":2,"post_count":2,"position":2,"description":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","description_text":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","description_excerpt":"Documentation for how to use Discourse, install and configure sites, and troubleshoot common issues. Includes topics for tutorials, how-tos, general reference and troubleshooting. Topics need to be created in one of the subcategories, and may only be created by trust level 2 and up.","topic_url":"/t/about-the-documentation-category/2629","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"top","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Article","activity_pub_publication_type":"full_topic","activity_pub_username":"documentation","activity_pub_enabled":true,"activity_pub_name":"Documentation"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":31769,"handle":"documentation@meta.discourse.org","name":"Documentation"},"activity_pub_username":"documentation","activity_pub_name":"Documentation","activity_pub_default_visibility":"public","activity_pub_publication_type":"full_topic","activity_pub_post_object_type":"Article","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":53,"name":"admins","color":"F15D22","text_color":"FFFFFF","slug":"admins","topic_count":222,"post_count":1703,"position":3,"description":"Guides for Discourse admins and community managers with admin access.","description_text":"Guides for Discourse admins and community managers with admin access.","description_excerpt":"Guides for Discourse admins and community managers with admin access.","topic_url":"/t/about-the-admins-category/56207","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["hosted-support","migrations"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":125,"name":"moderators","color":"40d0e2","text_color":"FFFFFF","slug":"moderators","topic_count":16,"post_count":71,"position":4,"description":"Documentation for moderators and community managers using Discourse.","description_text":"Documentation for moderators and community managers using Discourse.","description_excerpt":"Documentation for moderators and community managers using Discourse.","topic_url":"/t/about-the-moderators-category/238588","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":126,"name":"users","color":"0088CC","text_color":"FFFFFF","slug":"users","topic_count":53,"post_count":184,"position":5,"description":"Documentation for all members of communities running Discourse.","description_text":"Documentation for all members of communities running Discourse.","description_excerpt":"Documentation for all members of communities running Discourse.","topic_url":"/t/about-the-users-category/238917","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":55,"name":"sysadmin","color":"E9DD00","text_color":"FFFFFF","slug":"sysadmin","topic_count":175,"post_count":2803,"position":6,"description":"Documentation for self-hosters and Discourse system administrators.","description_text":"Documentation for self-hosters and Discourse system administrators.","description_excerpt":"Documentation for self-hosters and Discourse system administrators.","topic_url":"/t/about-the-sysadmin-category/56209","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":["migrations"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":127,"name":"theme developers","color":"92278F","text_color":"FFFFFF","slug":"theme-developers","topic_count":41,"post_count":215,"position":7,"description":"Documentation for developing themes and components that can be installed by admins.","description_text":"Documentation for developing themes and components that can be installed by admins.","description_excerpt":"Documentation for developing themes and components that can be installed by admins.","topic_url":"/t/about-the-theme-developers-category/239285","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":56,"name":"developers","color":"00A94F","text_color":"FFFFFF","slug":"devs","topic_count":96,"post_count":1185,"position":8,"description":"Documentation for developing features, plugins, or integrations with Discourse.","description_text":"Documentation for developing features, plugins, or integrations with Discourse.","description_excerpt":"Documentation for developing features, plugins, or integrations with Discourse.","topic_url":"/t/about-the-developers-category/56210","read_restricted":false,"permission":null,"parent_category_id":10,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Article","activity_pub_enabled":true,"activity_pub_username":"developer-docs","activity_pub_name":"Developer Documentation","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":35545,"handle":"developer-docs@meta.discourse.org","name":"Developer Documentation"},"activity_pub_username":"developer-docs","activity_pub_name":"Developer Documentation","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Article","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":6,"name":"support","color":"CEA9A9","text_color":"FFFFFF","slug":"support","topic_count":15841,"post_count":103687,"position":9,"description":"The category for general support questions on using your Discourse site.","description_text":"The category for general support questions on using your Discourse site.","description_excerpt":"The category for general support questions on using your Discourse site.","topic_url":"/t/about-the-support-category/389","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"\u003e Before asking, did you search first? Press 🔍 at the upper right to search.","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_accepted_answers":"true","enable_unassigned_filter":"false","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":21,"name":"wordpress","color":"F9CFCF","text_color":"FFFFFF","slug":"wordpress","topic_count":732,"post_count":5110,"position":10,"description":"Support for the official Discourse WordPress plugin at \u003ca href=\"https://github.com/discourse/wp-discourse\" class=\"inline-onebox\"\u003eGitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog\u003c/a\u003e","description_text":"Support for the official Discourse WordPress plugin at GitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog","description_excerpt":"Support for the official Discourse WordPress plugin at \u003ca href=\"https://github.com/discourse/wp-discourse\" class=\"inline-onebox\"\u003eGitHub - discourse/wp-discourse: WordPress plugin that lets you use Discourse as the community engine for a WordPress blog\u003c/a\u003e","topic_url":"/t/about-the-wordpress-category/12282","read_restricted":false,"permission":null,"parent_category_id":6,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":1,"name":"bug","color":"e9dd00","text_color":"000000","slug":"bug","topic_count":5153,"post_count":35973,"position":11,"description":"A bug report means \u003cstrong\u003esomething is broken, preventing normal/typical use of Discourse\u003c/strong\u003e. Do be sure to search prior to submitting bugs. \u003ca href=\"https://meta.discourse.org/t/how-to-write-a-good-bug-report/183671/\"\u003eInclude repro steps\u003c/a\u003e, and only describe one bug per topic please.","description_text":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. Include repro steps, and only describe one bug per topic please.","description_excerpt":"A bug report means something is broken, preventing normal/typical use of Discourse. Do be sure to search prior to submitting bugs. \u003ca href=\"https://meta.discourse.org/t/how-to-write-a-good-bug-report/183671/\"\u003eInclude repro steps\u003c/a\u003e, and only describe one bug per topic please.","topic_url":"/t/about-the-bug-category/2","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_accepted_answers":null,"enable_unassigned_filter":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":9,"name":"ux","color":"5F497A","text_color":"FFFFFF","slug":"ux","topic_count":2700,"post_count":18225,"position":12,"description":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","description_text":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","description_excerpt":"Discussion about the user interface of Discourse and how features are presented (including language and UI elements).","topic_url":"/t/about-the-ux-category/2628","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":2,"name":"feature","color":"0E76BD","text_color":"FFFFFF","slug":"feature","topic_count":6997,"post_count":58067,"position":13,"description":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","description_text":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","description_excerpt":"Discussion about existing Discourse features, how they can be improved or enhanced, and how proposed new features could work.","topic_url":"/t/about-the-feature-category/11","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_ready":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_username":"feature","activity_pub_name":"Feature Requests","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post","activity_pub_enabled":true},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":true,"activity_pub_ready":true,"activity_pub_actor":{"id":1,"handle":"feature@meta.discourse.org","name":"Feature Requests"},"activity_pub_username":"feature","activity_pub_name":"Feature Requests","activity_pub_default_visibility":"public","activity_pub_publication_type":"first_post","activity_pub_post_object_type":"Note","uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":148,"name":"data \u0026 reporting","color":"D24899","text_color":"FFFFFF","slug":"data-reporting","topic_count":631,"post_count":3056,"position":14,"description":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","description_text":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","description_excerpt":"This is the category for everything related to Discourse data and reporting. Here, you can discuss dashboard reports, custom badge queries, data-explorer queries and any other analytic add-ons.","topic_url":"/t/about-the-data-reporting-category/274664","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","enable_accepted_answers":"true","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":24,"name":"sso","color":"d47711","text_color":"FFFFFF","slug":"sso","topic_count":493,"post_count":2546,"position":15,"description":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the \u003ca href=\"https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045\"\u003eofficial documentation on DiscourseConnect SSO\u003c/a\u003e.","description_text":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the official documentation on DiscourseConnect SSO.","description_excerpt":"For queries specifically about SSO (single sign-on) and login using third-party providers (Google, Facebook, GitHub etc). See the \u003ca href=\"https://meta.discourse.org/t/discourseconnect-official-single-sign-on-for-discourse-sso/13045\"\u003eofficial documentation on DiscourseConnect SSO\u003c/a\u003e.","topic_url":"/t/about-the-sso-category/13110","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":65,"name":"community","color":"12A89D","text_color":"FFFFFF","slug":"community","topic_count":866,"post_count":8844,"position":16,"description":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","description_text":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","description_excerpt":"A great platform doesn’t guarantee success. Community building is a science. This category is for discussions about launching, building, growing and managing a thriving community.","topic_url":"/t/about-the-community-category/67750","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":7,"name":"dev","color":"292929","text_color":"fff","slug":"dev","topic_count":3467,"post_count":20210,"position":17,"description":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","description_text":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","description_excerpt":"The category for all things Discourse Development. Building a customization for yourself or the community? Then this is the category for you!","topic_url":"/t/about-the-dev-category/1026","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":27,"name":"translations","color":"808281","text_color":"FFFFFF","slug":"translations","topic_count":297,"post_count":1832,"position":18,"description":"This category is for discussion about localizing Discourse.","description_text":"This category is for discussion about localizing Discourse.","description_excerpt":"This category is for discussion about localizing Discourse.","topic_url":"/t/about-the-translations-category/14549","read_restricted":false,"permission":null,"parent_category_id":7,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":14,"name":"marketplace","color":"8C6238","text_color":"FFFFFF","slug":"marketplace","topic_count":1162,"post_count":5865,"position":19,"description":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","description_text":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","description_excerpt":"This is your hub for all Discourse-related commerce: jobs, gigs, plugins, themes, hosting, and more.","topic_url":"/t/about-the-marketplace-category/5425","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"What would you like done?\n\nWhen do you need it done?\n\nWhat is your budget, in $ USD that you can offer for this task?\n\n\u003c!-- We encourage caution and due diligence when engaging with potential contractors or clients. Verify their credentials, check previous work, and ensure a transparent and legitimate transaction. Always remember, your safety and security in the marketplace is your responsibility. --\u003e","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["delivered"],"allowed_tag_groups":[],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":22,"name":"plugin","color":"F7941D","text_color":"FFFFFF","slug":"plugin","topic_count":315,"post_count":10429,"position":20,"description":"A directory of Discourse plugins, both official and third-party.","description_text":"A directory of Discourse plugins, both official and third-party.","description_excerpt":"A directory of Discourse plugins, both official and third-party.","topic_url":"/t/about-the-plugin-category/12648","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"| | | |\n| - | - | - |\n| :information_source: | **Summary** | In a few words, what does this plugin do? |\n| :hammer_and_wrench: | **Repository Link** | \u003c\u003e |\n| :open_book: | **Install Guide** | [How to install plugins in Discourse](https://meta.discourse.org/t/install-plugins-in-discourse/19157) |\n\n\u003cbr\u003e \n\n### Features\n \nDescribe the major features of the plugin\n \n### Configuration\n \nInclude detailed steps on how to configure the plugin (include screenshots where necessary)\n \n### CHANGELOG\n- Add new bullets when major features are committed here\n \n### TODO\n- Add any #pr-welcome TODO tasks here","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"boxes","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":5,"name":"extras","color":"25AAE2","text_color":"FFFFFF","slug":"extras","topic_count":90,"post_count":985,"position":21,"description":"A directory of all extensions \u0026amp; integrations for Discourse which are \u003cem\u003enot\u003c/em\u003e Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","description_text":"A directory of all extensions \u0026amp; integrations for Discourse which are not Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","description_excerpt":"A directory of all extensions \u0026amp; integrations for Discourse which are not Discourse plugins, i.e. a CMS plugin, a browser extension or a native application.","topic_url":"/t/about-the-extras-category/28","read_restricted":false,"permission":null,"parent_category_id":22,"notification_level":1,"topic_template":"","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":61,"name":"theme","color":"E43D30","text_color":"FFFFFF","slug":"theme","topic_count":66,"post_count":2138,"position":22,"description":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","description_text":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","description_excerpt":"Themes are expansive customizations that change multiple elements of the style of your forum design, and often also include additional front-end features.","topic_url":"/t/about-the-theme-category/60925","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"||||\n|-|-|-|\n| :information_source: | **Summary** | ADD SHORT SUMMARY \n| :eyeglasses:|**Preview**| PREVIEW_LINK |\n| :hammer_and_wrench:|**Repository**| REPOSITORY_LINK |\n| :question:|**Install Guide**|[How to install a theme or theme component](https://meta.discourse.org/t/how-do-i-install-a-theme-or-theme-component/63682)|\n| :open_book:|**New to Discourse Themes?**| [Beginner’s guide to using Discourse Themes](https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966)\n\n\u003c!-- Describe this theme in one or two sentences --\u003e\n\nShort description...\n\n\u003c!-- Add screenshots (if applicable) --\u003e\n\nScreenshots...\n\n\u003c!-- Add more details and explain the settings (if applicable) --\u003e\n\nDetailed description...\n","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"none","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":["color-palette"],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":120,"name":"theme-component","color":"1dedf8","text_color":"FFFFFF","slug":"theme-component","topic_count":300,"post_count":7516,"position":23,"description":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","description_text":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","description_excerpt":"Theme components are customizations that change surface elements of your forum design, or add extra front-end features.","topic_url":"/t/about-the-theme-component-category/232731","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"| | | |\n| - | - | - |\n| :information_source: | **Summary** | ADD SHORT SUMMARY |\n| :eyeglasses: |**Preview**| PREVIEW_LINK |\n| :hammer_and_wrench: | **Repository**| REPOSITORY_LINK |\n| :question: | **Install Guide** | [How to install a theme or theme component](https://meta.discourse.org/t/how-do-i-install-a-theme-or-theme-component/63682) |\n| :open_book: | **New to Discourse Themes?** | [Beginner’s guide to using Discourse Themes](https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966) |\n\n\u003c!-- Fill in \"repoName\" and \"repoURL\" for the automatic install button --\u003e\n\n[wrap=theme-install-button repoName=\"Component's name\" repoUrl=\"GitHub repository link\"]\nInstall this theme component\n[/wrap]\n\n\u003c!-- Describe this theme/component in one or two sentences --\u003e\n\nShort description...\n\n\u003c!-- Add screenshots (if applicable) --\u003e\n\nScreenshots...\n\n\u003c!-- Add more details and explain the settings (if applicable) --\u003e\n\nDetailed description...","has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"none","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Resource Status"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":31,"name":"installation","color":"997E7E","text_color":"FFFFFF","slug":"installation","topic_count":3426,"post_count":29611,"position":24,"description":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","description_text":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","description_excerpt":"Getting Discourse up and running, keeping it going, upgrading, and any other general sysadmin maintenance.","topic_url":"/t/about-the-installation-category/21019","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":106,"name":"migration","color":"652D90","text_color":"FFFFFF","slug":"migration","topic_count":215,"post_count":1558,"position":25,"description":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","description_text":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","description_excerpt":"You want to migrate your community to Discourse? Awesome! This is the category where you can ask questions, get help, and document your Discourse migration journey.","topic_url":"/t/about-the-migration-category/196969","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":"true","activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":["Migration"],"allow_global_tags":true,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":8,"name":"hosting","color":"00AEEF","text_color":"FFFFFF","slug":"hosting","topic_count":501,"post_count":4117,"position":26,"description":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","description_text":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","description_excerpt":"Topics about hosting Discourse, either on your own servers, in the cloud, or with specific hosting services.","topic_url":"/t/about-the-hosting-category/2626","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post","enable_accepted_answers":"true"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":30,"name":"releases","color":"BF1E2E","text_color":"FFFFFF","slug":"releases","topic_count":23,"post_count":104,"position":27,"description":"Outlining each official release of Discourse, and plans for future releases.","description_text":"Outlining each official release of Discourse, and plans for future releases.","description_excerpt":"Outlining each official release of Discourse, and plans for future releases.","topic_url":"/t/about-the-releases-category/20857","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"created","sort_ascending":false,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":3,"name":"site feedback","color":"888","text_color":"FFFFFF","slug":"site-feedback","topic_count":417,"post_count":3220,"position":28,"description":"Discussion about \u003ca href=\"http://meta.discourse.org\"\u003emeta.discourse.org\u003c/a\u003e itself - the organization of this forum, how it works, and how we can improve this site.","description_text":"Discussion about meta.discourse.org itself - the organization of this forum, how it works, and how we can improve this site.","description_excerpt":"Discussion about \u003ca href=\"http://meta.discourse.org\"\u003emeta.discourse.org\u003c/a\u003e itself - the organization of this forum, how it works, and how we can improve this site.","topic_url":"/t/about-the-site-feedback-category/24","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":true,"sort_order":"","sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":152,"name":"theme feedback","color":"ED207B","text_color":"FFFFFF","slug":"theme-feedback","topic_count":11,"post_count":44,"position":29,"description":"This is the category to gather all the UX reports for \u003ca href=\"https://meta.discourse.org/t/we-have-a-new-default-theme-here-on-meta/284692\"\u003eour new theme on meta\u003c/a\u003e. It also uses the new Form Templates feature that I’ve been wanting to try out.","description_text":"This is the category to gather all the UX reports for our new theme on meta. It also uses the new Form Templates feature that I’ve been wanting to try out.","description_excerpt":"This is the category to gather all the UX reports for \u003ca href=\"https://meta.discourse.org/t/we-have-a-new-default-theme-here-on-meta/284692\"\u003eour new theme on meta\u003c/a\u003e. It also uses the new Form Templates feature that I’ve been wanting to try out.","topic_url":"/t/about-the-theme-feedback-category/284904","read_restricted":false,"permission":null,"parent_category_id":3,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[1],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":157,"name":"forum summaries","color":"72e9a7","text_color":"FFFFFF","slug":"forum-summaries","topic_count":4,"post_count":36,"position":30,"description":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summaries deliver the essence of the Discourse community’s heartbeat, right at your fingertips.","description_text":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summaries deliver the essence of the Discourse community’s heartbeat, right at your fingertips.","description_excerpt":"Stay up-to-date with the pulse of our community through AI-crafted summaries. This category harnesses the power of artificial intelligence to collate and condense forum activities, providing you with comprehensive yet succinct overviews. From emerging discussions to trending topics, our AI summarie\u0026hellip;","topic_url":"/t/about-the-forum-summaries-category/291765","read_restricted":false,"permission":null,"parent_category_id":3,"notification_level":0,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":35,"name":"praise","color":"9EB83B","text_color":"FFFFFF","slug":"praise","topic_count":294,"post_count":1091,"position":31,"description":"Have something nice to say about Discourse?","description_text":"Have something nice to say about Discourse?","description_excerpt":"Have something nice to say about Discourse?","topic_url":"/t/about-the-praise-category/30010","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":true,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":63,"name":"comparison","color":"F1592A","text_color":"FFFFFF","slug":"comparison","topic_count":11,"post_count":137,"position":32,"description":"Topics comparing Discourse to other platforms.","description_text":"Topics comparing Discourse to other platforms.","description_excerpt":"Topics comparing Discourse to other platforms.","topic_url":"/t/about-the-comparison-category/65736","read_restricted":false,"permission":null,"parent_category_id":35,"notification_level":1,"topic_template":"### About PLATFORM_NAME\n\nA blurb about the platform being compared to Discourse. \n\nhttp://link.to.website\n\n ### Previous discussions:\n\nlinks to previous discussions related\n\n### Importer status\n\nIs there an official Discourse importer? Where is it? ","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"latest","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":105,"name":"community support program","color":"92278F","text_color":"FFFFFF","slug":"support-program","topic_count":4,"post_count":27,"position":33,"description":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","description_text":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","description_excerpt":"Get recognition for your work in helping and supporting Discourse communities by joining our Community Support Program.","topic_url":"/t/about-the-community-support-program-category/193906","read_restricted":false,"permission":null,"notification_level":1,"topic_template":"","has_children":false,"sort_order":"","sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":"","subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":true,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":"","form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":124,"name":"General","color":"25AAE2","text_color":"FFFFFF","slug":"general","topic_count":155,"post_count":1408,"position":35,"description":"Create topics here that don’t fit into any other existing category.","description_text":"Create topics here that don’t fit into any other existing category.","description_excerpt":"Create topics here that don’t fit into any other existing category.","topic_url":"/t/about-the-general-category/237517","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":false,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows_with_featured_topics","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"enable_unassigned_filter":null,"enable_accepted_answers":null,"activity_pub_default_visibility":"public","activity_pub_post_object_type":"Note","activity_pub_publication_type":"first_post"},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false},{"id":17,"name":"Uncategorized","color":"AB9364","text_color":"FFFFFF","slug":"uncategorized","topic_count":0,"post_count":0,"position":77,"description":"Topics that don't need a category, or don't fit into any other existing category.","description_text":"Topics that don't need a category, or don't fit into any other existing category.","description_excerpt":"Topics that don't need a category, or don't fit into any other existing category.","topic_url":"/t/","read_restricted":false,"permission":null,"notification_level":1,"topic_template":null,"has_children":false,"sort_order":null,"sort_ascending":null,"show_subcategory_list":true,"num_featured_topics":3,"default_view":null,"subcategory_list_style":"rows","default_top_period":"all","default_list_filter":"all","minimum_required_tags":0,"navigate_to_first_post_after_read":false,"custom_fields":{"has_chat_enabled":null,"activity_pub_enabled":null,"activity_pub_ready":null,"activity_pub_username":null,"activity_pub_name":null,"activity_pub_default_visibility":null,"activity_pub_publication_type":null,"activity_pub_post_object_type":null,"enable_unassigned_filter":null,"enable_accepted_answers":null},"allowed_tags":[],"allowed_tag_groups":[],"allow_global_tags":false,"read_only_banner":null,"form_template_ids":[],"activity_pub_enabled":false,"uploaded_logo":null,"uploaded_logo_dark":null,"uploaded_background":null,"uploaded_background_dark":null,"required_tag_groups":[],"can_edit":false}],"markdown_additional_options":{"chat":{"limited_pretty_text_features":["anchor","bbcode-block","bbcode-inline","code","category-hashtag","censored","chat-transcript","discourse-local-dates","emoji","emojiShortcuts","inlineEmoji","html-img","hashtag-autocomplete","mentions","unicodeUsernames","onebox","quotes","spoiler-alert","table","text-post-process","upload-protocol","watched-words"],"limited_pretty_text_markdown_rules":["autolink","list","backticks","newline","code","fence","image","table","linkify","link","strikethrough","blockquote","emphasis","replacements"],"hashtag_configurations":{"topic-composer":["category","tag","channel"],"chat-composer":["channel","category","tag"]}}},"hashtag_configurations":{"topic-composer":["category","tag","channel"],"chat-composer":["channel","category","tag"]},"hashtag_icons":{"category":"folder","tag":"tag","channel":"comment"},"displayed_about_plugin_stat_groups":["chat_messages"],"anonymous_default_navigation_menu_tags":[{"name":"official","description":"This is a theme or plugin built by the Discourse team","pm_only":false},{"name":"release-notes","description":"Release notes for Discourse beta and stable branches. See https://meta.discourse.org/t/198215 for more details on the Discourse release process.","pm_only":false}],"anonymous_sidebar_sections":[{"id":56,"title":"Community","links":[{"id":274,"name":"Topics","value":"/latest","icon":"layer-group","external":false,"full_reload":false,"segment":"primary"},{"id":275,"name":"My Posts","value":"/my/activity","icon":"user","external":false,"full_reload":true,"segment":"primary"},{"id":276,"name":"Review","value":"/review","icon":"flag","external":false,"full_reload":false,"segment":"primary"},{"id":277,"name":"Admin","value":"/admin","icon":"wrench","external":false,"full_reload":false,"segment":"primary"},{"id":279,"name":"Users","value":"/u","icon":"users","external":false,"full_reload":false,"segment":"secondary"},{"id":280,"name":"About","value":"/about","icon":"info-circle","external":false,"full_reload":false,"segment":"secondary"},{"id":281,"name":"FAQ","value":"/faq","icon":"question-circle","external":false,"full_reload":false,"segment":"secondary"},{"id":282,"name":"Groups","value":"/g","icon":"user-friends","external":false,"full_reload":false,"segment":"secondary"},{"id":283,"name":"Badges","value":"/badges","icon":"certificate","external":false,"full_reload":false,"segment":"secondary"},{"id":287,"name":"Leaderboard","value":"/leaderboard/7","icon":"trophy","external":false,"full_reload":false,"segment":"secondary"},{"id":290,"name":"Global Leaderboard","value":"/leaderboard","icon":"trophy","external":false,"full_reload":false,"segment":"secondary"},{"id":291,"name":"Topic Filter","value":"/filter","icon":"filter","external":false,"full_reload":false,"segment":"secondary"}],"slug":"community","public":true,"section_type":"community"}],"tos_url":"/tos","privacy_policy_url":"https://www.discourse.org/privacy","activity_pub_enabled":true,"activity_pub_publishing_enabled":true,"activity_pub_host":"meta.discourse.org","docs_path":"docs","default_gamification_leaderboard_id":1,"hosting_tier":"enterprise","archetypes":[{"id":"regular","name":"Regular Topic","options":[]},{"id":"banner","name":"Banner Topic","options":[]}],"user_fields":[{"id":2,"name":"Pronouns","description":"Gender Pronouns (he/him, she/her, they/them, etc.)","field_type":"text","editable":true,"required":false,"show_on_profile":true,"show_on_user_card":true,"searchable":false,"position":1}],"auth_providers":[{"name":"facebook","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":580,"frame_height":400,"can_connect":true,"can_revoke":true,"icon":"fab-facebook"},{"name":"google_oauth2","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":850,"frame_height":500,"can_connect":true,"can_revoke":true,"icon":null},{"name":"github","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":null,"frame_height":null,"can_connect":true,"can_revoke":true,"icon":"fab-github"},{"name":"twitter","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":null,"frame_height":null,"can_connect":true,"can_revoke":true,"icon":"fab-twitter"},{"name":"discord","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":null,"frame_height":null,"can_connect":true,"can_revoke":true,"icon":"fab-discord"},{"name":"apple","custom_url":null,"pretty_name_override":null,"title_override":null,"frame_width":null,"frame_height":null,"can_connect":true,"can_revoke":true,"icon":"fab-apple"}]} \ No newline at end of file diff --git a/spec/jobs/regular/digest_rag_upload_spec.rb b/spec/jobs/regular/digest_rag_upload_spec.rb index d3b1ed58..26a2beb7 100644 --- a/spec/jobs/regular/digest_rag_upload_spec.rb +++ b/spec/jobs/regular/digest_rag_upload_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Jobs::DigestRagUpload do - fab!(:persona) { Fabricate(:ai_persona) } + fab!(:agent) { Fabricate(:ai_agent) } fab!(:upload) { Fabricate(:upload, extension: "txt") } fab!(:image_upload) { Fabricate(:upload, extension: "png") } let(:document_file) { StringIO.new("some text" * 200) } @@ -38,8 +38,8 @@ RSpec.describe Jobs::DigestRagUpload do expect { described_class.new.execute( upload_id: image_upload.id, - target_id: persona.id, - target_type: persona.class.to_s, + target_id: agent.id, + target_type: agent.class.to_s, ) }.to raise_error(Discourse::InvalidAccess) end @@ -47,12 +47,12 @@ RSpec.describe Jobs::DigestRagUpload do context "when processing an upload containing metadata" do it "correctly splits on metadata boundary" do # be explicit here about chunking strategy - persona.update!(rag_chunk_tokens: 100, rag_chunk_overlap_tokens: 10) + agent.update!(rag_chunk_tokens: 100, rag_chunk_overlap_tokens: 10) described_class.new.execute( upload_id: upload_with_metadata.id, - target_id: persona.id, - target_type: persona.class.to_s, + target_id: agent.id, + target_type: agent.class.to_s, ) parsed = +"" @@ -80,8 +80,8 @@ RSpec.describe Jobs::DigestRagUpload do it "splits an upload into chunks" do subject.execute( upload_id: upload.id, - target_id: persona.id, - target_type: persona.class.to_s, + target_id: agent.id, + target_type: agent.class.to_s, ) created_fragment = RagDocumentFragment.last @@ -95,20 +95,20 @@ RSpec.describe Jobs::DigestRagUpload do expect { subject.execute( upload_id: upload.id, - target_id: persona.id, - target_type: persona.class.to_s, + target_id: agent.id, + target_type: agent.class.to_s, ) }.to change(Jobs::GenerateRagEmbeddings.jobs, :size).by(1) end end it "doesn't generate new fragments if we already processed the upload" do - Fabricate(:rag_document_fragment, upload: upload, target: persona) + Fabricate(:rag_document_fragment, upload: upload, target: agent) - previous_count = RagDocumentFragment.where(upload: upload, target: persona).count + previous_count = RagDocumentFragment.where(upload: upload, target: agent).count - subject.execute(upload_id: upload.id, target_id: persona.id, target_type: persona.class.to_s) - updated_count = RagDocumentFragment.where(upload: upload, target: persona).count + subject.execute(upload_id: upload.id, target_id: agent.id, target_type: agent.class.to_s) + updated_count = RagDocumentFragment.where(upload: upload, target: agent).count expect(updated_count).to eq(previous_count) end diff --git a/spec/jobs/regular/generate_rag_embeddings_spec.rb b/spec/jobs/regular/generate_rag_embeddings_spec.rb index 10558745..9058ecc0 100644 --- a/spec/jobs/regular/generate_rag_embeddings_spec.rb +++ b/spec/jobs/regular/generate_rag_embeddings_spec.rb @@ -6,10 +6,10 @@ RSpec.describe Jobs::GenerateRagEmbeddings do let(:expected_embedding) { [0.0038493] * vector_def.dimensions } - fab!(:ai_persona) + fab!(:ai_agent) - let(:rag_document_fragment_1) { Fabricate(:rag_document_fragment, target: ai_persona) } - let(:rag_document_fragment_2) { Fabricate(:rag_document_fragment, target: ai_persona) } + let(:rag_document_fragment_1) { Fabricate(:rag_document_fragment, target: ai_agent) } + let(:rag_document_fragment_2) { Fabricate(:rag_document_fragment, target: ai_agent) } before do SiteSetting.ai_embeddings_selected_model = vector_def.id diff --git a/spec/jobs/regular/stream_discord_reply_spec.rb b/spec/jobs/regular/stream_discord_reply_spec.rb index 1543bb3f..3f5a7869 100644 --- a/spec/jobs/regular/stream_discord_reply_spec.rb +++ b/spec/jobs/regular/stream_discord_reply_spec.rb @@ -14,22 +14,22 @@ RSpec.describe Jobs::StreamDiscordReply, type: :job do end fab!(:llm_model) - fab!(:persona) { Fabricate(:ai_persona, default_llm_id: llm_model.id) } + fab!(:agent) { Fabricate(:ai_agent, default_llm_id: llm_model.id) } before do SiteSetting.ai_discord_search_enabled = true - SiteSetting.ai_discord_search_mode = "persona" - SiteSetting.ai_discord_search_persona = persona.id + SiteSetting.ai_discord_search_mode = "agent" + SiteSetting.ai_discord_search_agent = agent.id end - it "calls PersonaReplier when search mode is persona" do - expect_any_instance_of(DiscourseAi::Discord::Bot::PersonaReplier).to receive( + it "calls AgentReplier when search mode is agent" do + expect_any_instance_of(DiscourseAi::Discord::Bot::AgentReplier).to receive( :handle_interaction!, ) described_class.new.execute(interaction: interaction) end - it "calls Search when search mode is not persona" do + it "calls Search when search mode is not agent" do SiteSetting.ai_discord_search_mode = "search" expect_any_instance_of(DiscourseAi::Discord::Bot::Search).to receive(:handle_interaction!) described_class.new.execute(interaction: interaction) diff --git a/spec/jobs/regular/stream_discover_reply_spec.rb b/spec/jobs/regular/stream_discover_reply_spec.rb index 4736f03e..2d18c8d9 100644 --- a/spec/jobs/regular/stream_discover_reply_spec.rb +++ b/spec/jobs/regular/stream_discover_reply_spec.rb @@ -7,12 +7,12 @@ RSpec.describe Jobs::StreamDiscoverReply do fab!(:user) fab!(:llm_model) fab!(:group) - fab!(:ai_persona) do - Fabricate(:ai_persona, allowed_group_ids: [group.id], default_llm_id: llm_model.id) + fab!(:ai_agent) do + Fabricate(:ai_agent, allowed_group_ids: [group.id], default_llm_id: llm_model.id) end before do - SiteSetting.ai_bot_discover_persona = ai_persona.id + SiteSetting.ai_bot_discover_agent = ai_agent.id group.add(user) end diff --git a/spec/lib/personas/artifact_update_strategies/diff_spec.rb b/spec/lib/agents/artifact_update_strategies/diff_spec.rb similarity index 98% rename from spec/lib/personas/artifact_update_strategies/diff_spec.rb rename to spec/lib/agents/artifact_update_strategies/diff_spec.rb index d07813ff..4403f916 100644 --- a/spec/lib/personas/artifact_update_strategies/diff_spec.rb +++ b/spec/lib/agents/artifact_update_strategies/diff_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::ArtifactUpdateStrategies::Diff do +RSpec.describe DiscourseAi::Agents::ArtifactUpdateStrategies::Diff do fab!(:user) fab!(:post) fab!(:artifact) { Fabricate(:ai_artifact) } diff --git a/spec/lib/personas/bot_spec.rb b/spec/lib/agents/bot_spec.rb similarity index 79% rename from spec/lib/personas/bot_spec.rb rename to spec/lib/agents/bot_spec.rb index 5578f8b7..c8d6b142 100644 --- a/spec/lib/personas/bot_spec.rb +++ b/spec/lib/agents/bot_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Bot do - subject(:bot) { described_class.as(bot_user, persona: DiscourseAi::Personas::General.new) } +RSpec.describe DiscourseAi::Agents::Bot do + subject(:bot) { described_class.as(bot_user, agent: DiscourseAi::Agents::General.new) } fab!(:admin) fab!(:gpt_4) { Fabricate(:llm_model, name: "gpt-4") } @@ -39,8 +39,8 @@ RSpec.describe DiscourseAi::Personas::Bot do Group.refresh_automatic_groups! bot_user = DiscourseAi::AiBot::EntryPoint.find_user_from_model(fake.name) - AiPersona.create!( - name: "TestPersona", + AiAgent.create!( + name: "TestAgent", top_p: 0.5, temperature: 0.4, system_prompt: "test", @@ -48,11 +48,11 @@ RSpec.describe DiscourseAi::Personas::Bot do allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], ) - personaClass = DiscourseAi::Personas::Persona.find_by(user: admin, name: "TestPersona") + agentClass = DiscourseAi::Agents::Agent.find_by(user: admin, name: "TestAgent") - bot = described_class.as(bot_user, persona: personaClass.new) + bot = described_class.as(bot_user, agent: agentClass.new) bot.reply( - DiscourseAi::Personas::BotContext.new(messages: [{ type: :user, content: "test" }]), + DiscourseAi::Agents::BotContext.new(messages: [{ type: :user, content: "test" }]), ) do |_partial, _cancel, _placeholder| # we just need the block so bot has something to call with results end @@ -64,7 +64,7 @@ RSpec.describe DiscourseAi::Personas::Bot do context "when using function chaining" do it "yields a loading placeholder while proceeds to invoke the command" do - tool = DiscourseAi::Personas::Tools::ListCategories.new({}, bot_user: nil, llm: nil) + tool = DiscourseAi::Agents::Tools::ListCategories.new({}, bot_user: nil, llm: nil) partial_placeholder = +(<<~HTML) #{tool.summary}
@@ -75,7 +75,7 @@ RSpec.describe DiscourseAi::Personas::Bot do HTML context = - DiscourseAi::Personas::BotContext.new( + DiscourseAi::Agents::BotContext.new( messages: [{ type: :user, content: "Does my site has tags?" }], ) diff --git a/spec/lib/personas/persona_spec.rb b/spec/lib/agents/persona_spec.rb similarity index 63% rename from spec/lib/personas/persona_spec.rb rename to spec/lib/agents/persona_spec.rb index d3e90568..090bc66b 100644 --- a/spec/lib/personas/persona_spec.rb +++ b/spec/lib/agents/persona_spec.rb @@ -1,11 +1,11 @@ #frozen_string_literal: true -class TestPersona < DiscourseAi::Personas::Persona +class TestAgent < DiscourseAi::Agents::Agent def tools [ - DiscourseAi::Personas::Tools::ListTags, - DiscourseAi::Personas::Tools::Search, - DiscourseAi::Personas::Tools::Image, + DiscourseAi::Agents::Tools::ListTags, + DiscourseAi::Agents::Tools::Search, + DiscourseAi::Agents::Tools::Image, ] end @@ -21,9 +21,9 @@ class TestPersona < DiscourseAi::Personas::Persona end end -RSpec.describe DiscourseAi::Personas::Persona do - let :persona do - TestPersona.new +RSpec.describe DiscourseAi::Agents::Agent do + let :agent do + TestAgent.new end let :topic_with_users do @@ -34,13 +34,13 @@ RSpec.describe DiscourseAi::Personas::Persona do after do # we are rolling back transactions so we can create poison cache - AiPersona.persona_cache.flush! + AiAgent.agent_cache.flush! end let(:resource_url) { "https://path-to-resource" } let(:context) do - DiscourseAi::Personas::BotContext.new( + DiscourseAi::Agents::BotContext.new( site_url: Discourse.base_url, site_title: "test site title", site_description: "test site description", @@ -57,7 +57,7 @@ RSpec.describe DiscourseAi::Personas::Persona do it "renders the system prompt" do freeze_time - rendered = persona.craft_prompt(context) + rendered = agent.craft_prompt(context) system_message = rendered.messages.first[:content] expect(system_message).to include(Discourse.base_url) @@ -90,7 +90,7 @@ RSpec.describe DiscourseAi::Personas::Persona do ) tool_instance = - DiscourseAi::Personas::Artist.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) + DiscourseAi::Agents::Artist.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) expect(tool_instance.parameters[:prompts]).to eq(["cat oil painting", "big car"]) expect(tool_instance.parameters[:aspect_ratio]).to eq("16:9") @@ -109,7 +109,7 @@ RSpec.describe DiscourseAi::Personas::Persona do ) tool_instance = - DiscourseAi::Personas::General.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) + DiscourseAi::Agents::General.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) expect(tool_instance.parameters.key?(:status)).to eq(false) @@ -125,7 +125,7 @@ RSpec.describe DiscourseAi::Personas::Persona do ) tool_instance = - DiscourseAi::Personas::General.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) + DiscourseAi::Agents::General.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) expect(tool_instance.parameters[:status]).to eq("open") end @@ -143,7 +143,7 @@ RSpec.describe DiscourseAi::Personas::Persona do ) search = - DiscourseAi::Personas::General.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) + DiscourseAi::Agents::General.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) expect(search.parameters[:max_posts]).to eq(3) expect(search.parameters[:search_query]).to eq("hello world") @@ -163,17 +163,17 @@ RSpec.describe DiscourseAi::Personas::Persona do ) tool_instance = - DiscourseAi::Personas::DallE3.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) + DiscourseAi::Agents::DallE3.new.find_tool(tool_call, bot_user: nil, llm: nil, context: nil) expect(tool_instance.parameters[:prompts]).to eq(["cat oil painting", "big car"]) end - describe "custom personas" do - it "is able to find custom personas" do + describe "custom agents" do + it "is able to find custom agents" do Group.refresh_automatic_groups! - # define an ai persona everyone can see - persona = - AiPersona.create!( + # define an ai agent everyone can see + agent = + AiAgent.create!( name: "zzzpun_bot", description: "you write puns", system_prompt: "you are pun bot", @@ -181,35 +181,35 @@ RSpec.describe DiscourseAi::Personas::Persona do allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], ) - custom_persona = DiscourseAi::Personas::Persona.all(user: user).last - expect(custom_persona.name).to eq("zzzpun_bot") - expect(custom_persona.description).to eq("you write puns") + custom_agent = DiscourseAi::Agents::Agent.all(user: user).last + expect(custom_agent.name).to eq("zzzpun_bot") + expect(custom_agent.description).to eq("you write puns") - instance = custom_persona.new - expect(instance.tools).to eq([DiscourseAi::Personas::Tools::Image]) + instance = custom_agent.new + expect(instance.tools).to eq([DiscourseAi::Agents::Tools::Image]) expect(instance.craft_prompt(context).messages.first[:content]).to eq("you are pun bot") # should update - persona.update!(name: "zzzpun_bot2") - custom_persona = DiscourseAi::Personas::Persona.all(user: user).last - expect(custom_persona.name).to eq("zzzpun_bot2") + agent.update!(name: "zzzpun_bot2") + custom_agent = DiscourseAi::Agents::Agent.all(user: user).last + expect(custom_agent.name).to eq("zzzpun_bot2") # can be disabled - persona.update!(enabled: false) - last_persona = DiscourseAi::Personas::Persona.all(user: user).last - expect(last_persona.name).not_to eq("zzzpun_bot2") + agent.update!(enabled: false) + last_agent = DiscourseAi::Agents::Agent.all(user: user).last + expect(last_agent.name).not_to eq("zzzpun_bot2") - persona.update!(enabled: true) + agent.update!(enabled: true) # no groups have access - persona.update!(allowed_group_ids: []) + agent.update!(allowed_group_ids: []) - last_persona = DiscourseAi::Personas::Persona.all(user: user).last - expect(last_persona.name).not_to eq("zzzpun_bot2") + last_agent = DiscourseAi::Agents::Agent.all(user: user).last + expect(last_agent.name).not_to eq("zzzpun_bot2") end end - describe "available personas" do - it "includes all personas by default" do + describe "available agents" do + it "includes all agents by default" do Group.refresh_automatic_groups! # must be enabled to see it @@ -218,54 +218,54 @@ RSpec.describe DiscourseAi::Personas::Persona do SiteSetting.ai_google_custom_search_cx = "abc123" # should be ordered by priority and then alpha - expect(DiscourseAi::Personas::Persona.all(user: user).map(&:superclass)).to contain_exactly( - DiscourseAi::Personas::General, - DiscourseAi::Personas::Artist, - DiscourseAi::Personas::Creative, - DiscourseAi::Personas::DiscourseHelper, - DiscourseAi::Personas::GithubHelper, - DiscourseAi::Personas::Researcher, - DiscourseAi::Personas::SettingsExplorer, - DiscourseAi::Personas::SqlHelper, + expect(DiscourseAi::Agents::Agent.all(user: user).map(&:superclass)).to contain_exactly( + DiscourseAi::Agents::General, + DiscourseAi::Agents::Artist, + DiscourseAi::Agents::Creative, + DiscourseAi::Agents::DiscourseHelper, + DiscourseAi::Agents::GithubHelper, + DiscourseAi::Agents::Researcher, + DiscourseAi::Agents::SettingsExplorer, + DiscourseAi::Agents::SqlHelper, ) # it should allow staff access to WebArtifactCreator - expect(DiscourseAi::Personas::Persona.all(user: admin).map(&:superclass)).to contain_exactly( - DiscourseAi::Personas::General, - DiscourseAi::Personas::Artist, - DiscourseAi::Personas::Creative, - DiscourseAi::Personas::DiscourseHelper, - DiscourseAi::Personas::GithubHelper, - DiscourseAi::Personas::Researcher, - DiscourseAi::Personas::SettingsExplorer, - DiscourseAi::Personas::SqlHelper, - DiscourseAi::Personas::WebArtifactCreator, + expect(DiscourseAi::Agents::Agent.all(user: admin).map(&:superclass)).to contain_exactly( + DiscourseAi::Agents::General, + DiscourseAi::Agents::Artist, + DiscourseAi::Agents::Creative, + DiscourseAi::Agents::DiscourseHelper, + DiscourseAi::Agents::GithubHelper, + DiscourseAi::Agents::Researcher, + DiscourseAi::Agents::SettingsExplorer, + DiscourseAi::Agents::SqlHelper, + DiscourseAi::Agents::WebArtifactCreator, ) - # omits personas if key is missing + # omits agents if key is missing SiteSetting.ai_stability_api_key = "" SiteSetting.ai_google_custom_search_api_key = "" SiteSetting.ai_artifact_security = "disabled" - expect(DiscourseAi::Personas::Persona.all(user: admin).map(&:superclass)).to contain_exactly( - DiscourseAi::Personas::General, - DiscourseAi::Personas::SqlHelper, - DiscourseAi::Personas::SettingsExplorer, - DiscourseAi::Personas::Creative, - DiscourseAi::Personas::DiscourseHelper, - DiscourseAi::Personas::GithubHelper, + expect(DiscourseAi::Agents::Agent.all(user: admin).map(&:superclass)).to contain_exactly( + DiscourseAi::Agents::General, + DiscourseAi::Agents::SqlHelper, + DiscourseAi::Agents::SettingsExplorer, + DiscourseAi::Agents::Creative, + DiscourseAi::Agents::DiscourseHelper, + DiscourseAi::Agents::GithubHelper, ) - AiPersona.find( - DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::General], + AiAgent.find( + DiscourseAi::Agents::Agent.system_agents[DiscourseAi::Agents::General], ).update!(enabled: false) - expect(DiscourseAi::Personas::Persona.all(user: user).map(&:superclass)).to contain_exactly( - DiscourseAi::Personas::SqlHelper, - DiscourseAi::Personas::SettingsExplorer, - DiscourseAi::Personas::Creative, - DiscourseAi::Personas::DiscourseHelper, - DiscourseAi::Personas::GithubHelper, + expect(DiscourseAi::Agents::Agent.all(user: user).map(&:superclass)).to contain_exactly( + DiscourseAi::Agents::SqlHelper, + DiscourseAi::Agents::SettingsExplorer, + DiscourseAi::Agents::Creative, + DiscourseAi::Agents::DiscourseHelper, + DiscourseAi::Agents::GithubHelper, ) end end @@ -279,19 +279,19 @@ RSpec.describe DiscourseAi::Personas::Persona do SiteSetting.ai_embeddings_enabled = true end - let(:ai_persona) { DiscourseAi::Personas::Persona.all(user: user).first.new } + let(:ai_agent) { DiscourseAi::Agents::Agent.all(user: user).first.new } let(:with_cc) do context.messages = [{ content: "Tell me the time", type: :user }] context end - context "when a persona has no uploads" do + context "when a agent has no uploads" do it "doesn't include RAG guidance" do guidance_fragment = "The following texts will give you additional guidance to elaborate a response." - expect(ai_persona.craft_prompt(with_cc).messages.first[:content]).not_to include( + expect(ai_agent.craft_prompt(with_cc).messages.first[:content]).not_to include( guidance_fragment, ) end @@ -306,19 +306,19 @@ RSpec.describe DiscourseAi::Personas::Persona do context_embedding = vector_def.dimensions.times.map { rand(-1.0...1.0) } EmbeddingsGenerationStubs.hugging_face_service(consolidated_question, context_embedding) - custom_ai_persona = + custom_ai_agent = Fabricate( - :ai_persona, + :ai_agent, name: "custom", rag_conversation_chunks: 3, allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], question_consolidator_llm_id: llm_model.id, ) - UploadReference.ensure_exist!(target: custom_ai_persona, upload_ids: [upload.id]) + UploadReference.ensure_exist!(target: custom_ai_agent, upload_ids: [upload.id]) - custom_persona = - DiscourseAi::Personas::Persona.find_by(id: custom_ai_persona.id, user: user).new + custom_agent = + DiscourseAi::Agents::Agent.find_by(id: custom_ai_agent.id, user: user).new # this means that we will consolidate context.messages = [ @@ -328,7 +328,7 @@ RSpec.describe DiscourseAi::Personas::Persona do ] DiscourseAi::Completions::Endpoints::Fake.with_fake_content(consolidated_question) do - custom_persona.craft_prompt(context).messages.first[:content] + custom_agent.craft_prompt(context).messages.first[:content] end message = @@ -341,11 +341,11 @@ RSpec.describe DiscourseAi::Personas::Persona do end end - context "when a persona has RAG uploads" do + context "when a agent has RAG uploads" do let(:embedding_value) { 0.04381 } let(:prompt_cc_embeddings) { [embedding_value] * vector_def.dimensions } - def stub_fragments(fragment_count, persona: ai_persona) + def stub_fragments(fragment_count, agent: ai_agent) schema = DiscourseAi::Embeddings::Schema.for(RagDocumentFragment) fragment_count.times do |i| @@ -353,8 +353,8 @@ RSpec.describe DiscourseAi::Personas::Persona do Fabricate( :rag_document_fragment, fragment: "fragment-n#{i}", - target_id: persona.id, - target_type: "AiPersona", + target_id: agent.id, + target_type: "AiAgent", upload: upload, ) @@ -366,8 +366,8 @@ RSpec.describe DiscourseAi::Personas::Persona do end before do - stored_ai_persona = AiPersona.find(ai_persona.id) - UploadReference.ensure_exist!(target: stored_ai_persona, upload_ids: [upload.id]) + stored_ai_agent = AiAgent.find(ai_agent.id) + UploadReference.ensure_exist!(target: stored_ai_agent, upload_ids: [upload.id]) EmbeddingsGenerationStubs.hugging_face_service( with_cc.messages.dig(0, :content), @@ -375,26 +375,26 @@ RSpec.describe DiscourseAi::Personas::Persona do ) end - context "when persona allows for less fragments" do + context "when agent allows for less fragments" do it "will only pick 3 fragments" do - custom_ai_persona = + custom_ai_agent = Fabricate( - :ai_persona, + :ai_agent, name: "custom", rag_conversation_chunks: 3, allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], ) - stub_fragments(3, persona: custom_ai_persona) + stub_fragments(3, agent: custom_ai_agent) - UploadReference.ensure_exist!(target: custom_ai_persona, upload_ids: [upload.id]) + UploadReference.ensure_exist!(target: custom_ai_agent, upload_ids: [upload.id]) - custom_persona = - DiscourseAi::Personas::Persona.find_by(id: custom_ai_persona.id, user: user).new + custom_agent = + DiscourseAi::Agents::Agent.find_by(id: custom_ai_agent.id, user: user).new - expect(custom_persona.class.rag_conversation_chunks).to eq(3) + expect(custom_agent.class.rag_conversation_chunks).to eq(3) - crafted_system_prompt = custom_persona.craft_prompt(with_cc).messages.first[:content] + crafted_system_prompt = custom_agent.craft_prompt(with_cc).messages.first[:content] expect(crafted_system_prompt).to include("fragment-n0") expect(crafted_system_prompt).to include("fragment-n1") @@ -420,7 +420,7 @@ RSpec.describe DiscourseAi::Personas::Persona do body: JSON.dump(expected_reranked), ) - crafted_system_prompt = ai_persona.craft_prompt(with_cc).messages.first[:content] + crafted_system_prompt = ai_agent.craft_prompt(with_cc).messages.first[:content] expect(crafted_system_prompt).to include("fragment-n14") expect(crafted_system_prompt).to include("fragment-n13") @@ -433,7 +433,7 @@ RSpec.describe DiscourseAi::Personas::Persona do before { stub_fragments(10) } it "picks the first 10 candidates from the similarity search" do - crafted_system_prompt = ai_persona.craft_prompt(with_cc).messages.first[:content] + crafted_system_prompt = ai_agent.craft_prompt(with_cc).messages.first[:content] expect(crafted_system_prompt).to include("fragment-n0") expect(crafted_system_prompt).to include("fragment-n1") @@ -443,20 +443,20 @@ RSpec.describe DiscourseAi::Personas::Persona do end end - context "when the persona has examples" do - fab!(:examples_persona) do + context "when the agent has examples" do + fab!(:examples_agent) do Fabricate( - :ai_persona, + :ai_agent, examples: [["User message", "assistant response"]], allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], ) end it "includes them before the context messages" do - custom_persona = - DiscourseAi::Personas::Persona.find_by(id: examples_persona.id, user: user).new + custom_agent = + DiscourseAi::Agents::Agent.find_by(id: examples_agent.id, user: user).new - post_system_prompt_msgs = custom_persona.craft_prompt(with_cc).messages.last(3) + post_system_prompt_msgs = custom_agent.craft_prompt(with_cc).messages.last(3) expect(post_system_prompt_msgs).to contain_exactly( { content: "User message", type: :user }, diff --git a/spec/lib/personas/question_consolidator_spec.rb b/spec/lib/agents/question_consolidator_spec.rb similarity index 94% rename from spec/lib/personas/question_consolidator_spec.rb rename to spec/lib/agents/question_consolidator_spec.rb index 7fe54399..08af4a03 100644 --- a/spec/lib/personas/question_consolidator_spec.rb +++ b/spec/lib/agents/question_consolidator_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::QuestionConsolidator do +RSpec.describe DiscourseAi::Agents::QuestionConsolidator do let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{Fabricate(:fake_model).id}") } let(:fake_endpoint) { DiscourseAi::Completions::Endpoints::Fake } diff --git a/spec/lib/personas/researcher_spec.rb b/spec/lib/agents/researcher_spec.rb similarity index 51% rename from spec/lib/personas/researcher_spec.rb rename to spec/lib/agents/researcher_spec.rb index d216d6c0..56450d9e 100644 --- a/spec/lib/personas/researcher_spec.rb +++ b/spec/lib/agents/researcher_spec.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Researcher do +RSpec.describe DiscourseAi::Agents::Researcher do let :researcher do subject end it "renders schema" do expect(researcher.tools).to eq( - [DiscourseAi::Personas::Tools::Google, DiscourseAi::Personas::Tools::WebBrowser], + [DiscourseAi::Agents::Tools::Google, DiscourseAi::Agents::Tools::WebBrowser], ) end end diff --git a/spec/lib/personas/settings_explorer_spec.rb b/spec/lib/agents/settings_explorer_spec.rb similarity index 58% rename from spec/lib/personas/settings_explorer_spec.rb rename to spec/lib/agents/settings_explorer_spec.rb index 24f15224..6204189e 100644 --- a/spec/lib/personas/settings_explorer_spec.rb +++ b/spec/lib/agents/settings_explorer_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::SettingsExplorer do +RSpec.describe DiscourseAi::Agents::SettingsExplorer do let :settings_explorer do subject end @@ -9,12 +9,12 @@ RSpec.describe DiscourseAi::Personas::SettingsExplorer do prompt = settings_explorer.system_prompt # check we do not render plugin settings - expect(prompt).not_to include("ai_bot_enabled_personas") + expect(prompt).not_to include("ai_bot_enabled_agents") expect(prompt).to include("site_description") expect(settings_explorer.tools).to eq( - [DiscourseAi::Personas::Tools::SettingContext, DiscourseAi::Personas::Tools::SearchSettings], + [DiscourseAi::Agents::Tools::SettingContext, DiscourseAi::Agents::Tools::SearchSettings], ) end end diff --git a/spec/lib/personas/sql_helper_spec.rb b/spec/lib/agents/sql_helper_spec.rb similarity index 74% rename from spec/lib/personas/sql_helper_spec.rb rename to spec/lib/agents/sql_helper_spec.rb index d2f1614e..c1a0385f 100644 --- a/spec/lib/personas/sql_helper_spec.rb +++ b/spec/lib/agents/sql_helper_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::SqlHelper do +RSpec.describe DiscourseAi::Agents::SqlHelper do let :sql_helper do subject end @@ -12,6 +12,6 @@ RSpec.describe DiscourseAi::Personas::SqlHelper do expect(prompt).not_to include("translation_key") # not a priority table expect(prompt).to include("user_api_keys") # not a priority table - expect(sql_helper.tools).to eq([DiscourseAi::Personas::Tools::DbSchema]) + expect(sql_helper.tools).to eq([DiscourseAi::Agents::Tools::DbSchema]) end end diff --git a/spec/lib/personas/tools/create_artifact_spec.rb b/spec/lib/agents/tools/create_artifact_spec.rb similarity index 92% rename from spec/lib/personas/tools/create_artifact_spec.rb rename to spec/lib/agents/tools/create_artifact_spec.rb index 929e54ef..d5224d98 100644 --- a/spec/lib/personas/tools/create_artifact_spec.rb +++ b/spec/lib/agents/tools/create_artifact_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::CreateArtifact do +RSpec.describe DiscourseAi::Agents::Tools::CreateArtifact do fab!(:llm_model) let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } fab!(:post) @@ -34,7 +34,7 @@ RSpec.describe DiscourseAi::Personas::Tools::CreateArtifact do { html_body: "hello" }, bot_user: Fabricate(:user), llm: llm, - context: DiscourseAi::Personas::BotContext.new(post: post), + context: DiscourseAi::Agents::BotContext.new(post: post), ) tool.parameters = { name: "hello", specification: "hello spec" } diff --git a/spec/lib/personas/tools/create_image_spec.rb b/spec/lib/agents/tools/create_image_spec.rb similarity index 98% rename from spec/lib/personas/tools/create_image_spec.rb rename to spec/lib/agents/tools/create_image_spec.rb index 0aa18fea..6fda43e5 100644 --- a/spec/lib/personas/tools/create_image_spec.rb +++ b/spec/lib/agents/tools/create_image_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::CreateImage do +RSpec.describe DiscourseAi::Agents::Tools::CreateImage do let(:prompts) { ["a watercolor painting", "an abstract design"] } fab!(:gpt_35_turbo) { Fabricate(:llm_model, name: "gpt-3.5-turbo") } diff --git a/spec/lib/personas/tools/dall_e_spec.rb b/spec/lib/agents/tools/dall_e_spec.rb similarity index 98% rename from spec/lib/personas/tools/dall_e_spec.rb rename to spec/lib/agents/tools/dall_e_spec.rb index 50d4ab72..52628755 100644 --- a/spec/lib/personas/tools/dall_e_spec.rb +++ b/spec/lib/agents/tools/dall_e_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::DallE do +RSpec.describe DiscourseAi::Agents::Tools::DallE do let(:prompts) { ["a pink cow", "a red cow"] } fab!(:gpt_35_turbo) { Fabricate(:llm_model, name: "gpt-3.5-turbo") } diff --git a/spec/lib/personas/tools/db_schema_spec.rb b/spec/lib/agents/tools/db_schema_spec.rb similarity index 92% rename from spec/lib/personas/tools/db_schema_spec.rb rename to spec/lib/agents/tools/db_schema_spec.rb index 643e3fe7..7e6bb131 100644 --- a/spec/lib/personas/tools/db_schema_spec.rb +++ b/spec/lib/agents/tools/db_schema_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::DbSchema do +RSpec.describe DiscourseAi::Agents::Tools::DbSchema do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/discourse_meta_search_spec.rb b/spec/lib/agents/tools/discourse_meta_search_spec.rb similarity index 97% rename from spec/lib/personas/tools/discourse_meta_search_spec.rb rename to spec/lib/agents/tools/discourse_meta_search_spec.rb index 1ccc4d4d..be66646a 100644 --- a/spec/lib/personas/tools/discourse_meta_search_spec.rb +++ b/spec/lib/agents/tools/discourse_meta_search_spec.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::DiscourseMetaSearch do +RSpec.describe DiscourseAi::Agents::Tools::DiscourseMetaSearch do before { SiteSetting.ai_bot_enabled = true } fab!(:llm_model) { Fabricate(:llm_model, max_prompt_tokens: 8192) } diff --git a/spec/lib/personas/tools/edit_image_spec.rb b/spec/lib/agents/tools/edit_image_spec.rb similarity index 98% rename from spec/lib/personas/tools/edit_image_spec.rb rename to spec/lib/agents/tools/edit_image_spec.rb index 4242aec4..a18aedcb 100644 --- a/spec/lib/personas/tools/edit_image_spec.rb +++ b/spec/lib/agents/tools/edit_image_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::EditImage do +RSpec.describe DiscourseAi::Agents::Tools::EditImage do fab!(:gpt_35_turbo) { Fabricate(:llm_model, name: "gpt-3.5-turbo") } before do diff --git a/spec/lib/personas/tools/github_file_content_spec.rb b/spec/lib/agents/tools/github_file_content_spec.rb similarity index 97% rename from spec/lib/personas/tools/github_file_content_spec.rb rename to spec/lib/agents/tools/github_file_content_spec.rb index 4186dd01..f0693455 100644 --- a/spec/lib/personas/tools/github_file_content_spec.rb +++ b/spec/lib/agents/tools/github_file_content_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe DiscourseAi::Personas::Tools::GithubFileContent do +RSpec.describe DiscourseAi::Agents::Tools::GithubFileContent do fab!(:llm_model) let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/github_pull_request_diff_spec.rb b/spec/lib/agents/tools/github_pull_request_diff_spec.rb similarity index 98% rename from spec/lib/personas/tools/github_pull_request_diff_spec.rb rename to spec/lib/agents/tools/github_pull_request_diff_spec.rb index e8b3d226..cf6826ce 100644 --- a/spec/lib/personas/tools/github_pull_request_diff_spec.rb +++ b/spec/lib/agents/tools/github_pull_request_diff_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe DiscourseAi::Personas::Tools::GithubPullRequestDiff do +RSpec.describe DiscourseAi::Agents::Tools::GithubPullRequestDiff do let(:bot_user) { Fabricate(:user) } fab!(:llm_model) let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/github_search_code_spec.rb b/spec/lib/agents/tools/github_search_code_spec.rb similarity index 97% rename from spec/lib/personas/tools/github_search_code_spec.rb rename to spec/lib/agents/tools/github_search_code_spec.rb index b8fbca27..14b401a2 100644 --- a/spec/lib/personas/tools/github_search_code_spec.rb +++ b/spec/lib/agents/tools/github_search_code_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe DiscourseAi::Personas::Tools::GithubSearchCode do +RSpec.describe DiscourseAi::Agents::Tools::GithubSearchCode do let(:bot_user) { Fabricate(:user) } fab!(:llm_model) let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/github_search_files_spec.rb b/spec/lib/agents/tools/github_search_files_spec.rb similarity index 97% rename from spec/lib/personas/tools/github_search_files_spec.rb rename to spec/lib/agents/tools/github_search_files_spec.rb index cc6926fd..06def4ba 100644 --- a/spec/lib/personas/tools/github_search_files_spec.rb +++ b/spec/lib/agents/tools/github_search_files_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe DiscourseAi::Personas::Tools::GithubSearchFiles do +RSpec.describe DiscourseAi::Agents::Tools::GithubSearchFiles do fab!(:llm_model) let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/google_spec.rb b/spec/lib/agents/tools/google_spec.rb similarity index 97% rename from spec/lib/personas/tools/google_spec.rb rename to spec/lib/agents/tools/google_spec.rb index 5062cea9..bf87bb78 100644 --- a/spec/lib/personas/tools/google_spec.rb +++ b/spec/lib/agents/tools/google_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::Google do +RSpec.describe DiscourseAi::Agents::Tools::Google do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } @@ -36,7 +36,7 @@ RSpec.describe DiscourseAi::Personas::Tools::Google do { query: "some search term" }, bot_user: bot_user, llm: llm, - persona_options: { + agent_options: { "base_query" => base_query, }, ) diff --git a/spec/lib/personas/tools/image_spec.rb b/spec/lib/agents/tools/image_spec.rb similarity index 94% rename from spec/lib/personas/tools/image_spec.rb rename to spec/lib/agents/tools/image_spec.rb index 342c9f67..278c4c33 100644 --- a/spec/lib/personas/tools/image_spec.rb +++ b/spec/lib/agents/tools/image_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::Image do +RSpec.describe DiscourseAi::Agents::Tools::Image do let(:progress_blk) { Proc.new {} } let(:prompts) { ["a pink cow", "a red cow"] } @@ -9,7 +9,7 @@ RSpec.describe DiscourseAi::Personas::Tools::Image do { prompts: prompts, seeds: [99, 32] }, bot_user: bot_user, llm: llm, - context: DiscourseAi::Personas::BotContext.new, + context: DiscourseAi::Agents::BotContext.new, ) end diff --git a/spec/lib/personas/tools/javascript_evaluator_spec.rb b/spec/lib/agents/tools/javascript_evaluator_spec.rb similarity index 97% rename from spec/lib/personas/tools/javascript_evaluator_spec.rb rename to spec/lib/agents/tools/javascript_evaluator_spec.rb index cae05ee9..350b601c 100644 --- a/spec/lib/personas/tools/javascript_evaluator_spec.rb +++ b/spec/lib/agents/tools/javascript_evaluator_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::JavascriptEvaluator do +RSpec.describe DiscourseAi::Agents::Tools::JavascriptEvaluator do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/list_categories_spec.rb b/spec/lib/agents/tools/list_categories_spec.rb similarity index 90% rename from spec/lib/personas/tools/list_categories_spec.rb rename to spec/lib/agents/tools/list_categories_spec.rb index bcda2123..e96029bb 100644 --- a/spec/lib/personas/tools/list_categories_spec.rb +++ b/spec/lib/agents/tools/list_categories_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::ListCategories do +RSpec.describe DiscourseAi::Agents::Tools::ListCategories do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/list_tags_spec.rb b/spec/lib/agents/tools/list_tags_spec.rb similarity index 92% rename from spec/lib/personas/tools/list_tags_spec.rb rename to spec/lib/agents/tools/list_tags_spec.rb index b8f4ed5c..b20d3f1e 100644 --- a/spec/lib/personas/tools/list_tags_spec.rb +++ b/spec/lib/agents/tools/list_tags_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::ListTags do +RSpec.describe DiscourseAi::Agents::Tools::ListTags do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/random_picker_spec.rb b/spec/lib/agents/tools/random_picker_spec.rb similarity index 96% rename from spec/lib/personas/tools/random_picker_spec.rb rename to spec/lib/agents/tools/random_picker_spec.rb index 65f7c7f2..e55fb7af 100644 --- a/spec/lib/personas/tools/random_picker_spec.rb +++ b/spec/lib/agents/tools/random_picker_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe DiscourseAi::Personas::Tools::RandomPicker do +RSpec.describe DiscourseAi::Agents::Tools::RandomPicker do describe "#invoke" do subject { described_class.new({ options: options }, bot_user: nil, llm: nil).invoke } diff --git a/spec/lib/personas/tools/read_artifact_spec.rb b/spec/lib/agents/tools/read_artifact_spec.rb similarity index 90% rename from spec/lib/personas/tools/read_artifact_spec.rb rename to spec/lib/agents/tools/read_artifact_spec.rb index 279dd18e..26e201fc 100644 --- a/spec/lib/personas/tools/read_artifact_spec.rb +++ b/spec/lib/agents/tools/read_artifact_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::ReadArtifact do +RSpec.describe DiscourseAi::Agents::Tools::ReadArtifact do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } fab!(:post) @@ -25,7 +25,7 @@ RSpec.describe DiscourseAi::Personas::Tools::ReadArtifact do { url: "#{Discourse.base_url}/discourse-ai/ai-bot/artifacts/#{artifact.id}" }, bot_user: bot_user, llm: llm_model.to_llm, - context: DiscourseAi::Personas::BotContext.new(post: post), + context: DiscourseAi::Agents::BotContext.new(post: post), ) result = tool.invoke {} @@ -44,7 +44,7 @@ RSpec.describe DiscourseAi::Personas::Tools::ReadArtifact do { url: "invalid-url" }, bot_user: bot_user, llm: llm_model.to_llm, - context: DiscourseAi::Personas::BotContext.new(post: post), + context: DiscourseAi::Agents::BotContext.new(post: post), ) result = tool.invoke {} @@ -58,7 +58,7 @@ RSpec.describe DiscourseAi::Personas::Tools::ReadArtifact do { url: "#{Discourse.base_url}/discourse-ai/ai-bot/artifacts/99999" }, bot_user: bot_user, llm: llm_model.to_llm, - context: DiscourseAi::Personas::BotContext.new(post: post), + context: DiscourseAi::Agents::BotContext.new(post: post), ) result = tool.invoke {} @@ -91,7 +91,7 @@ RSpec.describe DiscourseAi::Personas::Tools::ReadArtifact do { url: "https://example.com" }, bot_user: bot_user, llm: llm_model.to_llm, - context: DiscourseAi::Personas::BotContext.new(post: post), + context: DiscourseAi::Agents::BotContext.new(post: post), ) result = tool.invoke {} @@ -120,7 +120,7 @@ RSpec.describe DiscourseAi::Personas::Tools::ReadArtifact do { url: "https://example.com" }, bot_user: bot_user, llm: llm_model.to_llm, - context: DiscourseAi::Personas::BotContext.new(post: post), + context: DiscourseAi::Agents::BotContext.new(post: post), ) result = tool.invoke {} diff --git a/spec/lib/personas/tools/read_spec.rb b/spec/lib/agents/tools/read_spec.rb similarity index 93% rename from spec/lib/personas/tools/read_spec.rb rename to spec/lib/agents/tools/read_spec.rb index 2affc1f4..4f4986d8 100644 --- a/spec/lib/personas/tools/read_spec.rb +++ b/spec/lib/agents/tools/read_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::Read do +RSpec.describe DiscourseAi::Agents::Tools::Read do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } @@ -53,10 +53,10 @@ RSpec.describe DiscourseAi::Personas::Tools::Read do { topic_id: topic_with_tags.id, post_numbers: [post1.post_number] }, bot_user: bot_user, llm: llm, - persona_options: { + agent_options: { "read_private" => true, }, - context: DiscourseAi::Personas::BotContext.new(user: admin), + context: DiscourseAi::Agents::BotContext.new(user: admin), ) results = tool.invoke expect(results[:content]).to include("hello there") @@ -66,7 +66,7 @@ RSpec.describe DiscourseAi::Personas::Tools::Read do { topic_id: topic_with_tags.id, post_numbers: [post1.post_number] }, bot_user: bot_user, llm: llm, - context: DiscourseAi::Personas::BotContext.new(user: admin), + context: DiscourseAi::Agents::BotContext.new(user: admin), ) results = tool.invoke diff --git a/spec/lib/personas/tools/researcher_spec.rb b/spec/lib/agents/tools/researcher_spec.rb similarity index 91% rename from spec/lib/personas/tools/researcher_spec.rb rename to spec/lib/agents/tools/researcher_spec.rb index 23ed98a7..07b10517 100644 --- a/spec/lib/personas/tools/researcher_spec.rb +++ b/spec/lib/agents/tools/researcher_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::Researcher do +RSpec.describe DiscourseAi::Agents::Tools::Researcher do before { SearchIndexer.enable } after { SearchIndexer.disable } @@ -28,7 +28,7 @@ RSpec.describe DiscourseAi::Personas::Tools::Researcher do { dry_run: true, filter: "topic:#{topic_with_tags.id}", goals: "analyze topic content" }, bot_user: bot_user, llm: llm, - context: DiscourseAi::Personas::BotContext.new(user: user, post: post), + context: DiscourseAi::Agents::BotContext.new(user: user, post: post), ) results = researcher.invoke(&progress_blk) expect(results[:number_of_posts]).to eq(1) @@ -40,7 +40,7 @@ RSpec.describe DiscourseAi::Personas::Tools::Researcher do { filter: "tag:research after:2023", goals: "analyze post patterns", dry_run: true }, bot_user: bot_user, llm: llm, - context: DiscourseAi::Personas::BotContext.new(user: user, post: post), + context: DiscourseAi::Agents::BotContext.new(user: user, post: post), ) results = researcher.invoke(&progress_blk) @@ -66,7 +66,7 @@ RSpec.describe DiscourseAi::Personas::Tools::Researcher do researcher = described_class.new( { filter: "category:research-category" }, - persona_options: { + agent_options: { "max_results" => "50", }, bot_user: bot_user, @@ -82,7 +82,7 @@ RSpec.describe DiscourseAi::Personas::Tools::Researcher do { filter: "invalidfilter tag:research", goals: "analyze content" }, bot_user: bot_user, llm: llm, - context: DiscourseAi::Personas::BotContext.new(user: user, post: post), + context: DiscourseAi::Agents::BotContext.new(user: user, post: post), ) results = researcher.invoke(&progress_blk) @@ -110,7 +110,7 @@ RSpec.describe DiscourseAi::Personas::Tools::Researcher do }, bot_user: bot_user, llm: llm, - context: DiscourseAi::Personas::BotContext.new(user: user, post: post), + context: DiscourseAi::Agents::BotContext.new(user: user, post: post), ) responses = 10.times.map { |i| ["Found: Relevant content #{i + 1}"] } diff --git a/spec/lib/personas/tools/search_settings_spec.rb b/spec/lib/agents/tools/search_settings_spec.rb similarity index 97% rename from spec/lib/personas/tools/search_settings_spec.rb rename to spec/lib/agents/tools/search_settings_spec.rb index f3cd4356..2c1b0f1f 100644 --- a/spec/lib/personas/tools/search_settings_spec.rb +++ b/spec/lib/agents/tools/search_settings_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::SearchSettings do +RSpec.describe DiscourseAi::Agents::Tools::SearchSettings do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/search_spec.rb b/spec/lib/agents/tools/search_spec.rb similarity index 96% rename from spec/lib/personas/tools/search_spec.rb rename to spec/lib/agents/tools/search_spec.rb index c28696f9..9788dc99 100644 --- a/spec/lib/personas/tools/search_spec.rb +++ b/spec/lib/agents/tools/search_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::Search do +RSpec.describe DiscourseAi::Agents::Tools::Search do before { SearchIndexer.enable } after { SearchIndexer.disable } @@ -40,8 +40,8 @@ RSpec.describe DiscourseAi::Personas::Tools::Search do before { SiteSetting.ai_bot_enabled = true } describe "#invoke" do - it "can retrieve options from persona correctly" do - persona_options = { + it "can retrieve options from agent correctly" do + agent_options = { "base_query" => "#funny", "search_private" => "true", "max_results" => "10", @@ -57,10 +57,10 @@ RSpec.describe DiscourseAi::Personas::Tools::Search do search = described_class.new( { order: "latest" }, - persona_options: persona_options, + agent_options: agent_options, bot_user: bot_user, llm: llm, - context: DiscourseAi::Personas::BotContext.new(user: user), + context: DiscourseAi::Agents::BotContext.new(user: user), ) expect(search.options[:base_query]).to eq("#funny") diff --git a/spec/lib/personas/tools/setting_context_spec.rb b/spec/lib/agents/tools/setting_context_spec.rb similarity index 95% rename from spec/lib/personas/tools/setting_context_spec.rb rename to spec/lib/agents/tools/setting_context_spec.rb index 20e26b64..4c54c41f 100644 --- a/spec/lib/personas/tools/setting_context_spec.rb +++ b/spec/lib/agents/tools/setting_context_spec.rb @@ -8,7 +8,7 @@ def has_rg? end end -RSpec.describe DiscourseAi::Personas::Tools::SettingContext, if: has_rg? do +RSpec.describe DiscourseAi::Agents::Tools::SettingContext, if: has_rg? do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } diff --git a/spec/lib/personas/tools/summarize_spec.rb b/spec/lib/agents/tools/summarize_spec.rb similarity index 96% rename from spec/lib/personas/tools/summarize_spec.rb rename to spec/lib/agents/tools/summarize_spec.rb index 2bda3cd3..4287ceb8 100644 --- a/spec/lib/personas/tools/summarize_spec.rb +++ b/spec/lib/agents/tools/summarize_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::Summarize do +RSpec.describe DiscourseAi::Agents::Tools::Summarize do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/time_spec.rb b/spec/lib/agents/tools/time_spec.rb similarity index 92% rename from spec/lib/personas/tools/time_spec.rb rename to spec/lib/agents/tools/time_spec.rb index e92a32ad..b16ea593 100644 --- a/spec/lib/personas/tools/time_spec.rb +++ b/spec/lib/agents/tools/time_spec.rb @@ -1,6 +1,6 @@ #frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::Time do +RSpec.describe DiscourseAi::Agents::Tools::Time do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/personas/tools/tool_spec.rb b/spec/lib/agents/tools/tool_spec.rb similarity index 95% rename from spec/lib/personas/tools/tool_spec.rb rename to spec/lib/agents/tools/tool_spec.rb index 5896d8e3..ce446ff1 100644 --- a/spec/lib/personas/tools/tool_spec.rb +++ b/spec/lib/agents/tools/tool_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::Tool do +RSpec.describe DiscourseAi::Agents::Tools::Tool do let :tool_class do described_class end diff --git a/spec/lib/personas/tools/update_artifact_spec.rb b/spec/lib/agents/tools/update_artifact_spec.rb similarity index 90% rename from spec/lib/personas/tools/update_artifact_spec.rb rename to spec/lib/agents/tools/update_artifact_spec.rb index 3f8b7f06..c8b25379 100644 --- a/spec/lib/personas/tools/update_artifact_spec.rb +++ b/spec/lib/agents/tools/update_artifact_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do +RSpec.describe DiscourseAi::Agents::Tools::UpdateArtifact do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } fab!(:post) @@ -44,10 +44,10 @@ RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do }, bot_user: bot_user, llm: llm_model.to_llm, - persona_options: { + agent_options: { "update_algorithm" => "full", }, - context: DiscourseAi::Personas::BotContext.new(messages: [], post: post), + context: DiscourseAi::Agents::BotContext.new(messages: [], post: post), ) result = tool.invoke {} @@ -88,10 +88,10 @@ RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do { artifact_id: artifact.id, instructions: "Update only JavaScript" }, bot_user: bot_user, llm: llm_model.to_llm, - persona_options: { + agent_options: { "update_algorithm" => "full", }, - context: DiscourseAi::Personas::BotContext.new(messages: [], post: post), + context: DiscourseAi::Agents::BotContext.new(messages: [], post: post), ) result = tool.invoke {} @@ -115,7 +115,7 @@ RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do { artifact_id: artifact.id, instructions: "Invalid update" }, bot_user: bot_user, llm: llm_model.to_llm, - context: DiscourseAi::Personas::BotContext.new(messages: [], post: post), + context: DiscourseAi::Agents::BotContext.new(messages: [], post: post), ) result = tool.invoke {} @@ -129,7 +129,7 @@ RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do { artifact_id: -1, instructions: "Update something" }, bot_user: bot_user, llm: llm_model.to_llm, - context: DiscourseAi::Personas::BotContext.new(messages: [], post: post), + context: DiscourseAi::Agents::BotContext.new(messages: [], post: post), ) result = tool.invoke {} @@ -152,10 +152,10 @@ RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do { artifact_id: artifact.id, instructions: "Just update the HTML" }, bot_user: bot_user, llm: llm_model.to_llm, - persona_options: { + agent_options: { "update_algorithm" => "full", }, - context: DiscourseAi::Personas::BotContext.new(messages: [], post: post), + context: DiscourseAi::Agents::BotContext.new(messages: [], post: post), ) tool.invoke {} @@ -183,10 +183,10 @@ RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do { artifact_id: artifact.id, instructions: "Update to version 1" }, bot_user: bot_user, llm: llm_model.to_llm, - persona_options: { + agent_options: { "update_algorithm" => "full", }, - context: DiscourseAi::Personas::BotContext.new(messages: [], post: post), + context: DiscourseAi::Agents::BotContext.new(messages: [], post: post), ) .invoke {} end @@ -209,10 +209,10 @@ RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do }, bot_user: bot_user, llm: llm_model.to_llm, - persona_options: { + agent_options: { "update_algorithm" => "full", }, - context: DiscourseAi::Personas::BotContext.new(messages: [], post: post), + context: DiscourseAi::Agents::BotContext.new(messages: [], post: post), ) result = tool.invoke {} @@ -262,8 +262,8 @@ RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do { artifact_id: artifact.id, instructions: "Change the text to Updated and color to red" }, bot_user: bot_user, llm: llm_model.to_llm, - context: DiscourseAi::Personas::BotContext.new(messages: [], post: post), - persona_options: { + context: DiscourseAi::Agents::BotContext.new(messages: [], post: post), + agent_options: { "update_algorithm" => "diff", }, ) @@ -330,8 +330,8 @@ RSpec.describe DiscourseAi::Personas::Tools::UpdateArtifact do { artifact_id: artifact.id, instructions: "Change the text to Updated and color to red" }, bot_user: bot_user, llm: llm_model.to_llm, - context: DiscourseAi::Personas::BotContext.new(messages: [], post: post), - persona_options: { + context: DiscourseAi::Agents::BotContext.new(messages: [], post: post), + agent_options: { "update_algorithm" => "diff", }, ) diff --git a/spec/lib/personas/tools/web_browser_spec.rb b/spec/lib/agents/tools/web_browser_spec.rb similarity index 98% rename from spec/lib/personas/tools/web_browser_spec.rb rename to spec/lib/agents/tools/web_browser_spec.rb index aebd4e66..15d36bf7 100644 --- a/spec/lib/personas/tools/web_browser_spec.rb +++ b/spec/lib/agents/tools/web_browser_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Personas::Tools::WebBrowser do +RSpec.describe DiscourseAi::Agents::Tools::WebBrowser do fab!(:llm_model) let(:bot_user) { DiscourseAi::AiBot::EntryPoint.find_user_from_model(llm_model.name) } let(:llm) { DiscourseAi::Completions::Llm.proxy("custom:#{llm_model.id}") } diff --git a/spec/lib/discord/bot/agent_replier_spec.rb b/spec/lib/discord/bot/agent_replier_spec.rb new file mode 100644 index 00000000..792201bc --- /dev/null +++ b/spec/lib/discord/bot/agent_replier_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe DiscourseAi::Discord::Bot::AgentReplier do + let(:interaction_body) do + { data: { options: [{ value: "test query" }] }, token: "interaction_token" }.to_json.to_s + end + let(:agent_replier) { described_class.new(interaction_body) } + + fab!(:llm_model) + fab!(:agent) { Fabricate(:ai_agent, default_llm_id: llm_model.id) } + + before do + SiteSetting.ai_discord_search_agent = agent.id.to_s + allow_any_instance_of(DiscourseAi::Agents::Bot).to receive(:reply).and_return( + "This is a reply from bot!", + ) + allow(agent_replier).to receive(:create_reply) + end + + describe "#handle_interaction!" do + it "creates and updates replies" do + agent_replier.handle_interaction! + expect(agent_replier).to have_received(:create_reply).at_least(:once) + end + end +end diff --git a/spec/lib/discord/bot/persona_replier_spec.rb b/spec/lib/discord/bot/persona_replier_spec.rb deleted file mode 100644 index 9228e1bf..00000000 --- a/spec/lib/discord/bot/persona_replier_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -require "rails_helper" - -RSpec.describe DiscourseAi::Discord::Bot::PersonaReplier do - let(:interaction_body) do - { data: { options: [{ value: "test query" }] }, token: "interaction_token" }.to_json.to_s - end - let(:persona_replier) { described_class.new(interaction_body) } - - fab!(:llm_model) - fab!(:persona) { Fabricate(:ai_persona, default_llm_id: llm_model.id) } - - before do - SiteSetting.ai_discord_search_persona = persona.id.to_s - allow_any_instance_of(DiscourseAi::Personas::Bot).to receive(:reply).and_return( - "This is a reply from bot!", - ) - allow(persona_replier).to receive(:create_reply) - end - - describe "#handle_interaction!" do - it "creates and updates replies" do - persona_replier.handle_interaction! - expect(persona_replier).to have_received(:create_reply).at_least(:once) - end - end -end diff --git a/spec/lib/discord/bot/search_spec.rb b/spec/lib/discord/bot/search_spec.rb index 36233f60..e8bf7bfa 100644 --- a/spec/lib/discord/bot/search_spec.rb +++ b/spec/lib/discord/bot/search_spec.rb @@ -20,7 +20,7 @@ RSpec.describe DiscourseAi::Discord::Bot::Search do describe "#handle_interaction!" do it "creates a reply with search results" do - allow_any_instance_of(DiscourseAi::Personas::Tools::Search).to receive(:invoke).and_return( + allow_any_instance_of(DiscourseAi::Agents::Tools::Search).to receive(:invoke).and_return( { rows: [%w[Title /link]] }, ) search.handle_interaction! diff --git a/spec/lib/discourse_automation/llm_persona_triage_spec.rb b/spec/lib/discourse_automation/llm_agent_triage_spec.rb similarity index 92% rename from spec/lib/discourse_automation/llm_persona_triage_spec.rb rename to spec/lib/discourse_automation/llm_agent_triage_spec.rb index 8ea2123e..9a5095a0 100644 --- a/spec/lib/discourse_automation/llm_persona_triage_spec.rb +++ b/spec/lib/discourse_automation/llm_agent_triage_spec.rb @@ -2,29 +2,29 @@ return if !defined?(DiscourseAutomation) -describe DiscourseAi::Automation::LlmPersonaTriage do +describe DiscourseAi::Automation::LlmAgentTriage do fab!(:user) fab!(:bot_user) { Fabricate(:user) } fab!(:llm_model) { Fabricate(:anthropic_model, name: "claude-3-opus", enabled_chat_bot: true) } - fab!(:ai_persona) do - persona = + fab!(:ai_agent) do + agent = Fabricate( - :ai_persona, + :ai_agent, name: "Triage Helper", - description: "A persona that helps with triaging posts", + description: "A agent that helps with triaging posts", system_prompt: "You are a helpful assistant that triages posts", default_llm: llm_model, ) - # Create the user for this persona - persona.update!(user_id: bot_user.id) - persona + # Create the user for this agent + agent.update!(user_id: bot_user.id) + agent end let(:automation) do - Fabricate(:automation, name: "my automation", script: "llm_persona_triage", enabled: true) + Fabricate(:automation, name: "my automation", script: "llm_agent_triage", enabled: true) end def add_automation_field(name, value, type: "text") @@ -42,11 +42,11 @@ describe DiscourseAi::Automation::LlmPersonaTriage do SiteSetting.ai_bot_enabled = true SiteSetting.ai_bot_allowed_groups = "#{Group::AUTO_GROUPS[:trust_level_0]}" - add_automation_field("persona", ai_persona.id, type: "choices") + add_automation_field("agent", ai_agent.id, type: "choices") add_automation_field("whisper", false, type: "boolean") end - it "can respond to a post using the specified persona" do + it "can respond to a post using the specified agent" do post = Fabricate(:post, raw: "This is a test post that needs triage") response_text = "I analyzed your post and can help with that." @@ -89,7 +89,7 @@ describe DiscourseAi::Automation::LlmPersonaTriage do expect(topic.posts.count).to eq(2) - # Verify that the response was posted by the persona's user + # Verify that the response was posted by the agent's user expect(last_post.user_id).to eq(bot_user.id) expect(last_post.raw).to eq(response_text) expect(last_post.post_type).to eq(Post.types[:regular]) # Not a whisper @@ -133,7 +133,7 @@ describe DiscourseAi::Automation::LlmPersonaTriage do post = Fabricate(:post, raw: "Error-triggering post") # Set up to cause an error - ai_persona.update!(user_id: nil) + ai_agent.update!(user_id: nil) # Should not raise an error expect { @@ -264,11 +264,11 @@ describe DiscourseAi::Automation::LlmPersonaTriage do topic = reply.topic - # should not inject persona into allowed users + # should not inject agent into allowed users expect(topic.topic_allowed_users.pluck(:user_id).sort).to eq(original_user_ids.sort) end - describe "LLM Persona Triage with Chat Message Creation" do + describe "LLM Agent Triage with Chat Message Creation" do fab!(:user) fab!(:bot_user) { Fabricate(:user) } fab!(:chat_channel) { Fabricate(:category_channel) } @@ -309,14 +309,14 @@ describe DiscourseAi::Automation::LlmPersonaTriage do before do SiteSetting.chat_enabled = true - ai_persona.update!(tools: ["custom-#{custom_tool.id}"]) + ai_agent.update!(tools: ["custom-#{custom_tool.id}"]) # Set up automation fields automation.fields.create!( component: "choices", - name: "persona", + name: "agent", metadata: { - value: ai_persona.id, + value: ai_agent.id, }, target: "script", ) diff --git a/spec/lib/discourse_automation/llm_tool_triage_spec.rb b/spec/lib/discourse_automation/llm_tool_triage_spec.rb index 8baa8dd0..0df43787 100644 --- a/spec/lib/discourse_automation/llm_tool_triage_spec.rb +++ b/spec/lib/discourse_automation/llm_tool_triage_spec.rb @@ -8,10 +8,10 @@ RSpec.describe DiscourseAi::Automation::LlmToolTriage do fab!(:topic) { Fabricate(:topic, user: new_user) } fab!(:post) { Fabricate(:post, topic: topic, user: new_user, raw: "How do I reset my password?") } fab!(:llm_model) - fab!(:ai_persona) do - persona = Fabricate(:ai_persona, default_llm: llm_model) - persona.create_user - persona + fab!(:ai_agent) do + agent = Fabricate(:ai_agent, default_llm: llm_model) + agent.create_user + agent end fab!(:tool) do @@ -28,7 +28,7 @@ RSpec.describe DiscourseAi::Automation::LlmToolTriage do }; } - const helper = discourse.getPersona("#{ai_persona.name}"); + const helper = discourse.getAgent("#{ai_agent.name}"); const answer = helper.respondTo({ post_id: post.id }); return { @@ -79,7 +79,7 @@ RSpec.describe DiscourseAi::Automation::LlmToolTriage do const postId = context.post_id; const post = discourse.getPost(postId); - const helper = discourse.getPersona("#{ai_persona.name}"); + const helper = discourse.getAgent("#{ai_agent.name}"); // Pass instructions to make response a whisper const answer = helper.respondTo({ post_id: post.id, diff --git a/spec/lib/discourse_automation/llm_triage_spec.rb b/spec/lib/discourse_automation/llm_triage_spec.rb index 1b3ca690..af92bb63 100644 --- a/spec/lib/discourse_automation/llm_triage_spec.rb +++ b/spec/lib/discourse_automation/llm_triage_spec.rb @@ -5,7 +5,7 @@ return if !defined?(DiscourseAutomation) describe DiscourseAi::Automation::LlmTriage do fab!(:category) fab!(:reply_user) { Fabricate(:user) } - fab!(:personal_message) { Fabricate(:private_message_topic) } + fab!(:agentl_message) { Fabricate(:private_message_topic) } let(:canned_reply_text) { "Hello, this is a reply" } let(:automation) { Fabricate(:automation, script: "llm_triage", enabled: true) } @@ -82,7 +82,7 @@ describe DiscourseAi::Automation::LlmTriage do end it "does not triage PMs by default" do - post = Fabricate(:post, topic: personal_message) + post = Fabricate(:post, topic: agentl_message) automation.running_in_background! automation.trigger!({ "post" => post }) @@ -93,9 +93,9 @@ describe DiscourseAi::Automation::LlmTriage do # needs to be admin or it will not be able to just step in to # PM reply_user.update!(admin: true) - add_automation_field("include_personal_messages", true, type: :boolean) + add_automation_field("include_agentl_messages", true, type: :boolean) add_automation_field("temperature", "0.2") - post = Fabricate(:post, topic: personal_message) + post = Fabricate(:post, topic: agentl_message) prompt_options = nil DiscourseAi::Completions::Llm.with_prepared_responses( @@ -124,11 +124,11 @@ describe DiscourseAi::Automation::LlmTriage do expect(last_post.raw).to eq post.raw end - it "can respond using an AI persona when configured" do + it "can respond using an AI agent when configured" do bot_user = Fabricate(:user, username: "ai_assistant") - ai_persona = + ai_agent = Fabricate( - :ai_persona, + :ai_agent, name: "Help Bot", description: "AI assistant for forum help", system_prompt: "You are a helpful forum assistant", @@ -136,16 +136,16 @@ describe DiscourseAi::Automation::LlmTriage do user_id: bot_user.id, ) - # Configure the automation to use the persona instead of canned reply + # Configure the automation to use the agent instead of canned reply add_automation_field("canned_reply", nil, type: "message") # Clear canned reply - add_automation_field("reply_persona", ai_persona.id, type: "choices") + add_automation_field("reply_agent", ai_agent.id, type: "choices") add_automation_field("whisper", true, type: "boolean") post = Fabricate(:post, raw: "I need help with a problem") ai_response = "I'll help you with your problem!" - # Set up the test to provide both the triage and the persona responses + # Set up the test to provide both the triage and the agent responses DiscourseAi::Completions::Llm.with_prepared_responses(["bad", ai_response]) do automation.running_in_background! automation.trigger!({ "post" => post }) @@ -155,7 +155,7 @@ describe DiscourseAi::Automation::LlmTriage do topic = post.topic.reload last_post = topic.posts.order(:post_number).last - # Verify the AI persona's user created the post + # Verify the AI agent's user created the post expect(last_post.user_id).to eq(bot_user.id) # Verify the content matches the AI response @@ -166,11 +166,11 @@ describe DiscourseAi::Automation::LlmTriage do end it "does not create replies when the action is edit" do - # Set up bot user and persona + # Set up bot user and agent bot_user = Fabricate(:user, username: "helper_bot") - ai_persona = + ai_agent = Fabricate( - :ai_persona, + :ai_agent, name: "Edit Helper", description: "AI assistant for editing", system_prompt: "You help with editing", @@ -180,7 +180,7 @@ describe DiscourseAi::Automation::LlmTriage do # Configure the automation with both reply methods add_automation_field("canned_reply", "This is a canned reply", type: "message") - add_automation_field("reply_persona", ai_persona.id, type: "choices") + add_automation_field("reply_agent", ai_agent.id, type: "choices") # Create a post and capture its topic post = Fabricate(:post, raw: "This needs to be evaluated") diff --git a/spec/lib/guardian_extensions_spec.rb b/spec/lib/guardian_extensions_spec.rb index 33d43c45..859ce2dd 100644 --- a/spec/lib/guardian_extensions_spec.rb +++ b/spec/lib/guardian_extensions_spec.rb @@ -17,7 +17,7 @@ describe DiscourseAi::GuardianExtensions do describe "#can_see_summary?" do context "when the user cannot generate a summary" do - before { assign_persona_to(:ai_summarization_persona, []) } + before { assign_agent_to(:ai_summarization_agent, []) } it "returns false" do expect(guardian.can_see_summary?(topic)).to eq(false) @@ -31,7 +31,7 @@ describe DiscourseAi::GuardianExtensions do end context "when the user can generate a summary" do - before { assign_persona_to(:ai_summarization_persona, [group.id]) } + before { assign_agent_to(:ai_summarization_agent, [group.id]) } it "returns true if the user group is present in the ai_custom_summarization_allowed_groups_map setting" do expect(guardian.can_see_summary?(topic)).to eq(true) @@ -39,7 +39,7 @@ describe DiscourseAi::GuardianExtensions do end context "when the topic is a PM" do - before { assign_persona_to(:ai_summarization_persona, [group.id]) } + before { assign_agent_to(:ai_summarization_agent, [group.id]) } let(:pm) { Fabricate(:private_message_topic) } it "returns false" do @@ -66,7 +66,7 @@ describe DiscourseAi::GuardianExtensions do end describe "#can_see_gists?" do - before { assign_persona_to(:ai_summary_gists_persona, [group.id]) } + before { assign_agent_to(:ai_summary_gists_agent, [group.id]) } let(:guardian) { Guardian.new(user) } context "when access is restricted to the user's group" do @@ -86,7 +86,7 @@ describe DiscourseAi::GuardianExtensions do end context "when access is set to everyone" do - before { assign_persona_to(:ai_summary_gists_persona, [Group::AUTO_GROUPS[:everyone]]) } + before { assign_agent_to(:ai_summary_gists_agent, [Group::AUTO_GROUPS[:everyone]]) } it "returns true" do expect(guardian.can_see_gists?).to eq(true) diff --git a/spec/lib/modules/ai_bot/entry_point_spec.rb b/spec/lib/modules/ai_bot/entry_point_spec.rb index 0e1aac3e..0d32161b 100644 --- a/spec/lib/modules/ai_bot/entry_point_spec.rb +++ b/spec/lib/modules/ai_bot/entry_point_spec.rb @@ -52,39 +52,39 @@ RSpec.describe DiscourseAi::AiBot::EntryPoint do it "adds information about forcing default llm to current_user_serializer" do Group.refresh_automatic_groups! - persona = + agent = Fabricate( - :ai_persona, + :ai_agent, enabled: true, allowed_group_ids: [bot_allowed_group.id], default_llm_id: claude_2.id, force_default_llm: true, ) - persona.create_user! + agent.create_user! serializer = CurrentUserSerializer.new(admin, scope: Guardian.new(admin)) serializer = serializer.as_json bots = serializer[:current_user][:ai_enabled_chat_bots] - persona_bot = bots.find { |bot| bot["id"] == persona.user_id } + agent_bot = bots.find { |bot| bot["id"] == agent.user_id } - expect(persona_bot["username"]).to eq(persona.user.username) - expect(persona_bot["force_default_llm"]).to eq(true) + expect(agent_bot["username"]).to eq(agent.user.username) + expect(agent_bot["force_default_llm"]).to eq(true) end - it "includes user ids for all personas in the serializer" do + it "includes user ids for all agents in the serializer" do Group.refresh_automatic_groups! - persona = Fabricate(:ai_persona, enabled: true, allowed_group_ids: [bot_allowed_group.id]) - persona.create_user! + agent = Fabricate(:ai_agent, enabled: true, allowed_group_ids: [bot_allowed_group.id]) + agent.create_user! serializer = CurrentUserSerializer.new(admin, scope: Guardian.new(admin)) serializer = serializer.as_json bots = serializer[:current_user][:ai_enabled_chat_bots] - persona_bot = bots.find { |bot| bot["id"] == persona.user_id } - expect(persona_bot["username"]).to eq(persona.user.username) - expect(persona_bot["force_default_llm"]).to eq(false) + agent_bot = bots.find { |bot| bot["id"] == agent.user_id } + expect(agent_bot["username"]).to eq(agent.user.username) + expect(agent_bot["force_default_llm"]).to eq(false) end it "queues a job to generate a reply by the AI" do @@ -176,9 +176,9 @@ RSpec.describe DiscourseAi::AiBot::EntryPoint do end end - it "will include ai_search_discoveries field in the user_option if discover persona is enabled" do + it "will include ai_search_discoveries field in the user_option if discover agent is enabled" do SiteSetting.ai_bot_enabled = true - SiteSetting.ai_bot_discover_persona = Fabricate(:ai_persona).id + SiteSetting.ai_bot_discover_agent = Fabricate(:ai_agent).id serializer = CurrentUserSerializer.new(Fabricate(:user), scope: Guardian.new(Fabricate(:user))) diff --git a/spec/lib/modules/ai_bot/jobs/regular/create_ai_reply_spec.rb b/spec/lib/modules/ai_bot/jobs/regular/create_ai_reply_spec.rb index 7808f1f9..46925251 100644 --- a/spec/lib/modules/ai_bot/jobs/regular/create_ai_reply_spec.rb +++ b/spec/lib/modules/ai_bot/jobs/regular/create_ai_reply_spec.rb @@ -15,17 +15,17 @@ RSpec.describe Jobs::CreateAiReply do "Hello this is a bot and what you just said is an interesting question" end - before { SiteSetting.min_personal_message_post_length = 5 } + before { SiteSetting.min_agentl_message_post_length = 5 } it "adds a reply from the bot" do - persona_id = AiPersona.find_by(name: "Forum Helper").id + agent_id = AiAgent.find_by(name: "Forum Helper").id bot_user = DiscourseAi::AiBot::EntryPoint.find_user_from_model("gpt-3.5-turbo") DiscourseAi::Completions::Llm.with_prepared_responses([expected_response]) do subject.execute( post_id: topic.first_post.id, bot_user_id: bot_user.id, - persona_id: persona_id, + agent_id: agent_id, ) end diff --git a/spec/lib/modules/ai_bot/playground_spec.rb b/spec/lib/modules/ai_bot/playground_spec.rb index e9ad0ac3..50746593 100644 --- a/spec/lib/modules/ai_bot/playground_spec.rb +++ b/spec/lib/modules/ai_bot/playground_spec.rb @@ -20,12 +20,12 @@ RSpec.describe DiscourseAi::AiBot::Playground do end fab!(:bot) do - persona = - AiPersona - .find(DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::General]) + agent = + AiAgent + .find(DiscourseAi::Agents::Agent.system_agents[DiscourseAi::Agents::General]) .class_instance .new - DiscourseAi::Personas::Bot.as(bot_user, persona: persona) + DiscourseAi::Agents::Bot.as(bot_user, agent: agent) end fab!(:admin) { Fabricate(:admin, refresh_auto_groups: true) } @@ -61,16 +61,16 @@ RSpec.describe DiscourseAi::AiBot::Playground do before { SiteSetting.ai_embeddings_enabled = false } after do - # we must reset cache on persona cause data can be rolled back - AiPersona.persona_cache.flush! + # we must reset cache on agent cause data can be rolled back + AiAgent.agent_cache.flush! end describe "is_bot_user_id?" do it "properly detects ALL bots as bot users" do - persona = Fabricate(:ai_persona, enabled: false) - persona.create_user! + agent = Fabricate(:ai_agent, enabled: false) + agent.create_user! - expect(DiscourseAi::AiBot::Playground.is_bot_user_id?(persona.user_id)).to eq(true) + expect(DiscourseAi::AiBot::Playground.is_bot_user_id?(agent.user_id)).to eq(true) end end @@ -88,7 +88,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do ) end - let!(:ai_persona) { Fabricate(:ai_persona, tools: ["custom-#{custom_tool.id}"]) } + let!(:ai_agent) { Fabricate(:ai_agent, tools: ["custom-#{custom_tool.id}"]) } let(:tool_call) do DiscourseAi::Completions::ToolCall.new( name: "search", @@ -99,7 +99,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do ) end - let(:bot) { DiscourseAi::Personas::Bot.as(bot_user, persona: ai_persona.class_instance.new) } + let(:bot) { DiscourseAi::Agents::Bot.as(bot_user, agent: ai_agent.class_instance.new) } let(:playground) { DiscourseAi::AiBot::Playground.new(bot) } @@ -114,7 +114,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do JS tool_name = "custom-#{custom_tool.id}" - ai_persona.update!(tools: [[tool_name, nil, true]], tool_details: false) + ai_agent.update!(tools: [[tool_name, nil, true]], tool_details: false) reply_post = nil prompts = nil @@ -136,7 +136,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do it "can force usage of a tool" do tool_name = "custom-#{custom_tool.id}" - ai_persona.update!(tools: [[tool_name, nil, true]], forced_tool_count: 1) + ai_agent.update!(tools: [[tool_name, nil, true]], forced_tool_count: 1) responses = [tool_call, ["custom tool did stuff (maybe)"], ["new PM title"]] prompts = nil @@ -154,7 +154,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do expect(prompts[0].tool_choice).to eq("search") expect(prompts[1].tool_choice).to eq(nil) - ai_persona.update!(forced_tool_count: 1) + ai_agent.update!(forced_tool_count: 1) responses = ["no tool call here"] DiscourseAi::Completions::Llm.with_prepared_responses(responses) do |_, _, _prompts| @@ -168,8 +168,8 @@ RSpec.describe DiscourseAi::AiBot::Playground do end it "uses custom tool in conversation" do - persona_klass = AiPersona.all_personas.find { |p| p.name == ai_persona.name } - bot = DiscourseAi::Personas::Bot.as(bot_user, persona: persona_klass.new) + agent_klass = AiAgent.all_agents.find { |p| p.name == ai_agent.name } + bot = DiscourseAi::Agents::Bot.as(bot_user, agent: agent_klass.new) playground = described_class.new(bot) responses = [tool_call, "custom tool did stuff (maybe)"] @@ -208,8 +208,8 @@ RSpec.describe DiscourseAi::AiBot::Playground do custom_tool.update!(enabled: false) # so we pick up new cache - persona_klass = AiPersona.all_personas.find { |p| p.name == ai_persona.name } - bot = DiscourseAi::Personas::Bot.as(bot_user, persona: persona_klass.new) + agent_klass = AiAgent.all_agents.find { |p| p.name == ai_agent.name } + bot = DiscourseAi::Agents::Bot.as(bot_user, agent: agent_klass.new) playground = DiscourseAi::AiBot::Playground.new(bot) responses = ["custom tool did stuff (maybe)", tool_call] @@ -230,10 +230,10 @@ RSpec.describe DiscourseAi::AiBot::Playground do SiteSetting.ai_bot_allowed_groups = "#{Group::AUTO_GROUPS[:trust_level_0]}" end - fab!(:persona) do - AiPersona.create!( - name: "Test Persona", - description: "A test persona", + fab!(:agent) do + AiAgent.create!( + name: "Test Agent", + description: "A test agent", allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], enabled: true, system_prompt: "You are a helpful bot", @@ -249,10 +249,10 @@ RSpec.describe DiscourseAi::AiBot::Playground do it "sends images to llm" do post = nil - persona.create_user! + agent.create_user! image = "" - body = "Hey @#{persona.user.username}, can you help me with this image? #{image}" + body = "Hey @#{agent.user.username}, can you help me with this image? #{image}" prompts = nil DiscourseAi::Completions::Llm.with_prepared_responses( @@ -276,29 +276,29 @@ RSpec.describe DiscourseAi::AiBot::Playground do end end - describe "persona with user support" do + describe "agent with user support" do before do Jobs.run_immediately! SiteSetting.ai_bot_allowed_groups = "#{Group::AUTO_GROUPS[:trust_level_0]}" end - fab!(:persona) do - persona = - AiPersona.create!( - name: "Test Persona", - description: "A test persona", + fab!(:agent) do + agent = + AiAgent.create!( + name: "Test Agent", + description: "A test agent", allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], enabled: true, system_prompt: "You are a helpful bot", ) - persona.create_user! - persona.update!( + agent.create_user! + agent.update!( default_llm_id: claude_2.id, allow_chat_channel_mentions: true, allow_topic_mentions: true, ) - persona + agent end context "with chat channels" do @@ -314,7 +314,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do SiteSetting.ai_bot_enabled = true SiteSetting.chat_allowed_groups = "#{Group::AUTO_GROUPS[:trust_level_0]}" Group.refresh_automatic_groups! - persona.update!(allow_chat_channel_mentions: true, default_llm_id: opus_model.id) + agent.update!(allow_chat_channel_mentions: true, default_llm_id: opus_model.id) end it "should behave in a sane way when threading is enabled" do @@ -358,7 +358,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do message = ChatSDK::Message.create( channel_id: channel.id, - raw: "Hello @#{persona.user.username}", + raw: "Hello @#{agent.user.username}", guardian: guardian, ) @@ -418,7 +418,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do ) do |_, _, _prompts| ChatSDK::Message.create( channel_id: channel.id, - raw: "Hello @#{persona.user.username}", + raw: "Hello @#{agent.user.username}", guardian: guardian, ) @@ -438,12 +438,12 @@ RSpec.describe DiscourseAi::AiBot::Playground do end context "with chat dms" do - fab!(:dm_channel) { Fabricate(:direct_message_channel, users: [user, persona.user]) } + fab!(:dm_channel) { Fabricate(:direct_message_channel, users: [user, agent.user]) } before do SiteSetting.chat_allowed_groups = "#{Group::AUTO_GROUPS[:trust_level_0]}" Group.refresh_automatic_groups! - persona.update!( + agent.update!( allow_chat_direct_messages: true, allow_topic_mentions: false, allow_chat_channel_mentions: false, @@ -479,7 +479,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do end it "can run tools" do - persona.update!(tools: ["Time"]) + agent.update!(tools: ["Time"]) tool_call1 = DiscourseAi::Completions::ToolCall.new( @@ -535,8 +535,8 @@ RSpec.describe DiscourseAi::AiBot::Playground do expect(thread_messages.last.message).to eq("World") # it also needs to include history per config - first feed some history - persona.update!(enabled: false) - persona_guardian = Guardian.new(persona.user) + agent.update!(enabled: false) + agent_guardian = Guardian.new(agent.user) 4.times do |i| ChatSDK::Message.create( @@ -550,11 +550,11 @@ RSpec.describe DiscourseAi::AiBot::Playground do channel_id: dm_channel.id, thread_id: message.thread_id, raw: "response #{i}", - guardian: persona_guardian, + guardian: agent_guardian, ) end - persona.update!(max_context_posts: 4, enabled: true) + agent.update!(max_context_posts: 4, enabled: true) prompts = nil DiscourseAi::Completions::Llm.with_prepared_responses( @@ -599,7 +599,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do post = create_post( title: "My public topic", - raw: "Hey @#{persona.user.username}, can you help me?", + raw: "Hey @#{agent.user.username}, can you help me?", post_type: Post.types[:whisper], ) end @@ -607,36 +607,36 @@ RSpec.describe DiscourseAi::AiBot::Playground do post.topic.reload last_post = post.topic.posts.order(:post_number).last expect(last_post.raw).to eq("Yes I can") - expect(last_post.user_id).to eq(persona.user_id) + expect(last_post.user_id).to eq(agent.user_id) expect(last_post.post_type).to eq(Post.types[:whisper]) end - it "allows mentioning a persona" do + it "allows mentioning a agent" do # we still should be able to mention with no bots toggle_enabled_bots(bots: []) - persona.update!(allow_topic_mentions: true) + agent.update!(allow_topic_mentions: true) post = nil DiscourseAi::Completions::Llm.with_prepared_responses(["Yes I can"]) do post = create_post( title: "My public topic", - raw: "Hey @#{persona.user.username}, can you help me?", + raw: "Hey @#{agent.user.username}, can you help me?", ) end post.topic.reload last_post = post.topic.posts.order(:post_number).last expect(last_post.raw).to eq("Yes I can") - expect(last_post.user_id).to eq(persona.user_id) + expect(last_post.user_id).to eq(agent.user_id) - persona.update!(allow_topic_mentions: false) + agent.update!(allow_topic_mentions: false) post = create_post( title: "My public topic ABC", - raw: "Hey @#{persona.user.username}, can you help me?", + raw: "Hey @#{agent.user.username}, can you help me?", ) expect(post.topic.posts.last.post_number).to eq(1) @@ -653,15 +653,15 @@ RSpec.describe DiscourseAi::AiBot::Playground do post = create_post( title: "I just made a PM", - raw: "Hey there #{persona.user.username}, can you help me?", - target_usernames: "#{user.username},#{persona.user.username},#{claude_2.user.username}", + raw: "Hey there #{agent.user.username}, can you help me?", + target_usernames: "#{user.username},#{agent.user.username},#{claude_2.user.username}", archetype: Archetype.private_message, user: admin, ) end # note that this is a string due to custom field shananigans - post.topic.custom_fields["ai_persona_id"] = persona.id.to_s + post.topic.custom_fields["ai_agent_id"] = agent.id.to_s post.topic.save_custom_fields llm2 = Fabricate(:llm_model, enabled_chat_bot: true) @@ -678,7 +678,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do last_post = post.topic.reload.posts.order("id desc").first expect(last_post.raw).to eq("Hi from bot two") - expect(last_post.user_id).to eq(persona.user_id) + expect(last_post.user_id).to eq(agent.user_id) current_users = last_post.topic.reload.topic_allowed_users.joins(:user).pluck(:username) expect(current_users).to include(llm2.user.username) @@ -694,10 +694,10 @@ RSpec.describe DiscourseAi::AiBot::Playground do last_post = post.topic.reload.posts.order("id desc").first expect(last_post.raw).to eq("Hi from bot two") - expect(last_post.user_id).to eq(persona.user_id) + expect(last_post.user_id).to eq(agent.user_id) # tether llm, so it can no longer be switched - persona.update!(force_default_llm: true, default_llm_id: claude_2.id) + agent.update!(force_default_llm: true, default_llm_id: claude_2.id) DiscourseAi::Completions::Llm.with_prepared_responses(["Hi from bot one"], llm: claude_2) do create_post( @@ -709,10 +709,10 @@ RSpec.describe DiscourseAi::AiBot::Playground do last_post = post.topic.reload.posts.order("id desc").first expect(last_post.raw).to eq("Hi from bot one") - expect(last_post.user_id).to eq(persona.user_id) + expect(last_post.user_id).to eq(agent.user_id) end - it "allows PMing a persona even when no particular bots are enabled" do + it "allows PMing a agent even when no particular bots are enabled" do SiteSetting.ai_bot_enabled = true toggle_enabled_bots(bots: []) post = nil @@ -724,8 +724,8 @@ RSpec.describe DiscourseAi::AiBot::Playground do post = create_post( title: "I just made a PM", - raw: "Hey there #{persona.user.username}, can you help me?", - target_usernames: "#{user.username},#{persona.user.username}", + raw: "Hey there #{agent.user.username}, can you help me?", + target_usernames: "#{user.username},#{agent.user.username}", archetype: Archetype.private_message, user: admin, ) @@ -733,19 +733,19 @@ RSpec.describe DiscourseAi::AiBot::Playground do last_post = post.topic.posts.order(:post_number).last expect(last_post.raw).to eq("Yes I can") - expect(last_post.user_id).to eq(persona.user_id) + expect(last_post.user_id).to eq(agent.user_id) last_post.topic.reload - expect(last_post.topic.allowed_users.pluck(:user_id)).to include(persona.user_id) + expect(last_post.topic.allowed_users.pluck(:user_id)).to include(agent.user_id) expect(last_post.topic.participant_count).to eq(2) # ensure it can be disabled - persona.update!(allow_personal_messages: false) + agent.update!(allow_agentl_messages: false) post = create_post( - raw: "Hey there #{persona.user.username}, can you help me please", + raw: "Hey there #{agent.user.username}, can you help me please", topic_id: post.topic.id, user: admin, ) @@ -753,7 +753,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do expect(post.post_number).to eq(3) end - it "can tether a persona unconditionally to an llm" do + it "can tether a agent unconditionally to an llm" do gpt_35_turbo = Fabricate(:llm_model, name: "gpt-3.5-turbo") # If you start a PM with GPT 3.5 bot, replies should come from it, not from Claude @@ -761,7 +761,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do toggle_enabled_bots(bots: [gpt_35_turbo, claude_2]) post = nil - persona.update!(force_default_llm: true, default_llm_id: gpt_35_turbo.id) + agent.update!(force_default_llm: true, default_llm_id: gpt_35_turbo.id) DiscourseAi::Completions::Llm.with_prepared_responses( ["Yes I can", "Magic Title"], @@ -775,21 +775,21 @@ RSpec.describe DiscourseAi::AiBot::Playground do archetype: Archetype.private_message, user: admin, custom_fields: { - "ai_persona_id" => persona.id, + "ai_agent_id" => agent.id, }, ) end last_post = post.topic.posts.order(:post_number).last expect(last_post.raw).to eq("Yes I can") - expect(last_post.user_id).to eq(persona.user_id) + expect(last_post.user_id).to eq(agent.user_id) expect(last_post.custom_fields[DiscourseAi::AiBot::POST_AI_LLM_NAME_FIELD]).to eq( gpt_35_turbo.display_name, ) end - it "picks the correct llm for persona in PMs" do + it "picks the correct llm for agent in PMs" do gpt_35_turbo = Fabricate(:llm_model, name: "gpt-3.5-turbo") # If you start a PM with GPT 3.5 bot, replies should come from it, not from Claude @@ -809,7 +809,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do post = create_post( title: "I just made a PM", - raw: "Hey @#{persona.user.username}, can you help me?", + raw: "Hey @#{agent.user.username}, can you help me?", target_usernames: "#{user.username},#{gpt3_5_bot_user.username}", archetype: Archetype.private_message, user: admin, @@ -823,10 +823,10 @@ RSpec.describe DiscourseAi::AiBot::Playground do expect(title_update_message.data).to eq({ title: "Magic Title" }) last_post = post.topic.posts.order(:post_number).last expect(last_post.raw).to eq("Yes I can") - expect(last_post.user_id).to eq(persona.user_id) + expect(last_post.user_id).to eq(agent.user_id) last_post.topic.reload - expect(last_post.topic.allowed_users.pluck(:user_id)).to include(persona.user_id) + expect(last_post.topic.allowed_users.pluck(:user_id)).to include(agent.user_id) # does not reply if replying directly to a user # nothing is mocked, so this would result in HTTP error @@ -838,7 +838,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do reply_to_post_number: post.post_number, ) - # replies as correct persona if replying direct to persona + # replies as correct agent if replying direct to agent DiscourseAi::Completions::Llm.with_prepared_responses(["Another reply"], llm: gpt_35_turbo) do create_post( raw: "Please ignore this bot, I am replying to a user", @@ -850,14 +850,14 @@ RSpec.describe DiscourseAi::AiBot::Playground do last_post = post.topic.posts.order(:post_number).last expect(last_post.raw).to eq("Another reply") - expect(last_post.user_id).to eq(persona.user_id) + expect(last_post.user_id).to eq(agent.user_id) end end describe "#title_playground" do let(:expected_response) { "This is a suggested title" } - before { SiteSetting.min_personal_message_post_length = 5 } + before { SiteSetting.min_agentl_message_post_length = 5 } it "updates the title using bot suggestions" do DiscourseAi::Completions::Llm.with_prepared_responses([expected_response]) do @@ -985,8 +985,8 @@ RSpec.describe DiscourseAi::AiBot::Playground do end it "supports disabling tool details" do - persona = Fabricate(:ai_persona, tool_details: false, tools: ["Search"]) - bot = DiscourseAi::Personas::Bot.as(bot_user, persona: persona.class_instance.new) + agent = Fabricate(:ai_agent, tool_details: false, tools: ["Search"]) + bot = DiscourseAi::Agents::Bot.as(bot_user, agent: agent.class_instance.new) playground = described_class.new(bot) response1 = @@ -1037,13 +1037,13 @@ RSpec.describe DiscourseAi::AiBot::Playground do context "with Dall E bot" do before { SiteSetting.ai_openai_api_key = "123" } - let(:persona) do - AiPersona.find( - DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::DallE3], + let(:agent) do + AiAgent.find( + DiscourseAi::Agents::Agent.system_agents[DiscourseAi::Agents::DallE3], ) end - let(:bot) { DiscourseAi::Personas::Bot.as(bot_user, persona: persona.class_instance.new) } + let(:bot) { DiscourseAi::Agents::Bot.as(bot_user, agent: agent.class_instance.new) } let(:data) do image = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==" @@ -1062,7 +1062,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do end it "properly returns an image when skipping tool details" do - persona.update!(tool_details: false) + agent.update!(tool_details: false) WebMock.stub_request(:post, SiteSetting.ai_openai_image_generation_url).to_return( status: 200, @@ -1165,11 +1165,11 @@ RSpec.describe DiscourseAi::AiBot::Playground do end describe "#available_bot_usernames" do - it "includes persona users" do - persona = Fabricate(:ai_persona) - persona.create_user! + it "includes agent users" do + agent = Fabricate(:ai_agent) + agent.create_user! - expect(playground.available_bot_usernames).to include(persona.user.username) + expect(playground.available_bot_usernames).to include(agent.user.username) end end @@ -1198,8 +1198,8 @@ RSpec.describe DiscourseAi::AiBot::Playground do ) end - let!(:ai_persona) { Fabricate(:ai_persona, tools: ["custom-#{custom_tool.id}"]) } - let(:bot) { DiscourseAi::Personas::Bot.as(bot_user, persona: ai_persona.class_instance.new) } + let!(:ai_agent) { Fabricate(:ai_agent, tools: ["custom-#{custom_tool.id}"]) } + let(:bot) { DiscourseAi::Agents::Bot.as(bot_user, agent: ai_agent.class_instance.new) } let(:playground) { DiscourseAi::AiBot::Playground.new(bot) } it "injects custom context into the prompt" do diff --git a/spec/lib/modules/sentiment/post_classification_spec.rb b/spec/lib/modules/sentiment/post_classification_spec.rb index f63da694..8c8b8fcd 100644 --- a/spec/lib/modules/sentiment/post_classification_spec.rb +++ b/spec/lib/modules/sentiment/post_classification_spec.rb @@ -127,7 +127,7 @@ RSpec.describe DiscourseAi::Sentiment::PostClassification do end describe ".backfill_query" do - it "excludes posts in personal messages" do + it "excludes posts in agentl messages" do Fabricate(:private_message_post) posts = described_class.backfill_query diff --git a/spec/lib/modules/summarization/entry_point_spec.rb b/spec/lib/modules/summarization/entry_point_spec.rb index 0d57cbf9..9eb80e85 100644 --- a/spec/lib/modules/summarization/entry_point_spec.rb +++ b/spec/lib/modules/summarization/entry_point_spec.rb @@ -63,7 +63,7 @@ RSpec.describe DiscourseAi::Summarization::EntryPoint do before do group.add(user) - assign_persona_to(:ai_summary_gists_persona, [group.id]) + assign_agent_to(:ai_summary_gists_agent, [group.id]) SiteSetting.ai_summary_gists_enabled = true end diff --git a/spec/lib/utils/research/filter_spec.rb b/spec/lib/utils/research/filter_spec.rb index 866d63e8..2869e5ea 100644 --- a/spec/lib/utils/research/filter_spec.rb +++ b/spec/lib/utils/research/filter_spec.rb @@ -4,7 +4,7 @@ describe DiscourseAi::Utils::Research::Filter do describe "integration tests" do before_all do SiteSetting.min_topic_title_length = 3 - SiteSetting.min_personal_message_title_length = 3 + SiteSetting.min_agentl_message_title_length = 3 end fab!(:user) diff --git a/spec/models/ai_agent_multisite_spec.rb b/spec/models/ai_agent_multisite_spec.rb new file mode 100644 index 00000000..a49033eb --- /dev/null +++ b/spec/models/ai_agent_multisite_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +RSpec.describe AiAgent, type: :multisite do + it "is able to amend settings on system agents on multisite" do + agent = AiAgent.find_by(name: "Designer") + expect(agent.allow_agentl_messages).to eq(true) + agent.update!(allow_agentl_messages: false) + + instance = agent.class_instance + expect(instance.allow_agentl_messages).to eq(false) + + test_multisite_connection("second") do + agent = AiAgent.find_by(name: "Designer") + expect(agent.allow_agentl_messages).to eq(true) + instance = agent.class_instance + expect(instance.name).to eq("Designer") + expect(instance.allow_agentl_messages).to eq(true) + end + + agent = AiAgent.find_by(name: "Designer") + instance = agent.class_instance + expect(instance.allow_agentl_messages).to eq(false) + end +end diff --git a/spec/models/ai_persona_spec.rb b/spec/models/ai_agent_spec.rb similarity index 55% rename from spec/models/ai_persona_spec.rb rename to spec/models/ai_agent_spec.rb index 0e6b9d13..c10d1337 100644 --- a/spec/models/ai_persona_spec.rb +++ b/spec/models/ai_agent_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -RSpec.describe AiPersona do - subject(:basic_persona) do - AiPersona.new( +RSpec.describe AiAgent do + subject(:basic_agent) do + AiAgent.new( name: "test", description: "test", system_prompt: "test", @@ -15,74 +15,74 @@ RSpec.describe AiPersona do fab!(:seeded_llm_model) { Fabricate(:llm_model, id: -1) } it "validates context settings" do - expect(basic_persona.valid?).to eq(true) + expect(basic_agent.valid?).to eq(true) - basic_persona.max_context_posts = 0 - expect(basic_persona.valid?).to eq(false) - expect(basic_persona.errors[:max_context_posts]).to eq(["must be greater than 0"]) + basic_agent.max_context_posts = 0 + expect(basic_agent.valid?).to eq(false) + expect(basic_agent.errors[:max_context_posts]).to eq(["must be greater than 0"]) - basic_persona.max_context_posts = 1 - expect(basic_persona.valid?).to eq(true) + basic_agent.max_context_posts = 1 + expect(basic_agent.valid?).to eq(true) - basic_persona.max_context_posts = nil - expect(basic_persona.valid?).to eq(true) + basic_agent.max_context_posts = nil + expect(basic_agent.valid?).to eq(true) end it "validates tools" do Fabricate(:ai_tool, id: 1) Fabricate(:ai_tool, id: 2, name: "Archie search", tool_name: "search") - expect(basic_persona.valid?).to eq(true) + expect(basic_agent.valid?).to eq(true) - basic_persona.tools = %w[search image_generation] - expect(basic_persona.valid?).to eq(true) + basic_agent.tools = %w[search image_generation] + expect(basic_agent.valid?).to eq(true) - basic_persona.tools = %w[search image_generation search] - expect(basic_persona.valid?).to eq(false) - expect(basic_persona.errors[:tools]).to eq(["Can not have duplicate tools"]) + basic_agent.tools = %w[search image_generation search] + expect(basic_agent.valid?).to eq(false) + expect(basic_agent.errors[:tools]).to eq(["Can not have duplicate tools"]) - basic_persona.tools = [ + basic_agent.tools = [ ["custom-1", { test: "test" }, false], ["custom-2", { test: "test" }, false], ] - expect(basic_persona.valid?).to eq(true) - expect(basic_persona.errors[:tools]).to eq([]) + expect(basic_agent.valid?).to eq(true) + expect(basic_agent.errors[:tools]).to eq([]) - basic_persona.tools = [ + basic_agent.tools = [ ["custom-1", { test: "test" }, false], ["custom-1", { test: "test" }, false], ] - expect(basic_persona.valid?).to eq(false) - expect(basic_persona.errors[:tools]).to eq(["Can not have duplicate tools"]) + expect(basic_agent.valid?).to eq(false) + expect(basic_agent.errors[:tools]).to eq(["Can not have duplicate tools"]) - basic_persona.tools = [ + basic_agent.tools = [ ["custom-1", { test: "test" }, false], ["custom-2", { test: "test" }, false], "image_generation", ] - expect(basic_persona.valid?).to eq(true) - expect(basic_persona.errors[:tools]).to eq([]) + expect(basic_agent.valid?).to eq(true) + expect(basic_agent.errors[:tools]).to eq([]) - basic_persona.tools = [ + basic_agent.tools = [ ["custom-1", { test: "test" }, false], ["custom-2", { test: "test" }, false], "Search", ] - expect(basic_persona.valid?).to eq(false) - expect(basic_persona.errors[:tools]).to eq(["Can not have duplicate tools"]) + expect(basic_agent.valid?).to eq(false) + expect(basic_agent.errors[:tools]).to eq(["Can not have duplicate tools"]) end it "allows creation of user" do - user = basic_persona.create_user! + user = basic_agent.create_user! expect(user.username).to eq("test_bot") expect(user.name).to eq("Test") expect(user.bot?).to be(true) - expect(user.id).to be <= AiPersona::FIRST_PERSONA_USER_ID + expect(user.id).to be <= AiAgent::FIRST_AGENT_USER_ID end it "removes all rag embeddings when rag params change" do - persona = - AiPersona.create!( + agent = + AiAgent.create!( name: "test", description: "test", system_prompt: "test", @@ -94,26 +94,26 @@ RSpec.describe AiPersona do id = RagDocumentFragment.create!( - target: persona, + target: agent, fragment: "test", fragment_number: 1, upload: Fabricate(:upload), ).id - persona.rag_chunk_tokens = 20 - persona.save! + agent.rag_chunk_tokens = 20 + agent.save! expect(RagDocumentFragment.exists?(id)).to eq(false) end - it "defines singleton methods on system persona classes" do - forum_helper = AiPersona.find_by(name: "Forum Helper") + it "defines singleton methods on system agent classes" do + forum_helper = AiAgent.find_by(name: "Forum Helper") forum_helper.update!( user_id: 1, default_llm_id: llm_model.id, max_context_posts: 3, allow_topic_mentions: true, - allow_personal_messages: true, + allow_agentl_messages: true, allow_chat_channel_mentions: true, allow_chat_direct_messages: true, ) @@ -128,14 +128,14 @@ RSpec.describe AiPersona do expect(klass.default_llm_id).to eq(llm_model.id) expect(klass.max_context_posts).to eq(3) expect(klass.allow_topic_mentions).to eq(true) - expect(klass.allow_personal_messages).to eq(true) + expect(klass.allow_agentl_messages).to eq(true) expect(klass.allow_chat_channel_mentions).to eq(true) expect(klass.allow_chat_direct_messages).to eq(true) end - it "defines singleton methods non persona classes" do - persona = - AiPersona.create!( + it "defines singleton methods non agent classes" do + agent = + AiAgent.create!( name: "test", description: "test", system_prompt: "test", @@ -144,29 +144,29 @@ RSpec.describe AiPersona do default_llm_id: llm_model.id, max_context_posts: 3, allow_topic_mentions: true, - allow_personal_messages: true, + allow_agentl_messages: true, allow_chat_channel_mentions: true, allow_chat_direct_messages: true, user_id: 1, ) - klass = persona.class_instance + klass = agent.class_instance - expect(klass.id).to eq(persona.id) + expect(klass.id).to eq(agent.id) expect(klass.system).to eq(false) expect(klass.allowed_group_ids).to eq([]) expect(klass.user_id).to eq(1) expect(klass.default_llm_id).to eq(llm_model.id) expect(klass.max_context_posts).to eq(3) expect(klass.allow_topic_mentions).to eq(true) - expect(klass.allow_personal_messages).to eq(true) + expect(klass.allow_agentl_messages).to eq(true) expect(klass.allow_chat_channel_mentions).to eq(true) expect(klass.allow_chat_direct_messages).to eq(true) end it "does not allow setting allowing chat without a default_llm" do - persona = - AiPersona.create( + agent = + AiAgent.create( name: "test", description: "test", system_prompt: "test", @@ -175,13 +175,13 @@ RSpec.describe AiPersona do allow_chat_channel_mentions: true, ) - expect(persona.valid?).to eq(false) - expect(persona.errors[:default_llm].first).to eq( - I18n.t("discourse_ai.ai_bot.personas.default_llm_required"), + expect(agent.valid?).to eq(false) + expect(agent.errors[:default_llm].first).to eq( + I18n.t("discourse_ai.ai_bot.agents.default_llm_required"), ) - persona = - AiPersona.create( + agent = + AiAgent.create( name: "test", description: "test", system_prompt: "test", @@ -190,13 +190,13 @@ RSpec.describe AiPersona do allow_chat_direct_messages: true, ) - expect(persona.valid?).to eq(false) - expect(persona.errors[:default_llm].first).to eq( - I18n.t("discourse_ai.ai_bot.personas.default_llm_required"), + expect(agent.valid?).to eq(false) + expect(agent.errors[:default_llm].first).to eq( + I18n.t("discourse_ai.ai_bot.agents.default_llm_required"), ) - persona = - AiPersona.create( + agent = + AiAgent.create( name: "test", description: "test", system_prompt: "test", @@ -205,28 +205,28 @@ RSpec.describe AiPersona do allow_topic_mentions: true, ) - expect(persona.valid?).to eq(false) - expect(persona.errors[:default_llm].first).to eq( - I18n.t("discourse_ai.ai_bot.personas.default_llm_required"), + expect(agent.valid?).to eq(false) + expect(agent.errors[:default_llm].first).to eq( + I18n.t("discourse_ai.ai_bot.agents.default_llm_required"), ) end it "validates allowed seeded model" do - basic_persona.default_llm_id = seeded_llm_model.id + basic_agent.default_llm_id = seeded_llm_model.id SiteSetting.ai_bot_allowed_seeded_models = "" - expect(basic_persona.valid?).to eq(false) - expect(basic_persona.errors[:default_llm]).to include( + expect(basic_agent.valid?).to eq(false) + expect(basic_agent.errors[:default_llm]).to include( I18n.t("discourse_ai.llm.configuration.invalid_seeded_model"), ) SiteSetting.ai_bot_allowed_seeded_models = "-1" - expect(basic_persona.valid?).to eq(true) + expect(basic_agent.valid?).to eq(true) end it "does not leak caches between sites" do - AiPersona.create!( + AiAgent.create!( name: "pun_bot", description: "you write puns", system_prompt: "you are pun bot", @@ -234,19 +234,19 @@ RSpec.describe AiPersona do allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], ) - AiPersona.all_personas + AiAgent.all_agents - expect(AiPersona.persona_cache[:value].length).to be > (0) + expect(AiAgent.agent_cache[:value].length).to be > (0) RailsMultisite::ConnectionManagement.stubs(:current_db) { "abc" } - expect(AiPersona.persona_cache[:value]).to eq(nil) + expect(AiAgent.agent_cache[:value]).to eq(nil) end - describe "system persona validations" do - let(:system_persona) do - AiPersona.create!( - name: "system_persona", - description: "system persona", - system_prompt: "system persona", + describe "system agent validations" do + let(:system_agent) do + AiAgent.create!( + name: "system_agent", + description: "system agent", + system_prompt: "system agent", tools: %w[Search Time], response_format: [{ key: "summary", type: "string" }], examples: [%w[user_msg1 assistant_msg1], %w[user_msg2 assistant_msg2]], @@ -254,25 +254,25 @@ RSpec.describe AiPersona do ) end - context "when modifying a system persona" do + context "when modifying a system agent" do it "allows changing tool options without allowing tool additions/removals" do tools = [["Search", { "base_query" => "abc" }], ["Time"]] - system_persona.update!(tools: tools) + system_agent.update!(tools: tools) - system_persona.reload - expect(system_persona.tools).to eq(tools) + system_agent.reload + expect(system_agent.tools).to eq(tools) invalid_tools = ["Time"] - system_persona.update(tools: invalid_tools) - expect(system_persona.errors[:base]).to include( - I18n.t("discourse_ai.ai_bot.personas.cannot_edit_system_persona"), + system_agent.update(tools: invalid_tools) + expect(system_agent.errors[:base]).to include( + I18n.t("discourse_ai.ai_bot.agents.cannot_edit_system_agent"), ) end it "doesn't accept response format changes" do new_format = [{ key: "summary2", type: "string" }] - expect { system_persona.update!(response_format: new_format) }.to raise_error( + expect { system_agent.update!(response_format: new_format) }.to raise_error( ActiveRecord::RecordInvalid, ) end @@ -280,7 +280,7 @@ RSpec.describe AiPersona do it "doesn't accept additional format changes" do new_format = [{ key: "summary", type: "string" }, { key: "summary2", type: "string" }] - expect { system_persona.update!(response_format: new_format) }.to raise_error( + expect { system_agent.update!(response_format: new_format) }.to raise_error( ActiveRecord::RecordInvalid, ) end @@ -288,7 +288,7 @@ RSpec.describe AiPersona do it "doesn't accept changes to examples" do other_examples = [%w[user_msg1 assistant_msg1]] - expect { system_persona.update!(examples: other_examples) }.to raise_error( + expect { system_agent.update!(examples: other_examples) }.to raise_error( ActiveRecord::RecordInvalid, ) end @@ -297,27 +297,27 @@ RSpec.describe AiPersona do describe "validates examples format" do it "doesn't accept examples that are not arrays" do - basic_persona.examples = [1] + basic_agent.examples = [1] - expect(basic_persona.valid?).to eq(false) - expect(basic_persona.errors[:examples].first).to eq( - I18n.t("discourse_ai.personas.malformed_examples"), + expect(basic_agent.valid?).to eq(false) + expect(basic_agent.errors[:examples].first).to eq( + I18n.t("discourse_ai.agents.malformed_examples"), ) end it "doesn't accept examples that don't come in pairs" do - basic_persona.examples = [%w[user_msg1]] + basic_agent.examples = [%w[user_msg1]] - expect(basic_persona.valid?).to eq(false) - expect(basic_persona.errors[:examples].first).to eq( - I18n.t("discourse_ai.personas.malformed_examples"), + expect(basic_agent.valid?).to eq(false) + expect(basic_agent.errors[:examples].first).to eq( + I18n.t("discourse_ai.agents.malformed_examples"), ) end it "works when example is well formatted" do - basic_persona.examples = [%w[user_msg1 assistant1]] + basic_agent.examples = [%w[user_msg1 assistant1]] - expect(basic_persona.valid?).to eq(true) + expect(basic_agent.valid?).to eq(true) end end end diff --git a/spec/models/ai_persona_multisite_spec.rb b/spec/models/ai_persona_multisite_spec.rb deleted file mode 100644 index 84670303..00000000 --- a/spec/models/ai_persona_multisite_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe AiPersona, type: :multisite do - it "is able to amend settings on system personas on multisite" do - persona = AiPersona.find_by(name: "Designer") - expect(persona.allow_personal_messages).to eq(true) - persona.update!(allow_personal_messages: false) - - instance = persona.class_instance - expect(instance.allow_personal_messages).to eq(false) - - test_multisite_connection("second") do - persona = AiPersona.find_by(name: "Designer") - expect(persona.allow_personal_messages).to eq(true) - instance = persona.class_instance - expect(instance.name).to eq("Designer") - expect(instance.allow_personal_messages).to eq(true) - end - - persona = AiPersona.find_by(name: "Designer") - instance = persona.class_instance - expect(instance.allow_personal_messages).to eq(false) - end -end diff --git a/spec/models/ai_tool_spec.rb b/spec/models/ai_tool_spec.rb index 56de5de5..a15bc981 100644 --- a/spec/models/ai_tool_spec.rb +++ b/spec/models/ai_tool_spec.rb @@ -121,7 +121,7 @@ RSpec.describe AiTool do }, ) - expect { runner.invoke }.to raise_error(DiscourseAi::Personas::ToolRunner::TooManyRequestsError) + expect { runner.invoke }.to raise_error(DiscourseAi::Agents::ToolRunner::TooManyRequestsError) end it "can perform GET HTTP requests" do @@ -566,15 +566,15 @@ RSpec.describe AiTool do end end - context "when updating personas" do - fab!(:ai_persona) do - Fabricate(:ai_persona, name: "TestPersona", system_prompt: "Original prompt") + context "when updating agents" do + fab!(:ai_agent) do + Fabricate(:ai_agent, name: "TestAgent", system_prompt: "Original prompt") end - it "can update a persona with proper permissions" do + it "can update a agent with proper permissions" do script = <<~JS function invoke(params) { - return discourse.updatePersona(params.persona_name, { + return discourse.updateAgent(params.agent_name, { system_prompt: params.new_prompt, temperature: 0.7, top_p: 0.9 @@ -585,28 +585,28 @@ RSpec.describe AiTool do tool = create_tool(script: script) runner = tool.runner( - { persona_name: "TestPersona", new_prompt: "Updated system prompt" }, + { agent_name: "TestAgent", new_prompt: "Updated system prompt" }, llm: nil, bot_user: bot_user, ) result = runner.invoke expect(result["success"]).to eq(true) - expect(result["persona"]["system_prompt"]).to eq("Updated system prompt") - expect(result["persona"]["temperature"]).to eq(0.7) + expect(result["agent"]["system_prompt"]).to eq("Updated system prompt") + expect(result["agent"]["temperature"]).to eq(0.7) - ai_persona.reload - expect(ai_persona.system_prompt).to eq("Updated system prompt") - expect(ai_persona.temperature).to eq(0.7) - expect(ai_persona.top_p).to eq(0.9) + ai_agent.reload + expect(ai_agent.system_prompt).to eq("Updated system prompt") + expect(ai_agent.temperature).to eq(0.7) + expect(ai_agent.top_p).to eq(0.9) end end - context "when fetching persona information" do - fab!(:ai_persona) do + context "when fetching agent information" do + fab!(:ai_agent) do Fabricate( - :ai_persona, - name: "TestPersona", + :ai_agent, + name: "TestAgent", description: "Test description", system_prompt: "Test system prompt", temperature: 0.8, @@ -616,21 +616,21 @@ RSpec.describe AiTool do ) end - it "can fetch a persona by name" do + it "can fetch a agent by name" do script = <<~JS function invoke(params) { - const persona = discourse.getPersona(params.persona_name); - return persona; + const agent = discourse.getAgent(params.agent_name); + return agent; } JS tool = create_tool(script: script) - runner = tool.runner({ persona_name: "TestPersona" }, llm: nil, bot_user: bot_user) + runner = tool.runner({ agent_name: "TestAgent" }, llm: nil, bot_user: bot_user) result = runner.invoke - expect(result["id"]).to eq(ai_persona.id) - expect(result["name"]).to eq("TestPersona") + expect(result["id"]).to eq(ai_agent.id) + expect(result["name"]).to eq("TestAgent") expect(result["description"]).to eq("Test description") expect(result["system_prompt"]).to eq("Test system prompt") expect(result["temperature"]).to eq(0.8) @@ -640,25 +640,25 @@ RSpec.describe AiTool do expect(result["tools"][1]).to be_a(Array) end - it "raises an error when the persona doesn't exist" do + it "raises an error when the agent doesn't exist" do script = <<~JS function invoke(params) { - return discourse.getPersona("NonExistentPersona"); + return discourse.getAgent("NonExistentAgent"); } JS tool = create_tool(script: script) runner = tool.runner({}, llm: nil, bot_user: bot_user) - expect { runner.invoke }.to raise_error(MiniRacer::RuntimeError, /Persona not found/) + expect { runner.invoke }.to raise_error(MiniRacer::RuntimeError, /Agent not found/) end - it "can update a persona after fetching it" do + it "can update a agent after fetching it" do script = <<~JS function invoke(params) { - const persona = discourse.getPersona("TestPersona"); - return persona.update({ - system_prompt: "Updated through getPersona().update()", + const agent = discourse.getAgent("TestAgent"); + return agent.update({ + system_prompt: "Updated through getAgent().update()", temperature: 0.5 }); } @@ -670,9 +670,9 @@ RSpec.describe AiTool do result = runner.invoke expect(result["success"]).to eq(true) - ai_persona.reload - expect(ai_persona.system_prompt).to eq("Updated through getPersona().update()") - expect(ai_persona.temperature).to eq(0.5) + ai_agent.reload + expect(ai_agent.system_prompt).to eq("Updated through getAgent().update()") + expect(ai_agent.temperature).to eq(0.5) end end end diff --git a/spec/models/rag_document_fragment_spec.rb b/spec/models/rag_document_fragment_spec.rb index 58824c47..5cdb8d2f 100644 --- a/spec/models/rag_document_fragment_spec.rb +++ b/spec/models/rag_document_fragment_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe RagDocumentFragment do - fab!(:persona) { Fabricate(:ai_persona) } + fab!(:agent) { Fabricate(:ai_agent) } fab!(:upload_1) { Fabricate(:upload) } fab!(:upload_2) { Fabricate(:upload) } fab!(:vector_def) { Fabricate(:embedding_definition) } @@ -11,8 +11,8 @@ RSpec.describe RagDocumentFragment do SiteSetting.ai_embeddings_enabled = true end - describe ".link_uploads_and_persona" do - it "does nothing if there is no persona" do + describe ".link_uploads_and_agent" do + it "does nothing if there is no agent" do expect { described_class.link_target_and_uploads(nil, [upload_1.id]) }.not_to change( Jobs::DigestRagUpload.jobs, :size, @@ -20,7 +20,7 @@ RSpec.describe RagDocumentFragment do end it "does nothing if there are no uploads" do - expect { described_class.link_target_and_uploads(persona, []) }.not_to change( + expect { described_class.link_target_and_uploads(agent, []) }.not_to change( Jobs::DigestRagUpload.jobs, :size, ) @@ -28,21 +28,21 @@ RSpec.describe RagDocumentFragment do it "queues a job for each upload to generate fragments" do expect { - described_class.link_target_and_uploads(persona, [upload_1.id, upload_2.id]) + described_class.link_target_and_uploads(agent, [upload_1.id, upload_2.id]) }.to change(Jobs::DigestRagUpload.jobs, :size).by(2) end - it "creates references between the persona an each upload" do - described_class.link_target_and_uploads(persona, [upload_1.id, upload_2.id]) + it "creates references between the agent an each upload" do + described_class.link_target_and_uploads(agent, [upload_1.id, upload_2.id]) - refs = UploadReference.where(target: persona).pluck(:upload_id) + refs = UploadReference.where(target: agent).pluck(:upload_id) expect(refs).to contain_exactly(upload_1.id, upload_2.id) end end describe ".update_target_uploads" do - it "does nothing if there is no persona" do + it "does nothing if there is no agent" do expect { described_class.update_target_uploads(nil, [upload_1.id]) }.not_to change( Jobs::DigestRagUpload.jobs, :size, @@ -50,24 +50,24 @@ RSpec.describe RagDocumentFragment do end it "deletes the fragment if its not present in the uploads list" do - fragment = Fabricate(:rag_document_fragment, target: persona) + fragment = Fabricate(:rag_document_fragment, target: agent) - described_class.update_target_uploads(persona, []) + described_class.update_target_uploads(agent, []) expect { fragment.reload }.to raise_error(ActiveRecord::RecordNotFound) end - it "delete references between the upload and the persona" do - described_class.link_target_and_uploads(persona, [upload_1.id, upload_2.id]) - described_class.update_target_uploads(persona, [upload_2.id]) + it "delete references between the upload and the agent" do + described_class.link_target_and_uploads(agent, [upload_1.id, upload_2.id]) + described_class.update_target_uploads(agent, [upload_2.id]) - refs = UploadReference.where(target: persona).pluck(:upload_id) + refs = UploadReference.where(target: agent).pluck(:upload_id) expect(refs).to contain_exactly(upload_2.id) end it "queues jobs to generate new fragments" do - expect { described_class.update_target_uploads(persona, [upload_1.id]) }.to change( + expect { described_class.update_target_uploads(agent, [upload_1.id]) }.to change( Jobs::DigestRagUpload.jobs, :size, ).by(1) @@ -78,11 +78,11 @@ RSpec.describe RagDocumentFragment do let(:vector) { DiscourseAi::Embeddings::Vector.instance } let(:rag_document_fragment_1) do - Fabricate(:rag_document_fragment, upload: upload_1, target: persona) + Fabricate(:rag_document_fragment, upload: upload_1, target: agent) end let(:rag_document_fragment_2) do - Fabricate(:rag_document_fragment, upload: upload_1, target: persona) + Fabricate(:rag_document_fragment, upload: upload_1, target: agent) end let(:expected_embedding) { [0.0038493] * vector_def.dimensions } @@ -103,8 +103,8 @@ RSpec.describe RagDocumentFragment do it "regenerates all embeddings if ai_embeddings_selected_model changes" do old_id = rag_document_fragment_1.id - UploadReference.create!(upload_id: upload_1.id, target: persona) - UploadReference.create!(upload_id: upload_2.id, target: persona) + UploadReference.create!(upload_id: upload_1.id, target: agent) + UploadReference.create!(upload_id: upload_2.id, target: agent) Sidekiq::Testing.fake! do SiteSetting.ai_embeddings_selected_model = Fabricate(:open_ai_embedding_def).id @@ -114,7 +114,7 @@ RSpec.describe RagDocumentFragment do end it "returns total, indexed and unindexed fragments for each upload" do - results = described_class.indexing_status(persona, [upload_1, upload_2]) + results = described_class.indexing_status(agent, [upload_1, upload_2]) upload_1_status = results[upload_1.id] expect(upload_1_status[:total]).to eq(2) diff --git a/spec/models/user_option_spec.rb b/spec/models/user_option_spec.rb index 34121ab9..7d40065f 100644 --- a/spec/models/user_option_spec.rb +++ b/spec/models/user_option_spec.rb @@ -4,8 +4,8 @@ RSpec.describe UserOption do fab!(:user) fab!(:llm_model) fab!(:group) - fab!(:ai_persona) do - Fabricate(:ai_persona, allowed_group_ids: [group.id], default_llm_id: llm_model.id) + fab!(:ai_agent) do + Fabricate(:ai_agent, allowed_group_ids: [group.id], default_llm_id: llm_model.id) end before do @@ -26,7 +26,7 @@ RSpec.describe UserOption do describe "#ai_search_discoveries" do before do - SiteSetting.ai_bot_discover_persona = ai_persona.id + SiteSetting.ai_bot_discover_agent = ai_agent.id group.add(user) end diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index 0346df7c..810f7b9e 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -16,8 +16,8 @@ module DiscourseAi::ChatBotHelper end end - def assign_persona_to(setting_name, allowed_group_ids) - Fabricate(:ai_persona, allowed_group_ids: allowed_group_ids).tap do |p| + def assign_agent_to(setting_name, allowed_group_ids) + Fabricate(:ai_agent, allowed_group_ids: allowed_group_ids).tap do |p| SiteSetting.public_send("#{setting_name}=", p.id) end end diff --git a/spec/requests/admin/ai_personas_controller_spec.rb b/spec/requests/admin/ai_agents_controller_spec.rb similarity index 62% rename from spec/requests/admin/ai_personas_controller_spec.rb rename to spec/requests/admin/ai_agents_controller_spec.rb index 17b7fe34..3f27b390 100644 --- a/spec/requests/admin/ai_personas_controller_spec.rb +++ b/spec/requests/admin/ai_agents_controller_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -RSpec.describe DiscourseAi::Admin::AiPersonasController do +RSpec.describe DiscourseAi::Admin::AiAgentsController do fab!(:admin) - fab!(:ai_persona) + fab!(:ai_agent) fab!(:embedding_definition) fab!(:llm_model) @@ -14,17 +14,17 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do describe "GET #index" do it "returns a success response" do - get "/admin/plugins/discourse-ai/ai-personas.json" + get "/admin/plugins/discourse-ai/ai-agents.json" expect(response).to be_successful - expect(response.parsed_body["ai_personas"].length).to eq(AiPersona.count) + expect(response.parsed_body["ai_agents"].length).to eq(AiAgent.count) expect(response.parsed_body["meta"]["tools"].length).to eq( - DiscourseAi::Personas::Persona.all_available_tools.length, + DiscourseAi::Agents::Agent.all_available_tools.length, ) end it "sideloads llms" do - get "/admin/plugins/discourse-ai/ai-personas.json" + get "/admin/plugins/discourse-ai/ai-agents.json" expect(response).to be_successful expect(response.parsed_body["meta"]["llms"]).to eq( @@ -39,38 +39,38 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end it "returns tool options with each tool" do - persona1 = Fabricate(:ai_persona, name: "search1", tools: ["SearchCommand"]) - persona2 = + agent1 = Fabricate(:ai_agent, name: "search1", tools: ["SearchCommand"]) + agent2 = Fabricate( - :ai_persona, + :ai_agent, name: "search2", tools: [["SearchCommand", { base_query: "test" }, true]], allow_topic_mentions: true, - allow_personal_messages: true, + allow_agentl_messages: true, allow_chat_channel_mentions: true, allow_chat_direct_messages: true, default_llm_id: llm_model.id, question_consolidator_llm_id: llm_model.id, forced_tool_count: 2, ) - persona2.create_user! + agent2.create_user! - get "/admin/plugins/discourse-ai/ai-personas.json" + get "/admin/plugins/discourse-ai/ai-agents.json" expect(response).to be_successful - serializer_persona1 = response.parsed_body["ai_personas"].find { |p| p["id"] == persona1.id } - serializer_persona2 = response.parsed_body["ai_personas"].find { |p| p["id"] == persona2.id } + serializer_agent1 = response.parsed_body["ai_agents"].find { |p| p["id"] == agent1.id } + serializer_agent2 = response.parsed_body["ai_agents"].find { |p| p["id"] == agent2.id } - expect(serializer_persona2["allow_topic_mentions"]).to eq(true) - expect(serializer_persona2["allow_personal_messages"]).to eq(true) - expect(serializer_persona2["allow_chat_channel_mentions"]).to eq(true) - expect(serializer_persona2["allow_chat_direct_messages"]).to eq(true) + expect(serializer_agent2["allow_topic_mentions"]).to eq(true) + expect(serializer_agent2["allow_agentl_messages"]).to eq(true) + expect(serializer_agent2["allow_chat_channel_mentions"]).to eq(true) + expect(serializer_agent2["allow_chat_direct_messages"]).to eq(true) - expect(serializer_persona2["default_llm_id"]).to eq(llm_model.id) - expect(serializer_persona2["question_consolidator_llm_id"]).to eq(llm_model.id) - expect(serializer_persona2["user_id"]).to eq(persona2.user_id) - expect(serializer_persona2["user"]["id"]).to eq(persona2.user_id) - expect(serializer_persona2["forced_tool_count"]).to eq(2) + expect(serializer_agent2["default_llm_id"]).to eq(llm_model.id) + expect(serializer_agent2["question_consolidator_llm_id"]).to eq(llm_model.id) + expect(serializer_agent2["user_id"]).to eq(agent2.user_id) + expect(serializer_agent2["user"]["id"]).to eq(agent2.user_id) + expect(serializer_agent2["forced_tool_count"]).to eq(2) tools = response.parsed_body["meta"]["tools"] search_tool = tools.find { |c| c["id"] == "Search" } @@ -100,8 +100,8 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do }, ) - expect(serializer_persona1["tools"]).to eq(["SearchCommand"]) - expect(serializer_persona2["tools"]).to eq( + expect(serializer_agent1["tools"]).to eq(["SearchCommand"]) + expect(serializer_agent2["tools"]).to eq( [["SearchCommand", { "base_query" => "test" }, true]], ) end @@ -112,12 +112,12 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do TranslationOverride.upsert!( SiteSetting.default_locale, - "discourse_ai.ai_bot.personas.general.name", + "discourse_ai.ai_bot.agents.general.name", "Général", ) TranslationOverride.upsert!( SiteSetting.default_locale, - "discourse_ai.ai_bot.personas.general.description", + "discourse_ai.ai_bot.agents.general.description", "Général Description", ) end @@ -125,44 +125,44 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do after do TranslationOverride.revert!( SiteSetting.default_locale, - "discourse_ai.ai_bot.personas.general.name", + "discourse_ai.ai_bot.agents.general.name", ) TranslationOverride.revert!( SiteSetting.default_locale, - "discourse_ai.ai_bot.personas.general.description", + "discourse_ai.ai_bot.agents.general.description", ) end - it "returns localized persona names and descriptions" do - get "/admin/plugins/discourse-ai/ai-personas.json" + it "returns localized agent names and descriptions" do + get "/admin/plugins/discourse-ai/ai-agents.json" - id = DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::General] - persona = response.parsed_body["ai_personas"].find { |p| p["id"] == id } + id = DiscourseAi::Agents::Agent.system_agents[DiscourseAi::Agents::General] + agent = response.parsed_body["ai_agents"].find { |p| p["id"] == id } - expect(persona["name"]).to eq("Général") - expect(persona["description"]).to eq("Général Description") + expect(agent["name"]).to eq("Général") + expect(agent["description"]).to eq("Général Description") end end end describe "GET #edit" do it "returns a success response" do - get "/admin/plugins/discourse-ai/ai-personas/#{ai_persona.id}/edit.json" + get "/admin/plugins/discourse-ai/ai-agents/#{ai_agent.id}/edit.json" expect(response).to be_successful - expect(response.parsed_body["ai_persona"]["name"]).to eq(ai_persona.name) + expect(response.parsed_body["ai_agent"]["name"]).to eq(ai_agent.name) end - it "includes rag uploads for each persona" do + it "includes rag uploads for each agent" do upload = Fabricate(:upload) - RagDocumentFragment.link_target_and_uploads(ai_persona, [upload.id]) + RagDocumentFragment.link_target_and_uploads(ai_agent, [upload.id]) - get "/admin/plugins/discourse-ai/ai-personas/#{ai_persona.id}/edit.json" + get "/admin/plugins/discourse-ai/ai-agents/#{ai_agent.id}/edit.json" expect(response).to be_successful - serialized_persona = response.parsed_body["ai_persona"] + serialized_agent = response.parsed_body["ai_agent"] - expect(serialized_persona.dig("rag_uploads", 0, "id")).to eq(upload.id) - expect(serialized_persona.dig("rag_uploads", 0, "original_filename")).to eq( + expect(serialized_agent.dig("rag_uploads", 0, "id")).to eq(upload.id) + expect(serialized_agent.dig("rag_uploads", 0, "original_filename")).to eq( upload.original_filename, ) end @@ -179,7 +179,7 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do top_p: 0.1, temperature: 0.5, allow_topic_mentions: true, - allow_personal_messages: true, + allow_agentl_messages: true, allow_chat_channel_mentions: true, allow_chat_direct_messages: true, default_llm_id: llm_model.id, @@ -190,44 +190,44 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do } end - it "creates a new AiPersona" do + it "creates a new AiAgent" do expect { - post "/admin/plugins/discourse-ai/ai-personas.json", - params: { ai_persona: valid_attributes }.to_json, + post "/admin/plugins/discourse-ai/ai-agents.json", + params: { ai_agent: valid_attributes }.to_json, headers: { "CONTENT_TYPE" => "application/json", } expect(response).to be_successful - persona_json = response.parsed_body["ai_persona"] + agent_json = response.parsed_body["ai_agent"] - expect(persona_json["name"]).to eq("superbot") - expect(persona_json["top_p"]).to eq(0.1) - expect(persona_json["temperature"]).to eq(0.5) - expect(persona_json["default_llm_id"]).to eq(llm_model.id) - expect(persona_json["forced_tool_count"]).to eq(2) - expect(persona_json["allow_topic_mentions"]).to eq(true) - expect(persona_json["allow_personal_messages"]).to eq(true) - expect(persona_json["allow_chat_channel_mentions"]).to eq(true) - expect(persona_json["allow_chat_direct_messages"]).to eq(true) - expect(persona_json["question_consolidator_llm_id"]).to eq(llm_model.id) - expect(persona_json["response_format"].map { |rf| rf["key"] }).to contain_exactly( + expect(agent_json["name"]).to eq("superbot") + expect(agent_json["top_p"]).to eq(0.1) + expect(agent_json["temperature"]).to eq(0.5) + expect(agent_json["default_llm_id"]).to eq(llm_model.id) + expect(agent_json["forced_tool_count"]).to eq(2) + expect(agent_json["allow_topic_mentions"]).to eq(true) + expect(agent_json["allow_agentl_messages"]).to eq(true) + expect(agent_json["allow_chat_channel_mentions"]).to eq(true) + expect(agent_json["allow_chat_direct_messages"]).to eq(true) + expect(agent_json["question_consolidator_llm_id"]).to eq(llm_model.id) + expect(agent_json["response_format"].map { |rf| rf["key"] }).to contain_exactly( "summary", ) - expect(persona_json["examples"]).to eq(valid_attributes[:examples]) + expect(agent_json["examples"]).to eq(valid_attributes[:examples]) - persona = AiPersona.find(persona_json["id"]) + agent = AiAgent.find(agent_json["id"]) - expect(persona.tools).to eq([["search", { "base_query" => "test" }, true]]) - expect(persona.top_p).to eq(0.1) - expect(persona.temperature).to eq(0.5) - }.to change(AiPersona, :count).by(1) + expect(agent.tools).to eq([["search", { "base_query" => "test" }, true]]) + expect(agent.top_p).to eq(0.1) + expect(agent.temperature).to eq(0.5) + }.to change(AiAgent, :count).by(1) end end context "with invalid params" do - it "renders a JSON response with errors for the new ai_persona" do - post "/admin/plugins/discourse-ai/ai-personas.json", params: { ai_persona: { foo: "" } } # invalid attribute + it "renders a JSON response with errors for the new ai_agent" do + post "/admin/plugins/discourse-ai/ai-agents.json", params: { ai_agent: { foo: "" } } # invalid attribute expect(response).to have_http_status(:unprocessable_entity) expect(response.content_type).to include("application/json") end @@ -235,12 +235,12 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end describe "POST #create_user" do - it "creates a user for the persona" do - post "/admin/plugins/discourse-ai/ai-personas/#{ai_persona.id}/create-user.json" - ai_persona.reload + it "creates a user for the agent" do + post "/admin/plugins/discourse-ai/ai-agents/#{ai_agent.id}/create-user.json" + ai_agent.reload expect(response).to be_successful - expect(response.parsed_body["user"]["id"]).to eq(ai_persona.user_id) + expect(response.parsed_body["user"]["id"]).to eq(ai_agent.user_id) end end @@ -252,15 +252,15 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do scope = ApiKeyScope.create!( resource: "discourse_ai", - action: "update_personas", + action: "update_agents", api_key_id: api_key.id, allowed_parameters: { }, ) - put "/admin/plugins/discourse-ai/ai-personas/#{ai_persona.id}.json", + put "/admin/plugins/discourse-ai/ai-agents/#{ai_agent.id}.json", params: { - ai_persona: { + ai_agent: { name: "UpdatedByAPI", description: "Updated via API key", }, @@ -271,15 +271,15 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do } expect(response).to have_http_status(:ok) - ai_persona.reload - expect(ai_persona.name).to eq("UpdatedByAPI") - expect(ai_persona.description).to eq("Updated via API key") + ai_agent.reload + expect(ai_agent.name).to eq("UpdatedByAPI") + expect(ai_agent.description).to eq("Updated via API key") scope.update!(action: "fake") - put "/admin/plugins/discourse-ai/ai-personas/#{ai_persona.id}.json", + put "/admin/plugins/discourse-ai/ai-agents/#{ai_agent.id}.json", params: { - ai_persona: { + ai_agent: { name: "UpdatedByAPI 2", description: "Updated via API key", }, @@ -294,28 +294,28 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end it "allows us to trivially clear top_p and temperature" do - persona = Fabricate(:ai_persona, name: "test_bot2", top_p: 0.5, temperature: 0.1) - put "/admin/plugins/discourse-ai/ai-personas/#{persona.id}.json", + agent = Fabricate(:ai_agent, name: "test_bot2", top_p: 0.5, temperature: 0.1) + put "/admin/plugins/discourse-ai/ai-agents/#{agent.id}.json", params: { - ai_persona: { + ai_agent: { top_p: "", temperature: "", }, } expect(response).to have_http_status(:ok) - persona.reload + agent.reload - expect(persona.top_p).to eq(nil) - expect(persona.temperature).to eq(nil) + expect(agent.top_p).to eq(nil) + expect(agent.temperature).to eq(nil) end it "supports updating rag params" do - persona = Fabricate(:ai_persona, name: "test_bot2") + agent = Fabricate(:ai_agent, name: "test_bot2") - put "/admin/plugins/discourse-ai/ai-personas/#{persona.id}.json", + put "/admin/plugins/discourse-ai/ai-agents/#{agent.id}.json", params: { - ai_persona: { + ai_agent: { rag_chunk_tokens: "102", rag_chunk_overlap_tokens: "12", rag_conversation_chunks: "13", @@ -325,36 +325,36 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do } expect(response).to have_http_status(:ok) - persona.reload + agent.reload - expect(persona.rag_chunk_tokens).to eq(102) - expect(persona.rag_chunk_overlap_tokens).to eq(12) - expect(persona.rag_conversation_chunks).to eq(13) - expect(persona.rag_llm_model_id).to eq(llm_model.id) - expect(persona.question_consolidator_llm_id).to eq(llm_model.id) + expect(agent.rag_chunk_tokens).to eq(102) + expect(agent.rag_chunk_overlap_tokens).to eq(12) + expect(agent.rag_conversation_chunks).to eq(13) + expect(agent.rag_llm_model_id).to eq(llm_model.id) + expect(agent.question_consolidator_llm_id).to eq(llm_model.id) end it "supports updating vision params" do - persona = Fabricate(:ai_persona, name: "test_bot2") - put "/admin/plugins/discourse-ai/ai-personas/#{persona.id}.json", + agent = Fabricate(:ai_agent, name: "test_bot2") + put "/admin/plugins/discourse-ai/ai-agents/#{agent.id}.json", params: { - ai_persona: { + ai_agent: { vision_enabled: true, vision_max_pixels: 512 * 512, }, } expect(response).to have_http_status(:ok) - persona.reload + agent.reload - expect(persona.vision_enabled).to eq(true) - expect(persona.vision_max_pixels).to eq(512 * 512) + expect(agent.vision_enabled).to eq(true) + expect(agent.vision_max_pixels).to eq(512 * 512) end - it "does not allow temperature and top p changes on stock personas" do - put "/admin/plugins/discourse-ai/ai-personas/#{DiscourseAi::Personas::Persona.system_personas.values.first}.json", + it "does not allow temperature and top p changes on stock agents" do + put "/admin/plugins/discourse-ai/ai-agents/#{DiscourseAi::Agents::Agent.system_agents.values.first}.json", params: { - ai_persona: { + ai_agent: { top_p: 0.5, temperature: 0.1, }, @@ -364,10 +364,10 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end context "with valid params" do - it "updates the requested ai_persona" do - put "/admin/plugins/discourse-ai/ai-personas/#{ai_persona.id}.json", + it "updates the requested ai_agent" do + put "/admin/plugins/discourse-ai/ai-agents/#{ai_agent.id}.json", params: { - ai_persona: { + ai_agent: { name: "SuperBot", enabled: false, tools: ["search"], @@ -377,18 +377,18 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do expect(response).to have_http_status(:ok) expect(response.content_type).to include("application/json") - ai_persona.reload - expect(ai_persona.name).to eq("SuperBot") - expect(ai_persona.enabled).to eq(false) - expect(ai_persona.tools).to eq([["search", nil, false]]) + ai_agent.reload + expect(ai_agent.name).to eq("SuperBot") + expect(ai_agent.enabled).to eq(false) + expect(ai_agent.tools).to eq([["search", nil, false]]) end end - context "with system personas" do + context "with system agents" do it "does not allow editing of system prompts" do - put "/admin/plugins/discourse-ai/ai-personas/#{DiscourseAi::Personas::Persona.system_personas.values.first}.json", + put "/admin/plugins/discourse-ai/ai-agents/#{DiscourseAi::Agents::Agent.system_agents.values.first}.json", params: { - ai_persona: { + ai_agent: { system_prompt: "you are not a helpful bot", }, } @@ -399,9 +399,9 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end it "does not allow editing of tools" do - put "/admin/plugins/discourse-ai/ai-personas/#{DiscourseAi::Personas::Persona.system_personas.values.first}.json", + put "/admin/plugins/discourse-ai/ai-agents/#{DiscourseAi::Agents::Agent.system_agents.values.first}.json", params: { - ai_persona: { + ai_agent: { tools: %w[SearchCommand ImageCommand], }, } @@ -412,9 +412,9 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end it "does not allow editing of name and description cause it is localized" do - put "/admin/plugins/discourse-ai/ai-personas/#{DiscourseAi::Personas::Persona.system_personas.values.first}.json", + put "/admin/plugins/discourse-ai/ai-agents/#{DiscourseAi::Agents::Agent.system_agents.values.first}.json", params: { - ai_persona: { + ai_agent: { name: "bob", description: "the bob", }, @@ -426,9 +426,9 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end it "does allow some actions" do - put "/admin/plugins/discourse-ai/ai-personas/#{DiscourseAi::Personas::Persona.system_personas.values.first}.json", + put "/admin/plugins/discourse-ai/ai-agents/#{DiscourseAi::Agents::Agent.system_agents.values.first}.json", params: { - ai_persona: { + ai_agent: { allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_1]], enabled: false, priority: 989, @@ -440,10 +440,10 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end context "with invalid params" do - it "renders a JSON response with errors for the ai_persona" do - put "/admin/plugins/discourse-ai/ai-personas/#{ai_persona.id}.json", + it "renders a JSON response with errors for the ai_agent" do + put "/admin/plugins/discourse-ai/ai-agents/#{ai_agent.id}.json", params: { - ai_persona: { + ai_agent: { name: "", }, } # invalid attribute @@ -454,22 +454,22 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do end describe "DELETE #destroy" do - it "destroys the requested ai_persona" do + it "destroys the requested ai_agent" do expect { - delete "/admin/plugins/discourse-ai/ai-personas/#{ai_persona.id}.json" + delete "/admin/plugins/discourse-ai/ai-agents/#{ai_agent.id}.json" expect(response).to have_http_status(:no_content) - }.to change(AiPersona, :count).by(-1) + }.to change(AiAgent, :count).by(-1) end - it "is not allowed to delete system personas" do + it "is not allowed to delete system agents" do expect { - delete "/admin/plugins/discourse-ai/ai-personas/#{DiscourseAi::Personas::Persona.system_personas.values.first}.json" + delete "/admin/plugins/discourse-ai/ai-agents/#{DiscourseAi::Agents::Agent.system_agents.values.first}.json" expect(response).to have_http_status(:unprocessable_entity) expect(response.parsed_body["errors"].join).not_to be_blank # let's make sure this is translated expect(response.parsed_body["errors"].join).not_to include("en.discourse") - }.not_to change(AiPersona, :count) + }.not_to change(AiAgent, :count) end end @@ -481,31 +481,31 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do after { fake_endpoint.reset! } - it "ensures persona exists" do - post "/admin/plugins/discourse-ai/ai-personas/stream-reply.json" + it "ensures agent exists" do + post "/admin/plugins/discourse-ai/ai-agents/stream-reply.json" expect(response).to have_http_status(:unprocessable_entity) # this ensures localization key is actually in the yaml - expect(response.body).to include("persona_name") + expect(response.body).to include("agent_name") end it "ensures question exists" do - ai_persona.update!(default_llm_id: llm.id) + ai_agent.update!(default_llm_id: llm.id) - post "/admin/plugins/discourse-ai/ai-personas/stream-reply.json", + post "/admin/plugins/discourse-ai/ai-agents/stream-reply.json", params: { - persona_id: ai_persona.id, + agent_id: ai_agent.id, user_unique_id: "site:test.com:user_id:1", } expect(response).to have_http_status(:unprocessable_entity) expect(response.body).to include("query") end - it "ensure persona has a user specified" do - ai_persona.update!(default_llm_id: llm.id) + it "ensure agent has a user specified" do + ai_agent.update!(default_llm_id: llm.id) - post "/admin/plugins/discourse-ai/ai-personas/stream-reply.json", + post "/admin/plugins/discourse-ai/ai-agents/stream-reply.json", params: { - persona_id: ai_persona.id, + agent_id: ai_agent.id, query: "how are you today?", user_unique_id: "site:test.com:user_id:1", } @@ -560,19 +560,19 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do fake_endpoint.fake_content = ["This is a test! Testing!", "An amazing title"] - ai_persona.create_user! - ai_persona.update!( + ai_agent.create_user! + ai_agent.update!( allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], default_llm_id: llm.id, - allow_personal_messages: true, + allow_agentl_messages: true, system_prompt: "you are a helpful bot", ) io_out, io_in = IO.pipe - post "/admin/plugins/discourse-ai/ai-personas/stream-reply.json", + post "/admin/plugins/discourse-ai/ai-agents/stream-reply.json", params: { - persona_name: ai_persona.name, + agent_name: ai_agent.name, query: "how are you today?", user_unique_id: "site:test.com:user_id:1", preferred_username: "test_user", @@ -600,7 +600,7 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do user_post = topic.posts.find_by(post_number: 1) expect(user_post.raw).to eq("how are you today?") - # need ai persona and user + # need ai agent and user expect(topic.topic_allowed_users.count).to eq(2) expect(topic.archetype).to eq(Archetype.private_message) expect(topic.title).to eq("An amazing title") @@ -613,7 +613,7 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do # this simplifies function calls fake_endpoint.chunk_count = 1 - ai_persona.update!(tools: ["Categories"]) + ai_agent.update!(tools: ["Categories"]) # lets also unstage the user and add the user to tl0 # this will ensure there are no feedback loops @@ -622,14 +622,14 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do Group.user_trust_level_change!(new_user.id, new_user.trust_level) # double check this happened and user is in group - personas = AiPersona.allowed_modalities(user: new_user.reload, allow_personal_messages: true) - expect(personas.count).to eq(1) + agents = AiAgent.allowed_modalities(user: new_user.reload, allow_agentl_messages: true) + expect(agents.count).to eq(1) io_out, io_in = IO.pipe - post "/admin/plugins/discourse-ai/ai-personas/stream-reply.json", + post "/admin/plugins/discourse-ai/ai-agents/stream-reply.json", params: { - persona_id: ai_persona.id, + agent_id: ai_agent.id, query: "how are you now?", user_unique_id: "site:test.com:user_id:1", preferred_username: "test_user", diff --git a/spec/requests/admin/ai_features_controller_spec.rb b/spec/requests/admin/ai_features_controller_spec.rb index 8265d856..799e88ce 100644 --- a/spec/requests/admin/ai_features_controller_spec.rb +++ b/spec/requests/admin/ai_features_controller_spec.rb @@ -5,8 +5,8 @@ RSpec.describe DiscourseAi::Admin::AiFeaturesController do fab!(:admin) fab!(:group) fab!(:llm_model) - fab!(:summarizer_persona) { Fabricate(:ai_persona) } - fab!(:alternate_summarizer_persona) { Fabricate(:ai_persona) } + fab!(:summarizer_agent) { Fabricate(:ai_agent) } + fab!(:alternate_summarizer_agent) { Fabricate(:ai_agent) } before do sign_in(admin) @@ -15,7 +15,7 @@ RSpec.describe DiscourseAi::Admin::AiFeaturesController do end describe "#index" do - it "lists all features backed by personas" do + it "lists all features backed by agents" do get "/admin/plugins/discourse-ai/ai-features.json" expect(response.status).to eq(200) diff --git a/spec/requests/admin/ai_llms_controller_spec.rb b/spec/requests/admin/ai_llms_controller_spec.rb index 280204f2..4b6c53e2 100644 --- a/spec/requests/admin/ai_llms_controller_spec.rb +++ b/spec/requests/admin/ai_llms_controller_spec.rb @@ -11,10 +11,10 @@ RSpec.describe DiscourseAi::Admin::AiLlmsController do describe "GET #index" do fab!(:llm_model) { Fabricate(:llm_model, enabled_chat_bot: true) } fab!(:llm_model2) { Fabricate(:llm_model) } - fab!(:ai_persona) do + fab!(:ai_agent) do Fabricate( - :ai_persona, - name: "Cool persona", + :ai_agent, + name: "Cool agent", force_default_llm: true, default_llm_id: llm_model2.id, ) @@ -79,7 +79,7 @@ RSpec.describe DiscourseAi::Admin::AiLlmsController do model2_json = llms.find { |m| m["id"] == llm_model2.id } expect(model2_json["used_by"]).to contain_exactly( - { "type" => "ai_persona", "name" => "Cool persona", "id" => ai_persona.id }, + { "type" => "ai_agent", "name" => "Cool agent", "id" => ai_agent.id }, { "type" => "ai_summarization" }, { "type" => "ai_embeddings_semantic_search" }, ) @@ -450,7 +450,7 @@ RSpec.describe DiscourseAi::Admin::AiLlmsController do describe "DELETE #destroy" do fab!(:llm_model) - it "destroys the requested ai_persona" do + it "destroys the requested ai_agent" do expect { delete "/admin/plugins/discourse-ai/ai-llms/#{llm_model.id}.json" diff --git a/spec/requests/admin/rag_document_fragments_controller_spec.rb b/spec/requests/admin/rag_document_fragments_controller_spec.rb index 24b4b387..aee074b2 100644 --- a/spec/requests/admin/rag_document_fragments_controller_spec.rb +++ b/spec/requests/admin/rag_document_fragments_controller_spec.rb @@ -2,7 +2,7 @@ RSpec.describe DiscourseAi::Admin::RagDocumentFragmentsController do fab!(:admin) - fab!(:ai_persona) + fab!(:ai_agent) fab!(:vector_def) { Fabricate(:embedding_definition) } @@ -15,8 +15,8 @@ RSpec.describe DiscourseAi::Admin::RagDocumentFragmentsController do after { @cleanup_files&.each(&:unlink) } describe "GET #indexing_status_check" do - it "works for AiPersona" do - get "/admin/plugins/discourse-ai/rag-document-fragments/files/status.json?target_type=AiPersona&target_id=#{ai_persona.id}" + it "works for AiAgent" do + get "/admin/plugins/discourse-ai/rag-document-fragments/files/status.json?target_type=AiAgent&target_id=#{ai_agent.id}" expect(response.parsed_body).to eq({}) expect(response.status).to eq(200) diff --git a/spec/requests/ai_bot/bot_controller_spec.rb b/spec/requests/ai_bot/bot_controller_spec.rb index 385c37e0..d2a6d108 100644 --- a/spec/requests/ai_bot/bot_controller_spec.rb +++ b/spec/requests/ai_bot/bot_controller_spec.rb @@ -127,9 +127,9 @@ RSpec.describe DiscourseAi::AiBot::BotController do before { SiteSetting.ai_bot_enabled = true } fab!(:group) - fab!(:ai_persona) { Fabricate(:ai_persona, allowed_group_ids: [group.id], default_llm_id: 1) } + fab!(:ai_agent) { Fabricate(:ai_agent, allowed_group_ids: [group.id], default_llm_id: 1) } - context "when no persona is selected" do + context "when no agent is selected" do it "returns a 403" do get "/discourse-ai/ai-bot/discover", params: { query: "What is Discourse?" } @@ -137,8 +137,8 @@ RSpec.describe DiscourseAi::AiBot::BotController do end end - context "when the user doesn't have access to the persona" do - before { SiteSetting.ai_bot_discover_persona = ai_persona.id } + context "when the user doesn't have access to the agent" do + before { SiteSetting.ai_bot_discover_agent = ai_agent.id } it "returns a 403" do get "/discourse-ai/ai-bot/discover", params: { query: "What is Discourse?" } @@ -149,7 +149,7 @@ RSpec.describe DiscourseAi::AiBot::BotController do context "when the user is allowed to use discover" do before do - SiteSetting.ai_bot_discover_persona = ai_persona.id + SiteSetting.ai_bot_discover_agent = ai_agent.id group.add(user) end @@ -173,17 +173,17 @@ RSpec.describe DiscourseAi::AiBot::BotController do before { SiteSetting.ai_bot_enabled = true } fab!(:group) fab!(:llm_model) - fab!(:ai_persona) do - persona = Fabricate(:ai_persona, allowed_group_ids: [group.id], default_llm_id: llm_model.id) - persona.create_user! - persona + fab!(:ai_agent) do + agent = Fabricate(:ai_agent, allowed_group_ids: [group.id], default_llm_id: llm_model.id) + agent.create_user! + agent end let(:query) { "What is Discourse?" } let(:context) { "Discourse is an open-source discussion platform." } context "when the user is allowed to discover" do before do - SiteSetting.ai_bot_discover_persona = ai_persona.id + SiteSetting.ai_bot_discover_agent = ai_agent.id group.add(user) end diff --git a/spec/serializers/ai_features_agent_serializer_spec.rb b/spec/serializers/ai_features_agent_serializer_spec.rb new file mode 100644 index 00000000..5663fe9a --- /dev/null +++ b/spec/serializers/ai_features_agent_serializer_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +RSpec.describe AiFeaturesAgentSerializer do + fab!(:admin) + fab!(:ai_agent) + fab!(:group) + fab!(:group_2) { Fabricate(:group) } + + describe "serialized attributes" do + before do + ai_agent.allowed_group_ids = [group.id, group_2.id] + ai_agent.save! + end + + context "when there is a agent with allowed groups" do + let(:allowed_groups) do + Group + .where(id: ai_agent.allowed_group_ids) + .pluck(:id, :name) + .map { |id, name| { id: id, name: name } } + end + + it "display every participant" do + serialized = described_class.new(ai_agent, scope: Guardian.new(admin), root: nil) + expect(serialized.id).to eq(ai_agent.id) + expect(serialized.name).to eq(ai_agent.name) + expect(serialized.system_prompt).to eq(ai_agent.system_prompt) + expect(serialized.allowed_groups).to eq(allowed_groups) + expect(serialized.enabled).to eq(ai_agent.enabled) + end + end + end +end diff --git a/spec/serializers/ai_features_persona_serializer_spec.rb b/spec/serializers/ai_features_persona_serializer_spec.rb deleted file mode 100644 index 677e2743..00000000 --- a/spec/serializers/ai_features_persona_serializer_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe AiFeaturesPersonaSerializer do - fab!(:admin) - fab!(:ai_persona) - fab!(:group) - fab!(:group_2) { Fabricate(:group) } - - describe "serialized attributes" do - before do - ai_persona.allowed_group_ids = [group.id, group_2.id] - ai_persona.save! - end - - context "when there is a persona with allowed groups" do - let(:allowed_groups) do - Group - .where(id: ai_persona.allowed_group_ids) - .pluck(:id, :name) - .map { |id, name| { id: id, name: name } } - end - - it "display every participant" do - serialized = described_class.new(ai_persona, scope: Guardian.new(admin), root: nil) - expect(serialized.id).to eq(ai_persona.id) - expect(serialized.name).to eq(ai_persona.name) - expect(serialized.system_prompt).to eq(ai_persona.system_prompt) - expect(serialized.allowed_groups).to eq(allowed_groups) - expect(serialized.enabled).to eq(ai_persona.enabled) - end - end - end -end diff --git a/spec/system/admin_ai_agent_spec.rb b/spec/system/admin_ai_agent_spec.rb new file mode 100644 index 00000000..d3d2b51e --- /dev/null +++ b/spec/system/admin_ai_agent_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +RSpec.describe "Admin AI agent configuration", type: :system, js: true do + fab!(:admin) + let(:page_header) { PageObjects::Components::DPageHeader.new } + let(:form) { PageObjects::Components::FormKit.new("form") } + + before do + SiteSetting.ai_bot_enabled = true + sign_in(admin) + end + + it "allows creation of a agent" do + visit "/admin/plugins/discourse-ai/ai-agents" + + expect(page_header).to be_visible + + find(".ai-agent-list-editor__new-button").click() + + expect(page_header).to be_hidden + + form.field("name").fill_in("Test Agent") + form.field("description").fill_in("I am a test agent") + form.field("system_prompt").fill_in("You are a helpful bot") + + tool_selector = PageObjects::Components::SelectKit.new("#control-tools .select-kit") + tool_selector.expand + tool_selector.select_row_by_value("Read") + tool_selector.select_row_by_value("ListCategories") + tool_selector.collapse + + tool_selector = PageObjects::Components::SelectKit.new("#control-forcedTools .select-kit") + tool_selector.expand + tool_selector.select_row_by_value("ListCategories") + tool_selector.select_row_by_value("Read") + tool_selector.collapse + + form.field("forced_tool_count").select(1) + + form.submit + + expect(page).not_to have_current_path("/admin/plugins/discourse-ai/ai-agents/new") + + agent_id = page.current_path.split("/")[-2].to_i + + agent = AiAgent.find(agent_id) + expect(agent.name).to eq("Test Agent") + expect(agent.description).to eq("I am a test agent") + expect(agent.system_prompt).to eq("You are a helpful bot") + expect(agent.forced_tool_count).to eq(1) + + expected_tools = [["Read", { "read_private" => nil }, true], ["ListCategories", {}, true]] + expect(agent.tools).to contain_exactly(*expected_tools) + end + + it "will not allow deletion or editing of system agents" do + visit "/admin/plugins/discourse-ai/ai-agents/#{DiscourseAi::Agents::Agent.system_agents.values.first}/edit" + expect(page).not_to have_selector(".ai-agent-editor__delete") + expect(form.field("system_prompt")).to be_disabled + end + + it "will enable agent right away when you click on enable but does not save side effects" do + agent = Fabricate(:ai_agent, enabled: false) + + visit "/admin/plugins/discourse-ai/ai-agents/#{agent.id}/edit" + + form.field("name").fill_in("Test Agent 1") + form.field("enabled").toggle + + try_until_success { expect(agent.reload.enabled).to eq(true) } + + agent.reload + expect(agent.enabled).to eq(true) + expect(agent.name).not_to eq("Test Agent 1") + end + + it "enabling a agent doesn't reset other fields" do + agent = Fabricate(:ai_agent, enabled: false) + updated_name = "Update agent 1" + + visit "/admin/plugins/discourse-ai/ai-agents/#{agent.id}/edit" + + form.field("name").fill_in(updated_name) + form.field("enabled").toggle + + try_until_success { expect(agent.reload.enabled).to eq(true) } + + expect(form.field("name").value).to eq(updated_name) + end + + it "toggling a agent's priority doesn't reset other fields" do + agent = Fabricate(:ai_agent, priority: false) + updated_name = "Update agent 1" + + visit "/admin/plugins/discourse-ai/ai-agents/#{agent.id}/edit" + + form.field("name").fill_in(updated_name) + form.field("priority").toggle + + try_until_success { expect(agent.reload.priority).to eq(true) } + + expect(form.field("name").value).to eq(updated_name) + end + + it "can navigate the AI plugin with breadcrumbs" do + visit "/admin/plugins/discourse-ai/ai-agents" + expect(page).to have_css(".d-breadcrumbs") + expect(page).to have_css(".d-breadcrumbs__item", count: 4) + find(".d-breadcrumbs__item", text: I18n.t("admin_js.admin.plugins.title")).click + expect(page).to have_current_path("/admin/plugins") + end +end diff --git a/spec/system/admin_ai_features_spec.rb b/spec/system/admin_ai_features_spec.rb index 613cd78c..51c068a0 100644 --- a/spec/system/admin_ai_features_spec.rb +++ b/spec/system/admin_ai_features_spec.rb @@ -3,7 +3,7 @@ RSpec.describe "Admin AI features configuration", type: :system, js: true do fab!(:admin) fab!(:llm_model) - fab!(:summarization_persona) { Fabricate(:ai_persona) } + fab!(:summarization_agent) { Fabricate(:ai_agent) } fab!(:group_1) { Fabricate(:group) } fab!(:group_2) { Fabricate(:group) } let(:page_header) { PageObjects::Components::DPageHeader.new } @@ -11,15 +11,15 @@ RSpec.describe "Admin AI features configuration", type: :system, js: true do let(:ai_features_page) { PageObjects::Pages::AdminAiFeatures.new } before do - summarization_persona.allowed_group_ids = [group_1.id, group_2.id] - summarization_persona.save! + summarization_agent.allowed_group_ids = [group_1.id, group_2.id] + summarization_agent.save! assign_fake_provider_to(:ai_summarization_model) SiteSetting.ai_summarization_enabled = true - SiteSetting.ai_summarization_persona = summarization_persona.id + SiteSetting.ai_summarization_agent = summarization_agent.id sign_in(admin) end - it "lists all persona backed AI features separated by configured/unconfigured" do + it "lists all agent backed AI features separated by configured/unconfigured" do ai_features_page.visit expect( ai_features_page @@ -32,9 +32,9 @@ RSpec.describe "Admin AI features configuration", type: :system, js: true do expect(ai_features_page).to have_unconfigured_feature_items(3) end - it "lists the persona used for the corresponding AI feature" do + it "lists the agent used for the corresponding AI feature" do ai_features_page.visit - expect(ai_features_page).to have_feature_persona(summarization_persona.name) + expect(ai_features_page).to have_feature_agent(summarization_agent.name) end it "lists the groups allowed to use the AI feature" do diff --git a/spec/system/admin_ai_persona_spec.rb b/spec/system/admin_ai_persona_spec.rb deleted file mode 100644 index add89b4d..00000000 --- a/spec/system/admin_ai_persona_spec.rb +++ /dev/null @@ -1,112 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe "Admin AI persona configuration", type: :system, js: true do - fab!(:admin) - let(:page_header) { PageObjects::Components::DPageHeader.new } - let(:form) { PageObjects::Components::FormKit.new("form") } - - before do - SiteSetting.ai_bot_enabled = true - sign_in(admin) - end - - it "allows creation of a persona" do - visit "/admin/plugins/discourse-ai/ai-personas" - - expect(page_header).to be_visible - - find(".ai-persona-list-editor__new-button").click() - - expect(page_header).to be_hidden - - form.field("name").fill_in("Test Persona") - form.field("description").fill_in("I am a test persona") - form.field("system_prompt").fill_in("You are a helpful bot") - - tool_selector = PageObjects::Components::SelectKit.new("#control-tools .select-kit") - tool_selector.expand - tool_selector.select_row_by_value("Read") - tool_selector.select_row_by_value("ListCategories") - tool_selector.collapse - - tool_selector = PageObjects::Components::SelectKit.new("#control-forcedTools .select-kit") - tool_selector.expand - tool_selector.select_row_by_value("ListCategories") - tool_selector.select_row_by_value("Read") - tool_selector.collapse - - form.field("forced_tool_count").select(1) - - form.submit - - expect(page).not_to have_current_path("/admin/plugins/discourse-ai/ai-personas/new") - - persona_id = page.current_path.split("/")[-2].to_i - - persona = AiPersona.find(persona_id) - expect(persona.name).to eq("Test Persona") - expect(persona.description).to eq("I am a test persona") - expect(persona.system_prompt).to eq("You are a helpful bot") - expect(persona.forced_tool_count).to eq(1) - - expected_tools = [["Read", { "read_private" => nil }, true], ["ListCategories", {}, true]] - expect(persona.tools).to contain_exactly(*expected_tools) - end - - it "will not allow deletion or editing of system personas" do - visit "/admin/plugins/discourse-ai/ai-personas/#{DiscourseAi::Personas::Persona.system_personas.values.first}/edit" - expect(page).not_to have_selector(".ai-persona-editor__delete") - expect(form.field("system_prompt")).to be_disabled - end - - it "will enable persona right away when you click on enable but does not save side effects" do - persona = Fabricate(:ai_persona, enabled: false) - - visit "/admin/plugins/discourse-ai/ai-personas/#{persona.id}/edit" - - form.field("name").fill_in("Test Persona 1") - form.field("enabled").toggle - - try_until_success { expect(persona.reload.enabled).to eq(true) } - - persona.reload - expect(persona.enabled).to eq(true) - expect(persona.name).not_to eq("Test Persona 1") - end - - it "enabling a persona doesn't reset other fields" do - persona = Fabricate(:ai_persona, enabled: false) - updated_name = "Update persona 1" - - visit "/admin/plugins/discourse-ai/ai-personas/#{persona.id}/edit" - - form.field("name").fill_in(updated_name) - form.field("enabled").toggle - - try_until_success { expect(persona.reload.enabled).to eq(true) } - - expect(form.field("name").value).to eq(updated_name) - end - - it "toggling a persona's priority doesn't reset other fields" do - persona = Fabricate(:ai_persona, priority: false) - updated_name = "Update persona 1" - - visit "/admin/plugins/discourse-ai/ai-personas/#{persona.id}/edit" - - form.field("name").fill_in(updated_name) - form.field("priority").toggle - - try_until_success { expect(persona.reload.priority).to eq(true) } - - expect(form.field("name").value).to eq(updated_name) - end - - it "can navigate the AI plugin with breadcrumbs" do - visit "/admin/plugins/discourse-ai/ai-personas" - expect(page).to have_css(".d-breadcrumbs") - expect(page).to have_css(".d-breadcrumbs__item", count: 4) - find(".d-breadcrumbs__item", text: I18n.t("admin_js.admin.plugins.title")).click - expect(page).to have_current_path("/admin/plugins") - end -end diff --git a/spec/system/ai_bot/agent_spec.rb b/spec/system/ai_bot/agent_spec.rb new file mode 100644 index 00000000..31e38c4d --- /dev/null +++ b/spec/system/ai_bot/agent_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +RSpec.describe "AI agents", type: :system, js: true do + fab!(:admin) + fab!(:gpt_4) { Fabricate(:llm_model, name: "gpt-4") } + + before do + SiteSetting.ai_bot_enabled = true + toggle_enabled_bots(bots: [gpt_4]) + sign_in(admin) + end + + it "remembers the last selected agent" do + visit "/" + find(".d-header .ai-bot-button").click() + agent_selector = + PageObjects::Components::SelectKit.new(".agent-llm-selector__agent-dropdown") + + id = DiscourseAi::Agents::Agent.all(user: admin).first.id + + expect(agent_selector).to have_selected_value(id) + + agent_selector.expand + agent_selector.select_row_by_value(-2) + + visit "/" + find(".d-header .ai-bot-button").click() + agent_selector = + PageObjects::Components::SelectKit.new(".agent-llm-selector__agent-dropdown") + agent_selector.expand + expect(agent_selector).to have_selected_value(-2) + end +end diff --git a/spec/system/ai_bot/ai_bot_helper_spec.rb b/spec/system/ai_bot/ai_bot_helper_spec.rb index b777c45a..1f44adf6 100644 --- a/spec/system/ai_bot/ai_bot_helper_spec.rb +++ b/spec/system/ai_bot/ai_bot_helper_spec.rb @@ -24,24 +24,24 @@ RSpec.describe "AI chat channel summarization", type: :system, js: true do group.add(user) group.save - allowed_persona = AiPersona.last - allowed_persona.update!(allowed_group_ids: [group.id], enabled: true) + allowed_agent = AiAgent.last + allowed_agent.update!(allowed_group_ids: [group.id], enabled: true) visit "/latest" expect(page).to have_selector(".ai-bot-button") find(".ai-bot-button").click - find(".gpt-persona").click - expect(page).to have_css(".gpt-persona ul li", count: 1) + find(".gpt-agent").click + expect(page).to have_css(".gpt-agent ul li", count: 1) find(".llm-selector").click expect(page).to have_css(".llm-selector ul li", count: 2) expect(page).to have_selector(".d-editor-container") - # lets disable bots but still allow 1 persona - allowed_persona.create_user! - allowed_persona.update!(default_llm_id: gpt_4.id) + # lets disable bots but still allow 1 agent + allowed_agent.create_user! + allowed_agent.update!(default_llm_id: gpt_4.id) gpt_4.update!(enabled_chat_bot: false) gpt_3_5_turbo.update!(enabled_chat_bot: false) @@ -49,8 +49,8 @@ RSpec.describe "AI chat channel summarization", type: :system, js: true do visit "/latest" find(".ai-bot-button").click - find(".gpt-persona").click - expect(page).to have_css(".gpt-persona ul li", count: 1) + find(".gpt-agent").click + expect(page).to have_css(".gpt-agent ul li", count: 1) expect(page).not_to have_selector(".llm-selector") SiteSetting.ai_bot_add_to_header = false diff --git a/spec/system/ai_bot/homepage_spec.rb b/spec/system/ai_bot/homepage_spec.rb index 3510c32f..8428edde 100644 --- a/spec/system/ai_bot/homepage_spec.rb +++ b/spec/system/ai_bot/homepage_spec.rb @@ -36,12 +36,12 @@ RSpec.describe "AI Bot - Homepage", type: :system do claude_2.reload.user end fab!(:bot) do - persona = - AiPersona - .find(DiscourseAi::Personas::Persona.system_personas[DiscourseAi::Personas::General]) + agent = + AiAgent + .find(DiscourseAi::Agents::Agent.system_agents[DiscourseAi::Agents::General]) .class_instance .new - DiscourseAi::Personas::Bot.as(bot_user, persona: persona) + DiscourseAi::Agents::Bot.as(bot_user, agent: agent) end fab!(:pm) do @@ -74,23 +74,23 @@ RSpec.describe "AI Bot - Homepage", type: :system do fab!(:topic_user) { Fabricate(:topic_user, topic: pm, user: user) } fab!(:topic_bot_user) { Fabricate(:topic_user, topic: pm, user: bot_user) } - fab!(:persona) do - persona = - AiPersona.create!( - name: "Test Persona", - description: "A test persona", + fab!(:agent) do + agent = + AiAgent.create!( + name: "Test Agent", + description: "A test agent", allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], enabled: true, system_prompt: "You are a helpful bot", ) - persona.create_user! - persona.update!( + agent.create_user! + agent.update!( default_llm_id: claude_2.id, allow_chat_channel_mentions: true, allow_topic_mentions: true, ) - persona + agent end before do @@ -334,7 +334,7 @@ RSpec.describe "AI Bot - Homepage", type: :system do expect(ai_pm_homepage).to have_empty_state end - it "Allows choosing persona and LLM" do + it "Allows choosing agent and LLM" do ai_pm_homepage.visit ai_pm_homepage.llm_selector.expand diff --git a/spec/system/ai_bot/persona_spec.rb b/spec/system/ai_bot/persona_spec.rb deleted file mode 100644 index 1cf5e231..00000000 --- a/spec/system/ai_bot/persona_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe "AI personas", type: :system, js: true do - fab!(:admin) - fab!(:gpt_4) { Fabricate(:llm_model, name: "gpt-4") } - - before do - SiteSetting.ai_bot_enabled = true - toggle_enabled_bots(bots: [gpt_4]) - sign_in(admin) - end - - it "remembers the last selected persona" do - visit "/" - find(".d-header .ai-bot-button").click() - persona_selector = - PageObjects::Components::SelectKit.new(".persona-llm-selector__persona-dropdown") - - id = DiscourseAi::Personas::Persona.all(user: admin).first.id - - expect(persona_selector).to have_selected_value(id) - - persona_selector.expand - persona_selector.select_row_by_value(-2) - - visit "/" - find(".d-header .ai-bot-button").click() - persona_selector = - PageObjects::Components::SelectKit.new(".persona-llm-selector__persona-dropdown") - persona_selector.expand - expect(persona_selector).to have_selected_value(-2) - end -end diff --git a/spec/system/ai_bot/share_spec.rb b/spec/system/ai_bot/share_spec.rb index 9e8c6803..43a99a26 100644 --- a/spec/system/ai_bot/share_spec.rb +++ b/spec/system/ai_bot/share_spec.rb @@ -43,14 +43,14 @@ RSpec.describe "Share conversation", type: :system do page.execute_script("window.navigator.clipboard.writeText('')") end - it "can share a conversation with a persona user" do + it "can share a conversation with a agent user" do clip_text = nil - persona = Fabricate(:ai_persona, name: "Tester") - persona.create_user! + agent = Fabricate(:ai_agent, name: "Tester") + agent.create_user! Fabricate(:post, topic: pm, user: admin, raw: "How do I do stuff?") - Fabricate(:post, topic: pm, user: persona.user, raw: "No idea") + Fabricate(:post, topic: pm, user: agent.user, raw: "No idea") visit(pm.url) diff --git a/spec/system/ai_bot/tool_spec.rb b/spec/system/ai_bot/tool_spec.rb index b1bc90fd..6cd30f75 100644 --- a/spec/system/ai_bot/tool_spec.rb +++ b/spec/system/ai_bot/tool_spec.rb @@ -65,7 +65,7 @@ describe "AI Tool Management", type: :system do expect(page.first(required_toggle_css).checked?).to eq(true) expect(page.first(enum_toggle_css).checked?).to eq(false) - visit "/admin/plugins/discourse-ai/ai-personas/new" + visit "/admin/plugins/discourse-ai/ai-agents/new" tool_id = AiTool.order("id desc").limit(1).pluck(:id).first tool_selector = PageObjects::Components::SelectKit.new("#control-tools .select-kit") diff --git a/spec/system/ai_helper/ai_split_topic_suggestion_spec.rb b/spec/system/ai_helper/ai_split_topic_suggestion_spec.rb index 12a1109d..b12de867 100644 --- a/spec/system/ai_helper/ai_split_topic_suggestion_spec.rb +++ b/spec/system/ai_helper/ai_split_topic_suggestion_spec.rb @@ -18,7 +18,7 @@ RSpec.describe "AI Post helper", type: :system, js: true do Fabricate( :post, topic: topic, - raw: "I prefer to eat croissants. They are my personal favorite dessert!", + raw: "I prefer to eat croissants. They are my agentl favorite dessert!", ) end fab!(:post_3) do diff --git a/spec/system/page_objects/components/ai_pm_homepage.rb b/spec/system/page_objects/components/ai_pm_homepage.rb index 69b93af3..ceee2b64 100644 --- a/spec/system/page_objects/components/ai_pm_homepage.rb +++ b/spec/system/page_objects/components/ai_pm_homepage.rb @@ -23,7 +23,7 @@ module PageObjects text: I18n.t( "js.discourse_ai.ai_bot.conversations.min_input_length_message", - count: SiteSetting.min_personal_message_post_length, + count: SiteSetting.min_agentl_message_post_length, ), ) end @@ -62,12 +62,12 @@ module PageObjects ).click end - def persona_selector - PageObjects::Components::SelectKit.new(".persona-llm-selector__persona-dropdown") + def agent_selector + PageObjects::Components::SelectKit.new(".agent-llm-selector__agent-dropdown") end def llm_selector - PageObjects::Components::SelectKit.new(".persona-llm-selector__llm-dropdown") + PageObjects::Components::SelectKit.new(".agent-llm-selector__llm-dropdown") end def has_sidebar_back_link? diff --git a/spec/system/page_objects/pages/admin_ai_features.rb b/spec/system/page_objects/pages/admin_ai_features.rb index e6ad9efd..dcf374cb 100644 --- a/spec/system/page_objects/pages/admin_ai_features.rb +++ b/spec/system/page_objects/pages/admin_ai_features.rb @@ -27,9 +27,9 @@ module PageObjects page.has_css?("#{UNCONFIGURED_FEATURES_TABLE} .ai-feature-list__row", count: count) end - def has_feature_persona?(name) + def has_feature_agent?(name) page.has_css?( - "#{CONFIGURED_FEATURES_TABLE} .ai-feature-list__persona .d-button-label ", + "#{CONFIGURED_FEATURES_TABLE} .ai-feature-list__agent .d-button-label ", text: name, ) end diff --git a/spec/system/summarization/chat_summarization_spec.rb b/spec/system/summarization/chat_summarization_spec.rb index dbb78417..3f5b8050 100644 --- a/spec/system/summarization/chat_summarization_spec.rb +++ b/spec/system/summarization/chat_summarization_spec.rb @@ -12,7 +12,7 @@ RSpec.describe "Summarize a channel since your last visit", type: :system do group.add(current_user) assign_fake_provider_to(:ai_summarization_model) - assign_persona_to(:ai_summarization_persona, [group.id]) + assign_agent_to(:ai_summarization_agent, [group.id]) SiteSetting.ai_summarization_enabled = true SiteSetting.chat_enabled = true diff --git a/spec/system/summarization/topic_summarization_spec.rb b/spec/system/summarization/topic_summarization_spec.rb index 30b147a0..a9fccd32 100644 --- a/spec/system/summarization/topic_summarization_spec.rb +++ b/spec/system/summarization/topic_summarization_spec.rb @@ -24,7 +24,7 @@ RSpec.describe "Summarize a topic ", type: :system do group.add(current_user) assign_fake_provider_to(:ai_summarization_model) - assign_persona_to(:ai_summarization_persona, [group.id]) + assign_agent_to(:ai_summarization_agent, [group.id]) SiteSetting.ai_summarization_enabled = true sign_in(current_user) diff --git a/test/javascripts/unit/models/ai-persona-test.js b/test/javascripts/unit/models/ai-agent-test.js similarity index 64% rename from test/javascripts/unit/models/ai-persona-test.js rename to test/javascripts/unit/models/ai-agent-test.js index a21d51a2..c4c83484 100644 --- a/test/javascripts/unit/models/ai-persona-test.js +++ b/test/javascripts/unit/models/ai-agent-test.js @@ -1,7 +1,7 @@ import { module, test } from "qunit"; -import AiPersona from "discourse/plugins/discourse-ai/discourse/admin/models/ai-persona"; +import AiAgent from "discourse/plugins/discourse-ai/discourse/admin/models/ai-agent"; -module("Discourse AI | Unit | Model | ai-persona", function () { +module("Discourse AI | Unit | Model | ai-agent", function () { test("toPOJO", function (assert) { const properties = { tools: [ @@ -11,15 +11,15 @@ module("Discourse AI | Unit | Model | ai-persona", function () { ], }; - const aiPersonaPOJO = AiPersona.create(properties).toPOJO(); + const aiAgentPOJO = AiAgent.create(properties).toPOJO(); - assert.deepEqual(aiPersonaPOJO.tools, [ + assert.deepEqual(aiAgentPOJO.tools, [ "ToolName", "ToolName2", "ToolName3", ]); - assert.equal(aiPersonaPOJO.toolOptions["ToolName"].option1, "value1"); - assert.equal(aiPersonaPOJO.toolOptions["ToolName"].option2, "value2"); + assert.equal(aiAgentPOJO.toolOptions["ToolName"].option1, "value1"); + assert.equal(aiAgentPOJO.toolOptions["ToolName"].option2, "value2"); }); test("fromPOJO", function (assert) { @@ -51,23 +51,23 @@ module("Discourse AI | Unit | Model | ai-persona", function () { allow_chat: false, tool_details: true, forced_tool_count: -1, - allow_personal_messages: true, + allow_agentl_messages: true, allow_topic_mentions: true, allow_chat_channel_mentions: true, allow_chat_direct_messages: true, }; const updatedValue = "updated"; - const aiPersona = AiPersona.create({ ...properties }); + const aiAgent = AiAgent.create({ ...properties }); - const personaPOJO = aiPersona.toPOJO(); + const agentPOJO = aiAgent.toPOJO(); - personaPOJO.toolOptions["ToolName"].option1 = updatedValue; - personaPOJO.forcedTools = "ToolName"; + agentPOJO.toolOptions["ToolName"].option1 = updatedValue; + agentPOJO.forcedTools = "ToolName"; - const updatedPersona = aiPersona.fromPOJO(personaPOJO); + const updatedAgent = aiAgent.fromPOJO(agentPOJO); - assert.deepEqual(updatedPersona.tools, [ + assert.deepEqual(updatedAgent.tools, [ ["ToolName", { option1: updatedValue }, true], ]); });