FEATURE: optional tool detail blocks (#662)

This is a rather huge refactor with 1 new feature (tool details can
be suppressed)

Previously we use the name "Command" to describe "Tools", this unifies
all the internal language and simplifies the code.

We also amended the persona UI to use less DToggles which aligns
with our design guidelines.

Co-authored-by: Martin Brennan <martin@discourse.org>
This commit is contained in:
Sam 2024-06-11 18:14:14 +10:00 committed by GitHub
parent 875bb04467
commit 52a7dd2a4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
85 changed files with 495 additions and 574 deletions

View File

@ -23,7 +23,7 @@ module DiscourseAi
DiscourseAi::Configuration::LlmEnumerator.values.map do |hash| DiscourseAi::Configuration::LlmEnumerator.values.map do |hash|
{ id: hash[:value], name: hash[:name] } { id: hash[:value], name: hash[:name] }
end end
render json: { ai_personas: ai_personas, meta: { commands: tools, llms: llms } } render json: { ai_personas: ai_personas, meta: { tools: tools, llms: llms } }
end end
def show def show
@ -126,28 +126,29 @@ module DiscourseAi
:rag_conversation_chunks, :rag_conversation_chunks,
:question_consolidator_llm, :question_consolidator_llm,
:allow_chat, :allow_chat,
:tool_details,
allowed_group_ids: [], allowed_group_ids: [],
rag_uploads: [:id], rag_uploads: [:id],
) )
if commands = params.dig(:ai_persona, :commands) if tools = params.dig(:ai_persona, :tools)
permitted[:commands] = permit_commands(commands) permitted[:tools] = permit_tools(tools)
end end
permitted permitted
end end
def permit_commands(commands) def permit_tools(tools)
return [] if !commands.is_a?(Array) return [] if !tools.is_a?(Array)
commands.filter_map do |command, options| tools.filter_map do |tool, options|
break nil if !command.is_a?(String) break nil if !tool.is_a?(String)
options&.permit! if options && options.is_a?(ActionController::Parameters) options&.permit! if options && options.is_a?(ActionController::Parameters)
if options if options
[command, options] [tool, options]
else else
command tool
end end
end end
end end

View File

@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class AiPersona < ActiveRecord::Base class AiPersona < ActiveRecord::Base
# TODO remove this line 01-11-2024
self.ignored_columns = [:commands]
# places a hard limit, so per site we cache a maximum of 500 classes # places a hard limit, so per site we cache a maximum of 500 classes
MAX_PERSONAS_PER_SITE = 500 MAX_PERSONAS_PER_SITE = 500
@ -102,96 +105,49 @@ class AiPersona < ActiveRecord::Base
end end
def class_instance def class_instance
allowed_group_ids = self.allowed_group_ids attributes = %i[
id = self.id id
system = self.system user_id
user_id = self.user_id system
mentionable = self.mentionable mentionable
default_llm = self.default_llm default_llm
max_context_posts = self.max_context_posts max_context_posts
vision_enabled = self.vision_enabled vision_enabled
vision_max_pixels = self.vision_max_pixels vision_max_pixels
rag_conversation_chunks = self.rag_conversation_chunks rag_conversation_chunks
question_consolidator_llm = self.question_consolidator_llm question_consolidator_llm
allow_chat = self.allow_chat allow_chat
name
description
allowed_group_ids
tool_details
]
persona_class = DiscourseAi::AiBot::Personas::Persona.system_personas_by_id[self.id] persona_class = DiscourseAi::AiBot::Personas::Persona.system_personas_by_id[self.id]
instance_attributes = {}
attributes.each do |attr|
value = self.read_attribute(attr)
instance_attributes[attr] = value
end
if persona_class if persona_class
persona_class.define_singleton_method :allowed_group_ids do instance_attributes.each do |key, value|
allowed_group_ids # description/name are localized
persona_class.define_singleton_method(key) { value } if key != :description && key != :name
end end
persona_class.define_singleton_method :id do
id
end
persona_class.define_singleton_method :system do
system
end
persona_class.define_singleton_method :user_id do
user_id
end
persona_class.define_singleton_method :allow_chat do
allow_chat
end
persona_class.define_singleton_method :mentionable do
mentionable
end
persona_class.define_singleton_method :default_llm do
default_llm
end
persona_class.define_singleton_method :max_context_posts do
max_context_posts
end
persona_class.define_singleton_method :vision_enabled do
vision_enabled
end
persona_class.define_singleton_method :vision_max_pixels do
vision_max_pixels
end
persona_class.define_singleton_method :question_consolidator_llm do
question_consolidator_llm
end
persona_class.define_singleton_method :rag_conversation_chunks do
rag_conversation_chunks
end
return persona_class return persona_class
end end
name = self.name
description = self.description
ai_persona_id = self.id
options = {} options = {}
tools = self.respond_to?(:commands) ? self.commands : self.tools
tools = tools =
tools.filter_map do |element| self.tools.filter_map do |element|
inner_name = element inner_name, current_options = element.is_a?(Array) ? element : [element, nil]
current_options = nil inner_name = inner_name.gsub("Tool", "")
if element.is_a?(Array)
inner_name = element[0]
current_options = element[1]
end
# Won't migrate data yet. Let's rewrite to the tool name.
inner_name = inner_name.gsub("Command", "")
inner_name = "List#{inner_name}" if %w[Categories Tags].include?(inner_name) inner_name = "List#{inner_name}" if %w[Categories Tags].include?(inner_name)
begin begin
klass = ("DiscourseAi::AiBot::Tools::#{inner_name}").constantize klass = "DiscourseAi::AiBot::Tools::#{inner_name}".constantize
options[klass] = current_options if current_options options[klass] = current_options if current_options
klass klass
rescue StandardError rescue StandardError
@ -199,107 +155,28 @@ class AiPersona < ActiveRecord::Base
end end
end end
ai_persona_id = self.id
Class.new(DiscourseAi::AiBot::Personas::Persona) do Class.new(DiscourseAi::AiBot::Personas::Persona) do
define_singleton_method :id do instance_attributes.each { |key, value| define_singleton_method(key) { value } }
id
define_singleton_method(:to_s) do
"#<#{self.class.name} @name=#{name} @allowed_group_ids=#{allowed_group_ids.join(",")}>"
end end
define_singleton_method :name do define_singleton_method(:inspect) { to_s }
name
end
define_singleton_method :user_id do define_method(:initialize) do |*args, **kwargs|
user_id
end
define_singleton_method :description do
description
end
define_singleton_method :system do
system
end
define_singleton_method :allowed_group_ids do
allowed_group_ids
end
define_singleton_method :user_id do
user_id
end
define_singleton_method :mentionable do
mentionable
end
define_singleton_method :default_llm do
default_llm
end
define_singleton_method :max_context_posts do
max_context_posts
end
define_singleton_method :vision_enabled do
vision_enabled
end
define_singleton_method :vision_max_pixels do
vision_max_pixels
end
define_singleton_method :rag_conversation_chunks do
rag_conversation_chunks
end
define_singleton_method :question_consolidator_llm do
question_consolidator_llm
end
define_singleton_method :allow_chat do
allow_chat
end
define_singleton_method :to_s do
"#<DiscourseAi::AiBot::Personas::Persona::Custom @name=#{self.name} @allowed_group_ids=#{self.allowed_group_ids.join(",")}>"
end
define_singleton_method :inspect do
"#<DiscourseAi::AiBot::Personas::Persona::Custom @name=#{self.name} @allowed_group_ids=#{self.allowed_group_ids.join(",")}>"
end
define_method :initialize do |*args, **kwargs|
@ai_persona = AiPersona.find_by(id: ai_persona_id) @ai_persona = AiPersona.find_by(id: ai_persona_id)
super(*args, **kwargs) super(*args, **kwargs)
end end
define_method :persona_id do define_method(:tools) { tools }
@ai_persona&.id define_method(:options) { options }
end define_method(:temperature) { @ai_persona&.temperature }
define_method(:top_p) { @ai_persona&.top_p }
define_method :tools do define_method(:system_prompt) { @ai_persona&.system_prompt || "You are a helpful bot." }
tools define_method(:uploads) { @ai_persona&.uploads }
end
define_method :options do
options
end
define_method :temperature do
@ai_persona&.temperature
end
define_method :top_p do
@ai_persona&.top_p
end
define_method :system_prompt do
@ai_persona&.system_prompt || "You are a helpful bot."
end
define_method :uploads do
@ai_persona&.uploads
end
end end
end end
@ -357,7 +234,7 @@ class AiPersona < ActiveRecord::Base
end end
def system_persona_unchangeable def system_persona_unchangeable
if top_p_changed? || temperature_changed? || system_prompt_changed? || commands_changed? || if top_p_changed? || temperature_changed? || system_prompt_changed? || tools_changed? ||
name_changed? || description_changed? name_changed? || description_changed?
errors.add(:base, I18n.t("discourse_ai.ai_bot.personas.cannot_edit_system_persona")) errors.add(:base, I18n.t("discourse_ai.ai_bot.personas.cannot_edit_system_persona"))
end end
@ -378,7 +255,7 @@ end
# id :bigint not null, primary key # id :bigint not null, primary key
# name :string(100) not null # name :string(100) not null
# description :string(2000) not null # description :string(2000) not null
# commands :json not null # tools :json not null
# system_prompt :string(10000000) not null # system_prompt :string(10000000) not null
# allowed_group_ids :integer default([]), not null, is an Array # allowed_group_ids :integer default([]), not null, is an Array
# created_by_id :integer # created_by_id :integer
@ -408,6 +285,7 @@ end
# role_max_responses_per_hour :integer default(50), not null # role_max_responses_per_hour :integer default(50), not null
# question_consolidator_llm :text # question_consolidator_llm :text
# allow_chat :boolean default(FALSE), not null # allow_chat :boolean default(FALSE), not null
# tool_details :boolean default(TRUE), not null
# #
# Indexes # Indexes
# #

View File

@ -9,7 +9,7 @@ class LocalizedAiPersonaSerializer < ApplicationSerializer
:enabled, :enabled,
:system, :system,
:priority, :priority,
:commands, :tools,
:system_prompt, :system_prompt,
:allowed_group_ids, :allowed_group_ids,
:temperature, :temperature,
@ -24,7 +24,8 @@ class LocalizedAiPersonaSerializer < ApplicationSerializer
:rag_chunk_overlap_tokens, :rag_chunk_overlap_tokens,
:rag_conversation_chunks, :rag_conversation_chunks,
:question_consolidator_llm, :question_consolidator_llm,
:allow_chat :allow_chat,
:tool_details
has_one :user, serializer: BasicUserSerializer, embed: :object has_one :user, serializer: BasicUserSerializer, embed: :object
has_many :rag_uploads, serializer: UploadSerializer, embed: :object has_many :rag_uploads, serializer: UploadSerializer, embed: :object

View File

@ -6,7 +6,7 @@ const CREATE_ATTRIBUTES = [
"id", "id",
"name", "name",
"description", "description",
"commands", "tools",
"system_prompt", "system_prompt",
"allowed_group_ids", "allowed_group_ids",
"enabled", "enabled",
@ -27,6 +27,7 @@ const CREATE_ATTRIBUTES = [
"rag_conversation_chunks", "rag_conversation_chunks",
"question_consolidator_llm", "question_consolidator_llm",
"allow_chat", "allow_chat",
"tool_details",
]; ];
const SYSTEM_ATTRIBUTES = [ const SYSTEM_ATTRIBUTES = [
@ -48,37 +49,37 @@ const SYSTEM_ATTRIBUTES = [
"rag_conversation_chunks", "rag_conversation_chunks",
"question_consolidator_llm", "question_consolidator_llm",
"allow_chat", "allow_chat",
"tool_details",
]; ];
class CommandOption { class ToolOption {
@tracked value = null; @tracked value = null;
} }
export default class AiPersona extends RestModel { export default class AiPersona extends RestModel {
// this code is here to convert the wire schema to easier to work with object // this code is here to convert the wire schema to easier to work with object
// on the wire we pass in/out commands as an Array. // on the wire we pass in/out tools as an Array.
// [[CommandName, {option1: value, option2: value}], CommandName2, CommandName3] // [[ToolName, {option1: value, option2: value}], ToolName2, ToolName3]
// So we rework this into a "commands" property and nested commandOptions // So we rework this into a "tools" property and nested toolOptions
init(properties) { init(properties) {
if (properties.commands) { if (properties.tools) {
properties.commands = properties.commands.map((command) => { properties.tools = properties.tools.map((tool) => {
if (typeof command === "string") { if (typeof tool === "string") {
return command; return tool;
} else { } else {
let [commandId, options] = command; let [toolId, options] = tool;
for (let optionId in options) { for (let optionId in options) {
if (!options.hasOwnProperty(optionId)) { if (!options.hasOwnProperty(optionId)) {
continue; continue;
} }
this.getCommandOption(commandId, optionId).value = this.getToolOption(toolId, optionId).value = options[optionId];
options[optionId];
} }
return commandId; return toolId;
} }
}); });
} }
super.init(properties); super.init(properties);
this.commands = properties.commands; this.tools = properties.tools;
} }
async createUser() { async createUser() {
@ -93,23 +94,23 @@ export default class AiPersona extends RestModel {
return this.user; return this.user;
} }
getCommandOption(commandId, optionId) { getToolOption(toolId, optionId) {
this.commandOptions ||= {}; this.toolOptions ||= {};
this.commandOptions[commandId] ||= {}; this.toolOptions[toolId] ||= {};
return (this.commandOptions[commandId][optionId] ||= new CommandOption()); return (this.toolOptions[toolId][optionId] ||= new ToolOption());
} }
populateCommandOptions(attrs) { populateToolOptions(attrs) {
if (!attrs.commands) { if (!attrs.tools) {
return; return;
} }
let commandsWithOptions = []; let toolsWithOptions = [];
attrs.commands.forEach((commandId) => { attrs.tools.forEach((toolId) => {
if (typeof commandId !== "string") { if (typeof toolId !== "string") {
commandId = commandId[0]; toolId = toolId[0];
} }
if (this.commandOptions && this.commandOptions[commandId]) { if (this.toolOptions && this.toolOptions[toolId]) {
let options = this.commandOptions[commandId]; let options = this.toolOptions[toolId];
let optionsWithValues = {}; let optionsWithValues = {};
for (let optionId in options) { for (let optionId in options) {
if (!options.hasOwnProperty(optionId)) { if (!options.hasOwnProperty(optionId)) {
@ -118,12 +119,12 @@ export default class AiPersona extends RestModel {
let option = options[optionId]; let option = options[optionId];
optionsWithValues[optionId] = option.value; optionsWithValues[optionId] = option.value;
} }
commandsWithOptions.push([commandId, optionsWithValues]); toolsWithOptions.push([toolId, optionsWithValues]);
} else { } else {
commandsWithOptions.push(commandId); toolsWithOptions.push(toolId);
} }
}); });
attrs.commands = commandsWithOptions; attrs.tools = toolsWithOptions;
} }
updateProperties() { updateProperties() {
@ -131,20 +132,20 @@ export default class AiPersona extends RestModel {
? this.getProperties(SYSTEM_ATTRIBUTES) ? this.getProperties(SYSTEM_ATTRIBUTES)
: this.getProperties(CREATE_ATTRIBUTES); : this.getProperties(CREATE_ATTRIBUTES);
attrs.id = this.id; attrs.id = this.id;
this.populateCommandOptions(attrs); this.populateToolOptions(attrs);
return attrs; return attrs;
} }
createProperties() { createProperties() {
let attrs = this.getProperties(CREATE_ATTRIBUTES); let attrs = this.getProperties(CREATE_ATTRIBUTES);
this.populateCommandOptions(attrs); this.populateToolOptions(attrs);
return attrs; return attrs;
} }
workingCopy() { workingCopy() {
let attrs = this.getProperties(CREATE_ATTRIBUTES); let attrs = this.getProperties(CREATE_ATTRIBUTES);
this.populateCommandOptions(attrs); this.populateToolOptions(attrs);
return AiPersona.create(attrs); return AiPersona.create(attrs);
} }
} }

View File

@ -1,81 +0,0 @@
import Component from "@glimmer/component";
import I18n from "discourse-i18n";
import AiPersonaCommandOptionEditor from "./ai-persona-command-option-editor";
export default class AiPersonaCommandOptions extends Component {
get showCommandOptions() {
const allCommands = this.args.allCommands;
if (!allCommands) {
return false;
}
return this.commandNames.any(
(command) => allCommands.find((c) => c.id === command)?.options
);
}
get commandNames() {
if (!this.args.commands) {
return [];
}
return this.args.commands.map((command) => {
if (typeof command === "string") {
return command;
} else {
return command[0];
}
});
}
get commandOptions() {
if (!this.args.commands) {
return [];
}
const allCommands = this.args.allCommands;
if (!allCommands) {
return [];
}
const options = [];
this.commandNames.forEach((commandId) => {
const command = allCommands.find((c) => c.id === commandId);
const commandName = command?.name;
const commandOptions = command?.options;
if (commandOptions) {
const mappedOptions = Object.keys(commandOptions).map((key) => {
const value = this.args.persona.getCommandOption(commandId, key);
return Object.assign({}, commandOptions[key], { id: key, value });
});
options.push({ commandName, options: mappedOptions });
}
});
return options;
}
<template>
{{#if this.showCommandOptions}}
<div class="control-group">
<label>{{I18n.t "discourse_ai.ai_persona.command_options"}}</label>
<div>
{{#each this.commandOptions as |commandOption|}}
<div class="ai-persona-editor__command-options">
<div class="ai-persona-editor__command-options-name">
{{commandOption.commandName}}
</div>
<div class="ai-persona-editor__command-option-options">
{{#each commandOption.options as |option|}}
<AiPersonaCommandOptionEditor @option={{option}} />
{{/each}}
</div>
</div>
{{/each}}
</div>
</div>
{{/if}}
</template>
}

View File

@ -20,9 +20,9 @@ import AdminUser from "admin/models/admin-user";
import ComboBox from "select-kit/components/combo-box"; import ComboBox from "select-kit/components/combo-box";
import GroupChooser from "select-kit/components/group-chooser"; import GroupChooser from "select-kit/components/group-chooser";
import DTooltip from "float-kit/components/d-tooltip"; import DTooltip from "float-kit/components/d-tooltip";
import AiCommandSelector from "./ai-command-selector";
import AiLlmSelector from "./ai-llm-selector"; import AiLlmSelector from "./ai-llm-selector";
import AiPersonaCommandOptions from "./ai-persona-command-options"; import AiPersonaToolOptions from "./ai-persona-tool-options";
import AiToolSelector from "./ai-tool-selector";
import PersonaRagUploader from "./persona-rag-uploader"; import PersonaRagUploader from "./persona-rag-uploader";
export default class PersonaEditor extends Component { export default class PersonaEditor extends Component {
@ -201,21 +201,6 @@ export default class PersonaEditor extends Component {
await this.toggleField("priority", true); await this.toggleField("priority", true);
} }
@action
async toggleMentionable() {
await this.toggleField("mentionable");
}
@action
async toggleAllowChat() {
await this.toggleField("allow_chat");
}
@action
async toggleVisionEnabled() {
await this.toggleField("vision_enabled");
}
@action @action
async createUser() { async createUser() {
try { try {
@ -303,45 +288,6 @@ export default class PersonaEditor extends Component {
@content={{I18n.t "discourse_ai.ai_persona.priority_help"}} @content={{I18n.t "discourse_ai.ai_persona.priority_help"}}
/> />
</div> </div>
{{#if this.editingModel.user}}
{{#if this.chatPluginEnabled}}
<div class="control-group ai-persona-editor__allow_chat">
<DToggleSwitch
class="ai-persona-editor__allow_chat_toggle"
@state={{@model.allow_chat}}
@label="discourse_ai.ai_persona.allow_chat"
{{on "click" this.toggleAllowChat}}
/>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.ai_persona.allow_chat_help"}}
/>
</div>
{{/if}}
<div class="control-group ai-persona-editor__mentionable">
<DToggleSwitch
class="ai-persona-editor__mentionable_toggle"
@state={{@model.mentionable}}
@label="discourse_ai.ai_persona.mentionable"
{{on "click" this.toggleMentionable}}
/>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.ai_persona.mentionable_help"}}
/>
</div>
{{/if}}
<div class="control-group ai-persona-editor__vision_enabled">
<DToggleSwitch
@state={{@model.vision_enabled}}
@label="discourse_ai.ai_persona.vision_enabled"
{{on "click" this.toggleVisionEnabled}}
/>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.ai_persona.vision_enabled_help"}}
/>
</div>
<div class="control-group"> <div class="control-group">
<label>{{I18n.t "discourse_ai.ai_persona.name"}}</label> <label>{{I18n.t "discourse_ai.ai_persona.name"}}</label>
<Input <Input
@ -400,19 +346,19 @@ export default class PersonaEditor extends Component {
</div> </div>
{{/unless}} {{/unless}}
<div class="control-group"> <div class="control-group">
<label>{{I18n.t "discourse_ai.ai_persona.commands"}}</label> <label>{{I18n.t "discourse_ai.ai_persona.tools"}}</label>
<AiCommandSelector <AiToolSelector
class="ai-persona-editor__commands" class="ai-persona-editor__tools"
@value={{this.editingModel.commands}} @value={{this.editingModel.tools}}
@disabled={{this.editingModel.system}} @disabled={{this.editingModel.system}}
@commands={{@personas.resultSetMeta.commands}} @tools={{@personas.resultSetMeta.tools}}
/> />
</div> </div>
{{#unless this.editingModel.system}} {{#unless this.editingModel.system}}
<AiPersonaCommandOptions <AiPersonaToolOptions
@persona={{this.editingModel}} @persona={{this.editingModel}}
@commands={{this.editingModel.commands}} @tools={{this.editingModel.tools}}
@allCommands={{@personas.resultSetMeta.commands}} @allTools={{@personas.resultSetMeta.tools}}
/> />
{{/unless}} {{/unless}}
<div class="control-group"> <div class="control-group">
@ -433,6 +379,65 @@ export default class PersonaEditor extends Component {
disabled={{this.editingModel.system}} disabled={{this.editingModel.system}}
/> />
</div> </div>
{{#if this.editingModel.user}}
{{#if this.chatPluginEnabled}}
<div class="control-group ai-persona-editor__allow_chat">
<label>
<Input
@type="checkbox"
@checked={{this.editingModel.allow_chat}}
/>
{{I18n.t "discourse_ai.ai_persona.allow_chat"}}</label>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.ai_persona.allow_chat_help"}}
/>
</div>
{{/if}}
<div class="control-group ai-persona-editor__mentionable">
<label>
<Input
@type="checkbox"
@checked={{this.editingModel.mentionable}}
/>
{{I18n.t "discourse_ai.ai_persona.mentionable"}}</label>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.ai_persona.mentionable_help"}}
/>
</div>
{{/if}}
<div class="control-group ai-persona-editor__tool-details">
<label>
<Input @type="checkbox" @checked={{this.editingModel.tool_details}} />
{{I18n.t "discourse_ai.ai_persona.tool_details"}}</label>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.ai_persona.tool_details_help"}}
/>
</div>
<div class="control-group ai-persona-editor__vision_enabled">
<label>
<Input
@type="checkbox"
@checked={{this.editingModel.vision_enabled}}
/>
{{I18n.t "discourse_ai.ai_persona.vision_enabled"}}</label>
<DTooltip
@icon="question-circle"
@content={{I18n.t "discourse_ai.ai_persona.vision_enabled_help"}}
/>
</div>
{{#if this.editingModel.vision_enabled}}
<div class="control-group">
<label>{{I18n.t "discourse_ai.ai_persona.vision_max_pixels"}}</label>
<ComboBox
@value={{this.maxPixelsValue}}
@content={{this.maxPixelValues}}
@onChange={{this.onChangeMaxPixels}}
/>
</div>
{{/if}}
<div class="control-group"> <div class="control-group">
<label>{{I18n.t "discourse_ai.ai_persona.max_context_posts"}}</label> <label>{{I18n.t "discourse_ai.ai_persona.max_context_posts"}}</label>
<Input <Input
@ -446,18 +451,8 @@ export default class PersonaEditor extends Component {
@content={{I18n.t "discourse_ai.ai_persona.max_context_posts_help"}} @content={{I18n.t "discourse_ai.ai_persona.max_context_posts_help"}}
/> />
</div> </div>
{{#if @model.vision_enabled}}
<div class="control-group">
<label>{{I18n.t "discourse_ai.ai_persona.vision_max_pixels"}}</label>
<ComboBox
@value={{this.maxPixelsValue}}
@content={{this.maxPixelValues}}
@onChange={{this.onChangeMaxPixels}}
/>
</div>
{{/if}}
<div class="control-group">
{{#if this.showTemperature}} {{#if this.showTemperature}}
<div class="control-group">
<label>{{I18n.t "discourse_ai.ai_persona.temperature"}}</label> <label>{{I18n.t "discourse_ai.ai_persona.temperature"}}</label>
<Input <Input
@type="number" @type="number"
@ -471,8 +466,10 @@ export default class PersonaEditor extends Component {
@icon="question-circle" @icon="question-circle"
@content={{I18n.t "discourse_ai.ai_persona.temperature_help"}} @content={{I18n.t "discourse_ai.ai_persona.temperature_help"}}
/> />
</div>
{{/if}} {{/if}}
{{#if this.showTopP}} {{#if this.showTopP}}
<div class="control-group">
<label>{{I18n.t "discourse_ai.ai_persona.top_p"}}</label> <label>{{I18n.t "discourse_ai.ai_persona.top_p"}}</label>
<Input <Input
@type="number" @type="number"
@ -486,8 +483,8 @@ export default class PersonaEditor extends Component {
@icon="question-circle" @icon="question-circle"
@content={{I18n.t "discourse_ai.ai_persona.top_p_help"}} @content={{I18n.t "discourse_ai.ai_persona.top_p_help"}}
/> />
{{/if}}
</div> </div>
{{/if}}
{{#if this.siteSettings.ai_embeddings_enabled}} {{#if this.siteSettings.ai_embeddings_enabled}}
<div class="control-group"> <div class="control-group">
<PersonaRagUploader <PersonaRagUploader

View File

@ -3,7 +3,7 @@ import { Input } from "@ember/component";
import { on } from "@ember/modifier"; import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
export default class AiPersonaCommandOptionEditor extends Component { export default class AiPersonaToolOptionEditor extends Component {
get isBoolean() { get isBoolean() {
return this.args.option.type === "boolean"; return this.args.option.type === "boolean";
} }
@ -18,7 +18,7 @@ export default class AiPersonaCommandOptionEditor extends Component {
} }
<template> <template>
<div class="control-group ai-persona-command-option-editor"> <div class="control-group ai-persona-tool-option-editor">
<label> <label>
{{@option.name}} {{@option.name}}
</label> </label>
@ -35,7 +35,7 @@ export default class AiPersonaCommandOptionEditor extends Component {
{{/if}} {{/if}}
</div> </div>
{{#unless this.isBoolean}} {{#unless this.isBoolean}}
<div class="ai-persona-command-option-editor__instructions"> <div class="ai-persona-tool-option-editor__instructions">
{{@option.description}} {{@option.description}}
</div> </div>
{{/unless}} {{/unless}}

View File

@ -0,0 +1,79 @@
import Component from "@glimmer/component";
import I18n from "discourse-i18n";
import AiPersonaToolOptionEditor from "./ai-persona-tool-option-editor";
export default class AiPersonaToolOptions extends Component {
get showToolOptions() {
const allTools = this.args.allTools;
if (!allTools) {
return false;
}
return this.toolNames.any((tool) => allTools.findBy("id", tool)?.options);
}
get toolNames() {
if (!this.args.tools) {
return [];
}
return this.args.tools.map((tool) => {
if (typeof tool === "string") {
return tool;
} else {
return tool[0];
}
});
}
get toolOptions() {
if (!this.args.tools) {
return [];
}
const allTools = this.args.allTools;
if (!allTools) {
return [];
}
const options = [];
this.toolNames.forEach((toolId) => {
const tool = allTools.findBy("id", toolId);
const toolName = tool?.name;
const toolOptions = tool?.options;
if (toolOptions) {
const mappedOptions = Object.keys(toolOptions).map((key) => {
const value = this.args.persona.getToolOption(toolId, key);
return Object.assign({}, toolOptions[key], { id: key, value });
});
options.push({ toolName, options: mappedOptions });
}
});
return options;
}
<template>
{{#if this.showToolOptions}}
<div class="control-group">
<label>{{I18n.t "discourse_ai.ai_persona.tool_options"}}</label>
<div>
{{#each this.toolOptions as |toolOption|}}
<div class="ai-persona-editor__tool-options">
<div class="ai-persona-editor__tool-options-name">
{{toolOption.toolName}}
</div>
<div class="ai-persona-editor__tool-option-options">
{{#each toolOption.options as |option|}}
<AiPersonaToolOptionEditor @option={{option}} />
{{/each}}
</div>
</div>
{{/each}}
</div>
</div>
{{/if}}
</template>
}

View File

@ -7,7 +7,7 @@ export default MultiSelectComponent.extend({
}), }),
content: computed(function () { content: computed(function () {
return this.commands; return this.tools;
}), }),
value: "", value: "",

View File

@ -23,7 +23,7 @@
} }
} }
.ai-persona-command-option-editor { .ai-persona-tool-option-editor {
&__instructions { &__instructions {
color: var(--primary-medium); color: var(--primary-medium);
font-size: var(--font-down-1); font-size: var(--font-down-1);
@ -49,12 +49,12 @@
label { label {
display: block; display: block;
} }
&__command-options { &__tool-options {
padding: 5px 10px 5px; padding: 5px 10px 5px;
border: 1px solid var(--primary-low-mid); border: 1px solid var(--primary-low-mid);
width: 480px; width: 480px;
} }
&__command-options-name { &__tool-options-name {
margin-bottom: 10px; margin-bottom: 10px;
font-size: var(--font-down-1); font-size: var(--font-down-1);
} }
@ -65,25 +65,16 @@
width: 500px; width: 500px;
height: 400px; height: 400px;
} }
&__priority {
display: flex; &__tool-details,
align-items: center; &__vision_enabled,
} &__allow_chat,
&__priority,
&__mentionable { &__mentionable {
display: flex; display: flex;
align-items: center; align-items: center;
} }
&__allow_chat {
display: flex;
align-items: center;
}
&__vision_enabled {
display: flex;
align-items: center;
}
&__indexing-options { &__indexing-options {
display: block; display: block;
margin-top: 1em; margin-top: 1em;

View File

@ -111,7 +111,7 @@ ar:
delete: حذف delete: حذف
priority: الأولوية priority: الأولوية
priority_help: يتم عرض الشخصيات ذات الأولوية للمستخدمين في أعلى قائمة الشخصيات. إذا كانت الأولوية لعدة أشخاص، فسيتم فرزهم أبجديًا. priority_help: يتم عرض الشخصيات ذات الأولوية للمستخدمين في أعلى قائمة الشخصيات. إذا كانت الأولوية لعدة أشخاص، فسيتم فرزهم أبجديًا.
command_options: "خيارات الأوامر" tool_options: "خيارات الأوامر"
uploads: uploads:
title: "التحميلات" title: "التحميلات"
uploading: "جارٍ التحميل..." uploading: "جارٍ التحميل..."

View File

@ -169,7 +169,7 @@ de:
top_p_help: Top P für die LLM, erhöhen, um die Zufälligkeit zu erhöhen (leer lassen, um die Modellvorgabe zu verwenden, in der Regel ein Wert zwischen 0,0 und 1,0) top_p_help: Top P für die LLM, erhöhen, um die Zufälligkeit zu erhöhen (leer lassen, um die Modellvorgabe zu verwenden, in der Regel ein Wert zwischen 0,0 und 1,0)
priority: Priorität 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: Personas mit Priorität werden den Benutzern am Anfang der Persona-Liste angezeigt. Wenn mehrere Personas Priorität haben, werden sie alphabetisch sortiert.
command_options: "Befehlsoptionen" tool_options: "Befehlsoptionen"
rag_chunk_tokens: "Chunk-Token hochladen" rag_chunk_tokens: "Chunk-Token hochladen"
rag_chunk_tokens_help: "Die Anzahl der Token, die für jeden Chunk im RAG-Modell verwendet werden. Erhöhen, um die Menge des Kontexts zu erhöhen, den die KI verwenden kann. (Eine Änderung führt zu einer Neuindizierung aller Uploads)" rag_chunk_tokens_help: "Die Anzahl der Token, die für jeden Chunk im RAG-Modell verwendet werden. Erhöhen, um die Menge des Kontexts zu erhöhen, den die KI verwenden kann. (Eine Änderung führt zu einer Neuindizierung aller Uploads)"
rag_chunk_overlap_tokens: "Chunk-Überlappungs-Token hochladen" rag_chunk_overlap_tokens: "Chunk-Überlappungs-Token hochladen"

View File

@ -131,12 +131,14 @@ en:
max_context_posts: "Max Context Posts" 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)" 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 vision_enabled: Vision Enabled
vision_enabled_help: If enabled, the AI will attempt to understand images users post in the topic, depends on the model being used supporting vision. Anthropic Claude 3 models support vision. vision_enabled_help: If enabled, the AI will attempt to understand images users post in the topic, depends on the model being used supporting vision. Supported by latest models from Anthropic, Google, and OpenAI.
vision_max_pixels: Supported image size vision_max_pixels: Supported image size
vision_max_pixel_sizes: vision_max_pixel_sizes:
low: Low Quality - cheapest (256x256) low: Low Quality - cheapest (256x256)
medium: Medium Quality (512x512) medium: Medium Quality (512x512)
high: High Quality - slowest (1024x1024) high: High Quality - slowest (1024x1024)
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: 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 persona.
user: User user: User
@ -154,7 +156,7 @@ en:
save: Save save: Save
saved: AI Persona Saved saved: AI Persona Saved
enabled: "Enabled?" enabled: "Enabled?"
commands: Enabled Commands tools: Enabled Tools
allowed_groups: Allowed Groups allowed_groups: Allowed Groups
confirm_delete: Are you sure you want to delete this persona? confirm_delete: Are you sure you want to delete this persona?
new: "New Persona" new: "New Persona"
@ -167,7 +169,7 @@ en:
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) 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: 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 personas are displayed to users at the top of the persona list. If multiple personas have priority, they will be sorted alphabetically.
command_options: "Command Options" tool_options: "Tool Options"
rag_chunk_tokens: "Upload Chunk Tokens" rag_chunk_tokens: "Upload Chunk Tokens"
rag_chunk_tokens_help: "The number of tokens to use for each chunk in the RAG model. Increase to increase the amount of context the AI can use. (changing will re-index all uploads)" rag_chunk_tokens_help: "The number of tokens to use for each chunk in the RAG model. Increase to increase the amount of context the AI can use. (changing will re-index all uploads)"
rag_chunk_overlap_tokens: "Upload Chunk Overlap Tokens" rag_chunk_overlap_tokens: "Upload Chunk Overlap Tokens"

View File

@ -111,7 +111,7 @@ es:
delete: Eliminar delete: Eliminar
priority: Prioridad 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 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.
command_options: "Opciones de comando" tool_options: "Opciones de comando"
uploads: uploads:
title: "Subidos" title: "Subidos"
uploading: "Subiendo..." uploading: "Subiendo..."

View File

@ -111,7 +111,7 @@ fi:
delete: Poista delete: Poista
priority: Prioriteetti priority: Prioriteetti
priority_help: Prioriteettipersoonat näytetään käyttäjille ensimmäisinä persoonaluettelossa. Jos useilla persoonilla on prioriteetti, ne järjestetään aakkosjärjestyksessä. priority_help: Prioriteettipersoonat näytetään käyttäjille ensimmäisinä persoonaluettelossa. Jos useilla persoonilla on prioriteetti, ne järjestetään aakkosjärjestyksessä.
command_options: "Komentoasetukset" tool_options: "Komentoasetukset"
uploads: uploads:
title: "Lataukset" title: "Lataukset"
uploading: "Ladataan..." uploading: "Ladataan..."

View File

@ -111,7 +111,7 @@ fr:
delete: Supprimer delete: Supprimer
priority: Priorité priority: Priorité
priority_help: Les personnages prioritaires sont affichés aux utilisateurs en haut de la liste des personnages. Si plusieurs personnages sont prioritaires, ils seront triés par ordre alphabétique. priority_help: Les personnages prioritaires sont affichés aux utilisateurs en haut de la liste des personnages. Si plusieurs personnages sont prioritaires, ils seront triés par ordre alphabétique.
command_options: "Options de commande" tool_options: "Options de commande"
uploads: uploads:
title: "Fichiers envoyés" title: "Fichiers envoyés"
uploading: "Envoi en cours…" uploading: "Envoi en cours…"

View File

@ -169,7 +169,7 @@ he:
top_p_help: ה־P המובילים לשימוש למודל השפה הגדול (LLM), הגדלה תגדיל את היצירתיות (אפשר להשאיר ריק לשימוש בברירת המחדל של הדגם, בדרך כלל זה ערך בין 0.0 לבין 1.0) top_p_help: ה־P המובילים לשימוש למודל השפה הגדול (LLM), הגדלה תגדיל את היצירתיות (אפשר להשאיר ריק לשימוש בברירת המחדל של הדגם, בדרך כלל זה ערך בין 0.0 לבין 1.0)
priority: עדיפות priority: עדיפות
priority_help: דמויות בעדיפות גבוהה מוצגות למשתמשים בראש רשימת הדמויות. אם מספר דמויות הן בעדיפות הן תסודרנה לפי האלפבית. priority_help: דמויות בעדיפות גבוהה מוצגות למשתמשים בראש רשימת הדמויות. אם מספר דמויות הן בעדיפות הן תסודרנה לפי האלפבית.
command_options: "אפשרויות פקודה" tool_options: "אפשרויות פקודה"
rag_chunk_tokens: "העלאת אסימוני חלקים" rag_chunk_tokens: "העלאת אסימוני חלקים"
rag_chunk_tokens_help: "מספר האסימונים לשימוש לכל נתח במודל ה־RAG. הגדלה תגדיל את כמות ההקשר בו יכולה להשתמש הבינה המלאכותית. (שינוי יסדר את כל ההעלאות במפתח מחדש)" rag_chunk_tokens_help: "מספר האסימונים לשימוש לכל נתח במודל ה־RAG. הגדלה תגדיל את כמות ההקשר בו יכולה להשתמש הבינה המלאכותית. (שינוי יסדר את כל ההעלאות במפתח מחדש)"
rag_chunk_overlap_tokens: "העלאת אסימוני חפיפת חלקים" rag_chunk_overlap_tokens: "העלאת אסימוני חפיפת חלקים"

View File

@ -111,7 +111,7 @@ it:
delete: Elimina delete: Elimina
priority: Priorità 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 personaggi prioritari vengono visualizzati agli utenti nella parte superiore dell'elenco dei personaggi. Se più personaggi hanno la priorità, verranno ordinati in ordine alfabetico.
command_options: "Opzioni di comando" tool_options: "Opzioni di comando"
uploads: uploads:
title: "Caricamenti" title: "Caricamenti"
uploading: "Caricamento..." uploading: "Caricamento..."

View File

@ -111,7 +111,7 @@ ja:
delete: 削除 delete: 削除
priority: 優先度 priority: 優先度
priority_help: 優先ペルソナはペルソナリストの先頭に表示されます。複数のペルソナが優先されている場合は、アルファベット順に並べ替えられます。 priority_help: 優先ペルソナはペルソナリストの先頭に表示されます。複数のペルソナが優先されている場合は、アルファベット順に並べ替えられます。
command_options: "コマンドオプション" tool_options: "コマンドオプション"
uploads: uploads:
title: "アップロード" title: "アップロード"
uploading: "アップロード中..." uploading: "アップロード中..."

View File

@ -111,7 +111,7 @@ nl:
delete: Verwijderen delete: Verwijderen
priority: Prioriteit 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 persona's worden bovenaan de personalijst weergegeven voor gebruikers. Als meerdere persona's prioriteit hebben, worden deze alfabetisch gesorteerd.
command_options: "Opdrachtopties" tool_options: "Opdrachtopties"
uploads: uploads:
title: "Uploads" title: "Uploads"
uploading: "Uploaden..." uploading: "Uploaden..."

View File

@ -137,7 +137,7 @@ pl_PL:
temperature_help: Temperatura do zastosowania w LLM, zwiększ, aby zwiększyć kreatywność (pozostaw puste, aby użyć domyślnego modelu, zazwyczaj wartość od 0,0 do 2,0) temperature_help: Temperatura do zastosowania w LLM, zwiększ, aby zwiększyć kreatywność (pozostaw puste, aby użyć domyślnego modelu, zazwyczaj wartość od 0,0 do 2,0)
priority: Priorytet priority: Priorytet
priority_help: Priorytetowe persony są wyświetlane użytkownikom na górze listy person. Jeśli wiele person ma priorytet, zostaną one posortowane alfabetycznie. priority_help: Priorytetowe persony są wyświetlane użytkownikom na górze listy person. Jeśli wiele person ma priorytet, zostaną one posortowane alfabetycznie.
command_options: "Opcje poleceń" tool_options: "Opcje poleceń"
uploads: uploads:
title: "Pliki" title: "Pliki"
button: "Dodaj pliki" button: "Dodaj pliki"

View File

@ -112,7 +112,7 @@ pt_BR:
delete: Excluir delete: Excluir
priority: Prioridade 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: 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.
command_options: "Opções de comando" tool_options: "Opções de comando"
uploads: uploads:
title: "Envios" title: "Envios"
uploading: "Enviando..." uploading: "Enviando..."

View File

@ -111,7 +111,7 @@ ru:
delete: Удалить delete: Удалить
priority: Приоритет priority: Приоритет
priority_help: Приоритетные персоны показываются пользователям вверху списка персон. Если приоритет имеют несколько персон, они будут отсортированы в алфавитном порядке. priority_help: Приоритетные персоны показываются пользователям вверху списка персон. Если приоритет имеют несколько персон, они будут отсортированы в алфавитном порядке.
command_options: "Параметры команды" tool_options: "Параметры команды"
uploads: uploads:
title: "Загрузки" title: "Загрузки"
uploading: "Загрузка…" uploading: "Загрузка…"

View File

@ -153,7 +153,7 @@ tr_TR:
top_p_help: LLM için kullanılacak en yüksek P, rastgeleliği artırmak için artırın (model varsayılanını kullanmak için boş bırakın, genellikle 0.0 ila 1.0 arasında bir değer) top_p_help: LLM için kullanılacak en yüksek P, rastgeleliği artırmak için artırın (model varsayılanını kullanmak için boş bırakın, genellikle 0.0 ila 1.0 arasında bir değer)
priority: Öncelik priority: Öncelik
priority_help: Öncelikli kişilikler kullanıcılara kişilik listesinin en üstünde gösterilir. Birden fazla kişiliğin önceliği varsa bunlar alfabetik olarak sıralanır. priority_help: Öncelikli kişilikler kullanıcılara kişilik listesinin en üstünde gösterilir. Birden fazla kişiliğin önceliği varsa bunlar alfabetik olarak sıralanır.
command_options: "Komut Seçenekleri" tool_options: "Komut Seçenekleri"
what_are_personas: "Yapay Zeka Personaları nedir?" what_are_personas: "Yapay Zeka Personaları nedir?"
no_persona_selected: | no_persona_selected: |
YZ Kişilikleri, Discourse forumunuzda YZ motorunun davranışını özelleştirebilmenizi sağlayan güçlü bir özelliktir. YZ'nin yanıt ve etkileşimlerine rehberlik eden bir "sistem mesajı" görevi görerek daha kişiselleştirilmiş ve etkileşimli bir kullanıcı deneyimi oluşturmaya yardımcı olurlar. YZ Kişilikleri, Discourse forumunuzda YZ motorunun davranışını özelleştirebilmenizi sağlayan güçlü bir özelliktir. YZ'nin yanıt ve etkileşimlerine rehberlik eden bir "sistem mesajı" görevi görerek daha kişiselleştirilmiş ve etkileşimli bir kullanıcı deneyimi oluşturmaya yardımcı olurlar.

View File

@ -128,7 +128,7 @@ zh_CN:
top_p_help: 用于 LLM 的 Top P增大它可提升创造力留空以使用模型默认值通常为 0.0 到 1.0 之间的值) top_p_help: 用于 LLM 的 Top P增大它可提升创造力留空以使用模型默认值通常为 0.0 到 1.0 之间的值)
priority: 优先 priority: 优先
priority_help: 优先角色会在角色列表的顶部向用户显示。如果多个角色都具有优先级,将按字母顺序排序。 priority_help: 优先角色会在角色列表的顶部向用户显示。如果多个角色都具有优先级,将按字母顺序排序。
command_options: "命令选项" tool_options: "命令选项"
uploads: uploads:
title: "上传" title: "上传"
uploading: "正在上传…" uploading: "正在上传…"

View File

@ -151,7 +151,7 @@ ar:
topic_not_found: "الملخص غير متوفر، الموضوع غير موجود!" topic_not_found: "الملخص غير متوفر، الموضوع غير موجود!"
summarizing: "جارٍ تلخيص الموضوع" summarizing: "جارٍ تلخيص الموضوع"
searching: "جارٍ البحث عن: '%{query}'" searching: "جارٍ البحث عن: '%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "الحد الأقصى لعدد النتائج" name: "الحد الأقصى لعدد النتائج"
@ -159,7 +159,7 @@ ar:
base_query: base_query:
name: "استعلام البحث الأساسي" name: "استعلام البحث الأساسي"
description: "استعلام البحث الأساسي الذي سيتم استخدامه عند البحث. على سبيل المثال: سيسبق \"#urgent\" \"#urgent\" في استعلام البحث وسيتضمن الموضوعات ذات الفئة أو الوسم العاجل فقط." description: "استعلام البحث الأساسي الذي سيتم استخدامه عند البحث. على سبيل المثال: سيسبق \"#urgent\" \"#urgent\" في استعلام البحث وسيتضمن الموضوعات ذات الفئة أو الوسم العاجل فقط."
command_summary: tool_summary:
categories: "إدراج الفئات" categories: "إدراج الفئات"
search: "البحث" search: "البحث"
tags: "إدراج الوسوم" tags: "إدراج الوسوم"
@ -172,7 +172,7 @@ ar:
schema: "البحث عن مخطط قاعدة البيانات" schema: "البحث عن مخطط قاعدة البيانات"
search_settings: "جارٍ البحث في إعدادات الموقع" search_settings: "جارٍ البحث في إعدادات الموقع"
dall_e: "إنشاء صورة" dall_e: "إنشاء صورة"
command_help: tool_help:
categories: "إدراج جميع الفئات المرئية بشكلٍ عام في المنتدى" categories: "إدراج جميع الفئات المرئية بشكلٍ عام في المنتدى"
search: "البحث في جميع الموضوعات العامة في المنتدى" search: "البحث في جميع الموضوعات العامة في المنتدى"
tags: "إدراج جميع الوسوم في المنتدى" tags: "إدراج جميع الوسوم في المنتدى"
@ -185,7 +185,7 @@ ar:
schema: "البحث عن مخطط قاعدة البيانات" schema: "البحث عن مخطط قاعدة البيانات"
search_settings: "البحث في إعدادات الموقع" search_settings: "البحث في إعدادات الموقع"
dall_e: "إنشاء صورة باستخدام DALL-E 3" dall_e: "إنشاء صورة باستخدام DALL-E 3"
command_description: tool_description:
read: "القراءة: <a href='%{url}'>%{title}</a>" read: "القراءة: <a href='%{url}'>%{title}</a>"
time: "الوقت في المنطقة الزمنية %{timezone} هو %{time}" time: "الوقت في المنطقة الزمنية %{timezone} هو %{time}"
summarize: "تم تلخيص <a href='%{url}'>%{title}</a>" summarize: "تم تلخيص <a href='%{url}'>%{title}</a>"

View File

@ -7,7 +7,7 @@
be: be:
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Пошук" search: "Пошук"
sentiment: sentiment:
reports: reports:

View File

@ -10,7 +10,7 @@ bg:
yaxis: "Дата" yaxis: "Дата"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Търсене" search: "Търсене"
time: "Време " time: "Време "
summarize: "Обобщаване" summarize: "Обобщаване"

View File

@ -10,6 +10,6 @@ bs_BA:
yaxis: "Datum" yaxis: "Datum"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Pretraži" search: "Pretraži"
time: "Vrijeme" time: "Vrijeme"

View File

@ -10,7 +10,7 @@ ca:
yaxis: "Data" yaxis: "Data"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Cerca" search: "Cerca"
time: "Hora" time: "Hora"
sentiment: sentiment:

View File

@ -10,7 +10,7 @@ cs:
yaxis: "Datum" yaxis: "Datum"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Vyhledat" search: "Vyhledat"
tags: "Seznam značek" tags: "Seznam značek"
time: "Čas" time: "Čas"

View File

@ -10,7 +10,7 @@ da:
yaxis: "Dato" yaxis: "Dato"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Søg" search: "Søg"
time: "Tidspunkt" time: "Tidspunkt"
sentiment: sentiment:

View File

@ -193,7 +193,7 @@ de:
topic_not_found: "Zusammenfassung nicht verfügbar, Thema nicht gefunden!" topic_not_found: "Zusammenfassung nicht verfügbar, Thema nicht gefunden!"
summarizing: "Thema zusammenfassen" summarizing: "Thema zusammenfassen"
searching: "Suche nach: „%{query}“" searching: "Suche nach: „%{query}“"
command_options: tool_options:
search: search:
search_private: search_private:
name: "Suche Privat" name: "Suche Privat"
@ -204,7 +204,7 @@ de:
base_query: base_query:
name: "Basissuchanfrage" name: "Basissuchanfrage"
description: "Basisanfrage, die bei der Suche verwendet wird. Beispiel: Bei „#dringend“ wird der Suchanfrage „#dringend“ vorangestellt und es werden nur Themen mit der Kategorie oder dem Schlagwort „dringend“ angezeigt." description: "Basisanfrage, die bei der Suche verwendet wird. Beispiel: Bei „#dringend“ wird der Suchanfrage „#dringend“ vorangestellt und es werden nur Themen mit der Kategorie oder dem Schlagwort „dringend“ angezeigt."
command_summary: tool_summary:
web_browser: "Web durchsuchen" web_browser: "Web durchsuchen"
github_search_files: "GitHub Datei-Suche" github_search_files: "GitHub Datei-Suche"
github_search_code: "GitHub Code-Suche" github_search_code: "GitHub Code-Suche"
@ -225,7 +225,7 @@ de:
dall_e: "Bild generieren" dall_e: "Bild generieren"
search_meta_discourse: "Suche Meta Discourse" search_meta_discourse: "Suche Meta Discourse"
javascript_evaluator: "JavaScript auswerten" javascript_evaluator: "JavaScript auswerten"
command_help: tool_help:
web_browser: "Webseite mit dem KI Bot durchsuchen" web_browser: "Webseite mit dem KI Bot durchsuchen"
github_search_code: "Suche nach Code in einem GitHub-Repository" github_search_code: "Suche nach Code in einem GitHub-Repository"
github_search_files: "Suche nach Dateien in einem GitHub-Repository" github_search_files: "Suche nach Dateien in einem GitHub-Repository"
@ -246,7 +246,7 @@ de:
dall_e: "Bild mit DALL-E 3 generieren" dall_e: "Bild mit DALL-E 3 generieren"
search_meta_discourse: "Suche Meta Discourse" search_meta_discourse: "Suche Meta Discourse"
javascript_evaluator: "JavaScript auswerten" javascript_evaluator: "JavaScript auswerten"
command_description: tool_description:
web_browser: "Lesen <a href='%{url}'>%{url}</a>" web_browser: "Lesen <a href='%{url}'>%{url}</a>"
github_search_files: "Gesucht wurde nach '%{keywords}' in %{repo}/%{branch}" github_search_files: "Gesucht wurde nach '%{keywords}' in %{repo}/%{branch}"
github_search_code: "Gesucht wurde nach '%{query}' in %{repo}" github_search_code: "Gesucht wurde nach '%{query}' in %{repo}"

View File

@ -10,6 +10,6 @@ el:
yaxis: "Ημερομηνία" yaxis: "Ημερομηνία"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Αναζήτηση" search: "Αναζήτηση"
time: "Ώρα" time: "Ώρα"

View File

@ -175,7 +175,7 @@ en:
personas: personas:
default_llm_required: "Default LLM model is required prior to enabling Chat" 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_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 commands or system prompt, instead disable and make a copy" cannot_edit_system_persona: "System personas can only be renamed, you may not edit tools or system prompt, instead disable and make a copy"
github_helper: github_helper:
name: "GitHub Helper" name: "GitHub Helper"
description: "AI Bot specialized in assisting with GitHub-related tasks and questions" description: "AI Bot specialized in assisting with GitHub-related tasks and questions"
@ -206,7 +206,7 @@ en:
topic_not_found: "Summary unavailable, topic not found!" topic_not_found: "Summary unavailable, topic not found!"
summarizing: "Summarizing topic" summarizing: "Summarizing topic"
searching: "Searching for: '%{query}'" searching: "Searching for: '%{query}'"
command_options: tool_options:
search: search:
search_private: search_private:
name: "Search Private" name: "Search Private"
@ -217,7 +217,7 @@ en:
base_query: base_query:
name: "Base Search Query" name: "Base Search Query"
description: "Base query to use when searching. Example: '#urgent' will prepend '#urgent' to the search query and only include topics with the urgent category or tag." description: "Base query to use when searching. Example: '#urgent' will prepend '#urgent' to the search query and only include topics with the urgent category or tag."
command_summary: tool_summary:
web_browser: "Browse Web" web_browser: "Browse Web"
github_search_files: "GitHub search files" github_search_files: "GitHub search files"
github_search_code: "GitHub code search" github_search_code: "GitHub code search"
@ -238,7 +238,7 @@ en:
dall_e: "Generate image" dall_e: "Generate image"
search_meta_discourse: "Search Meta Discourse" search_meta_discourse: "Search Meta Discourse"
javascript_evaluator: "Evaluate JavaScript" javascript_evaluator: "Evaluate JavaScript"
command_help: tool_help:
web_browser: "Browse web page using the AI Bot" web_browser: "Browse web page using the AI Bot"
github_search_code: "Search for code in a GitHub repository" github_search_code: "Search for code in a GitHub repository"
github_search_files: "Search for files in a GitHub repository" github_search_files: "Search for files in a GitHub repository"
@ -259,7 +259,7 @@ en:
dall_e: "Generate image using DALL-E 3" dall_e: "Generate image using DALL-E 3"
search_meta_discourse: "Search Meta Discourse" search_meta_discourse: "Search Meta Discourse"
javascript_evaluator: "Evaluate JavaScript" javascript_evaluator: "Evaluate JavaScript"
command_description: tool_description:
web_browser: "Reading <a href='%{url}'>%{url}</a>" web_browser: "Reading <a href='%{url}'>%{url}</a>"
github_search_files: "Searched for '%{keywords}' in %{repo}/%{branch}" github_search_files: "Searched for '%{keywords}' in %{repo}/%{branch}"
github_search_code: "Searched for '%{query}' in %{repo}" github_search_code: "Searched for '%{query}' in %{repo}"

View File

@ -151,7 +151,7 @@ es:
topic_not_found: "¡Resumen no disponible, tema no encontrado!" topic_not_found: "¡Resumen no disponible, tema no encontrado!"
summarizing: "Resumiendo tema" summarizing: "Resumiendo tema"
searching: "Buscando: '%{query}'" searching: "Buscando: '%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "Número máximo de resultados" name: "Número máximo de resultados"
@ -159,7 +159,7 @@ es:
base_query: base_query:
name: "Consulta de búsqueda básica" name: "Consulta de búsqueda básica"
description: "Consulta base a utilizar en la búsqueda. Ejemplo: «#urgente» antepondrá «#urgente» a la consulta de búsqueda y solo incluirá temas con la categoría o etiqueta urgente." description: "Consulta base a utilizar en la búsqueda. Ejemplo: «#urgente» antepondrá «#urgente» a la consulta de búsqueda y solo incluirá temas con la categoría o etiqueta urgente."
command_summary: tool_summary:
categories: "Lista de categorías" categories: "Lista de categorías"
search: "Buscar" search: "Buscar"
tags: "Listar etiquetas" tags: "Listar etiquetas"
@ -172,7 +172,7 @@ es:
schema: "Buscar esquema de base de datos" schema: "Buscar esquema de base de datos"
search_settings: "Buscando los ajustes del sitio" search_settings: "Buscando los ajustes del sitio"
dall_e: "Generar imagen" dall_e: "Generar imagen"
command_help: tool_help:
categories: "Listar todas las categorías visibles públicamente en el foro" categories: "Listar todas las categorías visibles públicamente en el foro"
search: "Buscar todos los temas públicos en el foro." search: "Buscar todos los temas públicos en el foro."
tags: "Listar todas las etiquetas en el foro" tags: "Listar todas las etiquetas en el foro"
@ -185,7 +185,7 @@ es:
schema: "Buscar esquema de base de datos" schema: "Buscar esquema de base de datos"
search_settings: "Buscar ajustes del sitio" search_settings: "Buscar ajustes del sitio"
dall_e: "Generar imagen usando DALL-E 3" dall_e: "Generar imagen usando DALL-E 3"
command_description: tool_description:
read: "Leyendo: <a href='%{url}'>%{title}</a>" read: "Leyendo: <a href='%{url}'>%{title}</a>"
time: "La hora en %{timezone} es %{time}" time: "La hora en %{timezone} es %{time}"
summarize: "Resumido <a href='%{url}'>%{title}</a>" summarize: "Resumido <a href='%{url}'>%{title}</a>"

View File

@ -10,7 +10,7 @@ et:
yaxis: "Date" yaxis: "Date"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Otsi" search: "Otsi"
time: "Aeg" time: "Aeg"
sentiment: sentiment:

View File

@ -19,7 +19,7 @@ fa_IR:
personas: personas:
dall_e3: dall_e3:
name: "DALL-E 3" name: "DALL-E 3"
command_summary: tool_summary:
categories: "فهرست دسته‌بندی‌ها" categories: "فهرست دسته‌بندی‌ها"
search: "جستجو" search: "جستجو"
tags: "فهرست برچسب‌ها" tags: "فهرست برچسب‌ها"
@ -28,7 +28,7 @@ fa_IR:
image: "تولید تصویر" image: "تولید تصویر"
google: "جستجو در گوگل" google: "جستجو در گوگل"
dall_e: "تولید تصویر" dall_e: "تولید تصویر"
command_description: tool_description:
summarize: "خلاصه شده <a href='%{url}'>%{title}</a>" summarize: "خلاصه شده <a href='%{url}'>%{title}</a>"
dall_e: "%{prompt}" dall_e: "%{prompt}"
image: "%{prompt}" image: "%{prompt}"

View File

@ -151,7 +151,7 @@ fi:
topic_not_found: "Yhteenveto ei ole saatavilla, ketjua ei löydy!" topic_not_found: "Yhteenveto ei ole saatavilla, ketjua ei löydy!"
summarizing: "Laaditaan yhteenvetoa ketjusta" summarizing: "Laaditaan yhteenvetoa ketjusta"
searching: "Haetaan: \"%{query}\"" searching: "Haetaan: \"%{query}\""
command_options: tool_options:
search: search:
max_results: max_results:
name: "Tulosten enimmäismäärä" name: "Tulosten enimmäismäärä"
@ -159,7 +159,7 @@ fi:
base_query: base_query:
name: "Perushakukysely" name: "Perushakukysely"
description: "Peruskysely, jota käytetään haussa. Esimerkki: \"#kiireellinen\" lisää hakukyselyn alkuun \"#kiireellinen\" ja sisältää vain ketjut, joissa on kiireellinen alue tai tunniste." description: "Peruskysely, jota käytetään haussa. Esimerkki: \"#kiireellinen\" lisää hakukyselyn alkuun \"#kiireellinen\" ja sisältää vain ketjut, joissa on kiireellinen alue tai tunniste."
command_summary: tool_summary:
categories: "Listaa alueet" categories: "Listaa alueet"
search: "Haku" search: "Haku"
tags: "Listaa tunnisteet" tags: "Listaa tunnisteet"
@ -172,7 +172,7 @@ fi:
schema: "Etsi tietokantaskeema" schema: "Etsi tietokantaskeema"
search_settings: "Haetaan sivustoasetuksia" search_settings: "Haetaan sivustoasetuksia"
dall_e: "Luo kuva" dall_e: "Luo kuva"
command_help: tool_help:
categories: "Listaa kaikki foorumin julkisesti näkyvät alueet" categories: "Listaa kaikki foorumin julkisesti näkyvät alueet"
search: "Hae kaikista foorumin julkisista ketjuista" search: "Hae kaikista foorumin julkisista ketjuista"
tags: "Listaa kaikki foorumin tunnisteet" tags: "Listaa kaikki foorumin tunnisteet"
@ -185,7 +185,7 @@ fi:
schema: "Etsi tietokantaskeema" schema: "Etsi tietokantaskeema"
search_settings: "Hae sivustoasetuksia" search_settings: "Hae sivustoasetuksia"
dall_e: "Luo kuva DALL-E 3:lla" dall_e: "Luo kuva DALL-E 3:lla"
command_description: tool_description:
read: "Luetaan: <a href='%{url}'>%{title}</a>" read: "Luetaan: <a href='%{url}'>%{title}</a>"
time: "Aika aikavyöhykkeellä %{timezone} on %{time}" time: "Aika aikavyöhykkeellä %{timezone} on %{time}"
summarize: "Yhteenveto: <a href='%{url}'>%{title}</a>" summarize: "Yhteenveto: <a href='%{url}'>%{title}</a>"

View File

@ -151,7 +151,7 @@ fr:
topic_not_found: "Résumé indisponible, sujet introuvable !" topic_not_found: "Résumé indisponible, sujet introuvable !"
summarizing: "Synthèse du sujet" summarizing: "Synthèse du sujet"
searching: "Recherche de : '%{query}'" searching: "Recherche de : '%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "Nombre maximal de résultats" name: "Nombre maximal de résultats"
@ -159,7 +159,7 @@ fr:
base_query: base_query:
name: "Requête de recherche de base" name: "Requête de recherche de base"
description: "Requête de base à utiliser lors de la recherche. Exemple : « #urgent » ajoutera « #urgent » à la requête de recherche et inclura uniquement les sujets avec la catégorie ou l'étiquette correspondante." description: "Requête de base à utiliser lors de la recherche. Exemple : « #urgent » ajoutera « #urgent » à la requête de recherche et inclura uniquement les sujets avec la catégorie ou l'étiquette correspondante."
command_summary: tool_summary:
categories: "Lister les catégories" categories: "Lister les catégories"
search: "Rechercher" search: "Rechercher"
tags: "Répertorier les étiquettes" tags: "Répertorier les étiquettes"
@ -172,7 +172,7 @@ fr:
schema: "Rechercher le schéma de la base de données" schema: "Rechercher le schéma de la base de données"
search_settings: "Recherche des paramètres du site" search_settings: "Recherche des paramètres du site"
dall_e: "Générer une image" dall_e: "Générer une image"
command_help: tool_help:
categories: "Répertoriez toutes les catégories visibles publiquement sur le forum" categories: "Répertoriez toutes les catégories visibles publiquement sur le forum"
search: "Rechercher dans tous les sujets publics sur le forum" search: "Rechercher dans tous les sujets publics sur le forum"
tags: "Répertorier toutes les étiquettes du forum" tags: "Répertorier toutes les étiquettes du forum"
@ -185,7 +185,7 @@ fr:
schema: "Rechercher le schéma de la base de données" schema: "Rechercher le schéma de la base de données"
search_settings: "Paramètres du site de recherche" search_settings: "Paramètres du site de recherche"
dall_e: "Générer une image à l'aide de DALL-E 3" dall_e: "Générer une image à l'aide de DALL-E 3"
command_description: tool_description:
read: "Lecture : <a href='%{url}'>%{title}</a>" read: "Lecture : <a href='%{url}'>%{title}</a>"
time: "L'heure (%{timezone}) est %{time}" time: "L'heure (%{timezone}) est %{time}"
summarize: "Résumé de <a href='%{url}'>%{title}</a>" summarize: "Résumé de <a href='%{url}'>%{title}</a>"

View File

@ -10,7 +10,7 @@ gl:
yaxis: "Data" yaxis: "Data"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Buscar" search: "Buscar"
time: "Hora" time: "Hora"
sentiment: sentiment:

View File

@ -192,7 +192,7 @@ he:
topic_not_found: "תקציר לא זמין, לא נמצא נושא!" topic_not_found: "תקציר לא זמין, לא נמצא נושא!"
summarizing: "הנושא מסוכם" summarizing: "הנושא מסוכם"
searching: "חיפוש אחר: %{query}" searching: "חיפוש אחר: %{query}"
command_options: tool_options:
search: search:
search_private: search_private:
name: "חיפוש פרטי" name: "חיפוש פרטי"
@ -203,7 +203,7 @@ he:
base_query: base_query:
name: "שאילתת חיפוש בסיסית" name: "שאילתת חיפוש בסיסית"
description: "שאילתת בסיס לשימוש בעת חיפוש. למשל: #urgent יוסיף את #urgent לשאילתת החיפוש ויכלול רק נושאים עם הקטגוריה או התגית urgent (דחוף)." description: "שאילתת בסיס לשימוש בעת חיפוש. למשל: #urgent יוסיף את #urgent לשאילתת החיפוש ויכלול רק נושאים עם הקטגוריה או התגית urgent (דחוף)."
command_summary: tool_summary:
web_browser: "גלישה באינטרנט" web_browser: "גלישה באינטרנט"
github_search_files: "חיפוש קבצים ב־GitHub" github_search_files: "חיפוש קבצים ב־GitHub"
github_search_code: "חיפוש קוד ב־GitHub" github_search_code: "חיפוש קוד ב־GitHub"
@ -224,7 +224,7 @@ he:
dall_e: "יצירת תמונה" dall_e: "יצירת תמונה"
search_meta_discourse: "חיפוש ב־Meta Discrouse" search_meta_discourse: "חיפוש ב־Meta Discrouse"
javascript_evaluator: "שערוך JavaScript" javascript_evaluator: "שערוך JavaScript"
command_help: tool_help:
web_browser: "גלישה באינטרנט באמצעות בוט בינה מלאכותית" web_browser: "גלישה באינטרנט באמצעות בוט בינה מלאכותית"
github_search_code: "חיפוש אחר קוד במאגר GitHub" github_search_code: "חיפוש אחר קוד במאגר GitHub"
github_search_files: "חיפוש אחר קבצים במאגר GitHub" github_search_files: "חיפוש אחר קבצים במאגר GitHub"
@ -245,7 +245,7 @@ he:
dall_e: "יצירת תמונה באמצעות DALL-E 3" dall_e: "יצירת תמונה באמצעות DALL-E 3"
search_meta_discourse: "חיפוש ב־Meta Discrouse" search_meta_discourse: "חיפוש ב־Meta Discrouse"
javascript_evaluator: "שערוך JavaScript" javascript_evaluator: "שערוך JavaScript"
command_description: tool_description:
web_browser: "קורא את <a href='%{url}'>%{url}</a>" web_browser: "קורא את <a href='%{url}'>%{url}</a>"
github_search_files: "בוצע חיפוש אחר %{keywords} בתוך %{repo}/%{branch}" github_search_files: "בוצע חיפוש אחר %{keywords} בתוך %{repo}/%{branch}"
github_search_code: "בוצע חיפוש אחר %{query} בתוך %{repo}" github_search_code: "בוצע חיפוש אחר %{query} בתוך %{repo}"

View File

@ -10,7 +10,7 @@ hr:
yaxis: "Datum" yaxis: "Datum"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Pretraživanje" search: "Pretraživanje"
time: "Vrijeme" time: "Vrijeme"
summarize: "Rezimirati" summarize: "Rezimirati"

View File

@ -10,7 +10,7 @@ hu:
yaxis: "Dátum" yaxis: "Dátum"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Keresés" search: "Keresés"
tags: "Címkék listázása" tags: "Címkék listázása"
time: "Idő" time: "Idő"

View File

@ -10,7 +10,7 @@ hy:
yaxis: "Ամսաթիվ" yaxis: "Ամսաթիվ"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Որոնում" search: "Որոնում"
time: "Ժամ" time: "Ժամ"
sentiment: sentiment:

View File

@ -71,19 +71,19 @@ id:
ai_bot: ai_bot:
personas: personas:
default_llm_required: "Model LLM default diperlukan sebelum mengaktifkan Obrolan" default_llm_required: "Model LLM default diperlukan sebelum mengaktifkan Obrolan"
command_options: tool_options:
search: search:
search_private: search_private:
name: "Pencarian Pribadi" name: "Pencarian Pribadi"
description: "Sertakan semua topik yang dapat diakses pengguna dalam hasil pencarian (secara bawaan hanya topik publik yang disertakan)" description: "Sertakan semua topik yang dapat diakses pengguna dalam hasil pencarian (secara bawaan hanya topik publik yang disertakan)"
command_summary: tool_summary:
github_search_files: "File pencarian GitHub" github_search_files: "File pencarian GitHub"
search: "Cari" search: "Cari"
time: "Waktu" time: "Waktu"
summarize: "Meringkas" summarize: "Meringkas"
command_help: tool_help:
github_search_files: "Cari file di repositori GitHub" github_search_files: "Cari file di repositori GitHub"
summary: "Meringkas suatu topik" summary: "Meringkas suatu topik"
command_description: tool_description:
github_search_files: "Mencari '%{keywords}' di %{repo}/%{branch}" github_search_files: "Mencari '%{keywords}' di %{repo}/%{branch}"
github_search_code: "Mencari '%{query}' di %{repo}" github_search_code: "Mencari '%{query}' di %{repo}"

View File

@ -151,7 +151,7 @@ it:
topic_not_found: "Riepilogo non disponibile, argomento non trovato!" topic_not_found: "Riepilogo non disponibile, argomento non trovato!"
summarizing: "Riepilogo argomento" summarizing: "Riepilogo argomento"
searching: "Ricerca di: '%{query}'" searching: "Ricerca di: '%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "Numero massimo di risultati" name: "Numero massimo di risultati"
@ -159,7 +159,7 @@ it:
base_query: base_query:
name: "Query di ricerca di base" name: "Query di ricerca di base"
description: "Query di base da utilizzare durante la ricerca. Esempio: \"#urgente\" anteporrà \"#urgente\" alla query di ricerca e includerà solo gli argomenti con la categoria o l'etichetta urgente." description: "Query di base da utilizzare durante la ricerca. Esempio: \"#urgente\" anteporrà \"#urgente\" alla query di ricerca e includerà solo gli argomenti con la categoria o l'etichetta urgente."
command_summary: tool_summary:
categories: "Elenca le categorie" categories: "Elenca le categorie"
search: "Cerca" search: "Cerca"
tags: "Elenca le etichette" tags: "Elenca le etichette"
@ -172,7 +172,7 @@ it:
schema: "Cerca lo schema del database" schema: "Cerca lo schema del database"
search_settings: "Ricerca nelle impostazioni del sito" search_settings: "Ricerca nelle impostazioni del sito"
dall_e: "Genera immagine" dall_e: "Genera immagine"
command_help: tool_help:
categories: "Elenca tutte le categorie visibili pubblicamente sul forum" categories: "Elenca tutte le categorie visibili pubblicamente sul forum"
search: "Cerca tutti gli argomenti pubblici sul forum" search: "Cerca tutti gli argomenti pubblici sul forum"
tags: "Elenca tutte le etichette sul forum" tags: "Elenca tutte le etichette sul forum"
@ -185,7 +185,7 @@ it:
schema: "Cerca lo schema del database" schema: "Cerca lo schema del database"
search_settings: "Cerca le impostazioni del sito" search_settings: "Cerca le impostazioni del sito"
dall_e: "Genera immagine utilizzando DALL-E 3" dall_e: "Genera immagine utilizzando DALL-E 3"
command_description: tool_description:
read: "Lettura: <a href='%{url}'>%{title}</a>" read: "Lettura: <a href='%{url}'>%{title}</a>"
time: "L'orario in %{timezone} è %{time}" time: "L'orario in %{timezone} è %{time}"
summarize: "Riassunto di <a href='%{url}'>%{title}</a>" summarize: "Riassunto di <a href='%{url}'>%{title}</a>"

View File

@ -151,7 +151,7 @@ ja:
topic_not_found: "要約がありません。トピックが見つかりません!" topic_not_found: "要約がありません。トピックが見つかりません!"
summarizing: "トピックの要約を生成中" summarizing: "トピックの要約を生成中"
searching: "検索中: '%{query}'" searching: "検索中: '%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "結果の最大件数" name: "結果の最大件数"
@ -159,7 +159,7 @@ ja:
base_query: base_query:
name: "ベース検索クエリ" name: "ベース検索クエリ"
description: "検索時に使用するベースクエリ。例: '#urgent' は検索クエリの先頭に '#urgent' を追加し、緊急のカテゴリまたはタグを持つトピックのみが含まれます。" description: "検索時に使用するベースクエリ。例: '#urgent' は検索クエリの先頭に '#urgent' を追加し、緊急のカテゴリまたはタグを持つトピックのみが含まれます。"
command_summary: tool_summary:
categories: "カテゴリをリスト表示" categories: "カテゴリをリスト表示"
search: "検索" search: "検索"
tags: "タグをリスト" tags: "タグをリスト"
@ -172,7 +172,7 @@ ja:
schema: "データベーススキーマを検索" schema: "データベーススキーマを検索"
search_settings: "サイト設定を検索中" search_settings: "サイト設定を検索中"
dall_e: "画像を生成" dall_e: "画像を生成"
command_help: tool_help:
categories: "フォーラムのすべての公開カテゴリをリストします" categories: "フォーラムのすべての公開カテゴリをリストします"
search: "フォーラムのすべての公開トピックを検索します" search: "フォーラムのすべての公開トピックを検索します"
tags: "フォーラムのすべてのタグをリストします" tags: "フォーラムのすべてのタグをリストします"
@ -185,7 +185,7 @@ ja:
schema: "データベーススキーマを検索します" schema: "データベーススキーマを検索します"
search_settings: "サイト設定を検索します" search_settings: "サイト設定を検索します"
dall_e: "DALL-E 3 を使って画像を生成します" dall_e: "DALL-E 3 を使って画像を生成します"
command_description: tool_description:
read: "読み取り中: <a href='%{url}'>%{title}</a>" read: "読み取り中: <a href='%{url}'>%{title}</a>"
time: "%{timezone} の時刻は %{time} です" time: "%{timezone} の時刻は %{time} です"
summarize: "<a href='%{url}'>%{title}</a> の要約" summarize: "<a href='%{url}'>%{title}</a> の要約"

View File

@ -10,7 +10,7 @@ ko:
yaxis: "날짜" yaxis: "날짜"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "검색" search: "검색"
time: "시간" time: "시간"
summarize: "요약하기" summarize: "요약하기"

View File

@ -17,7 +17,7 @@ lt:
image_caption: image_caption:
attribution: "Antraštė teikiama AI" attribution: "Antraštė teikiama AI"
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Paieška" search: "Paieška"
time: "Laikas" time: "Laikas"
summarize: "Apibendrinti" summarize: "Apibendrinti"

View File

@ -10,6 +10,6 @@ lv:
yaxis: "Datums" yaxis: "Datums"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Meklēt" search: "Meklēt"
time: "Laiks" time: "Laiks"

View File

@ -10,6 +10,6 @@ nb_NO:
yaxis: "Dato" yaxis: "Dato"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Søk" search: "Søk"
time: "Tid" time: "Tid"

View File

@ -151,7 +151,7 @@ nl:
topic_not_found: "Samenvatting niet beschikbaar, topic niet gevonden!" topic_not_found: "Samenvatting niet beschikbaar, topic niet gevonden!"
summarizing: "Topic samenvatten" summarizing: "Topic samenvatten"
searching: "Zoeken naar: '%{query}'" searching: "Zoeken naar: '%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "Maximaal aantal resultaten" name: "Maximaal aantal resultaten"
@ -159,7 +159,7 @@ nl:
base_query: base_query:
name: "Basiszoekopdracht" name: "Basiszoekopdracht"
description: "Basisquery om te gebruiken bij het zoeken. Voorbeeld: '#urgent' voegt '#urgent' toe aan de zoekquery en neemt alleen topics mee met de categorie of tag 'urgent'." description: "Basisquery om te gebruiken bij het zoeken. Voorbeeld: '#urgent' voegt '#urgent' toe aan de zoekquery en neemt alleen topics mee met de categorie of tag 'urgent'."
command_summary: tool_summary:
categories: "Categorieën weergeven" categories: "Categorieën weergeven"
search: "Zoeken" search: "Zoeken"
tags: "Tags weergeven" tags: "Tags weergeven"
@ -172,7 +172,7 @@ nl:
schema: "Databaseschema opzoeken" schema: "Databaseschema opzoeken"
search_settings: "Zoeken in site-instellingen" search_settings: "Zoeken in site-instellingen"
dall_e: "Afbeelding genereren" dall_e: "Afbeelding genereren"
command_help: tool_help:
categories: "Geef een lijst weer van alle openbaar zichtbare categorieën op het forum" categories: "Geef een lijst weer van alle openbaar zichtbare categorieën op het forum"
search: "Doorzoek alle openbare topics op het forum" search: "Doorzoek alle openbare topics op het forum"
tags: "Geef een lijst weer van alle tags op het forum" tags: "Geef een lijst weer van alle tags op het forum"
@ -185,7 +185,7 @@ nl:
schema: "Zoek een databaseschema op" schema: "Zoek een databaseschema op"
search_settings: "Zoek site-instellingen" search_settings: "Zoek site-instellingen"
dall_e: "Genereer een afbeelding met DALL-E 3" dall_e: "Genereer een afbeelding met DALL-E 3"
command_description: tool_description:
read: "Lezen: <a href='%{url}'>%{title}</a>" read: "Lezen: <a href='%{url}'>%{title}</a>"
time: "De tijd in %{timezone} is %{time}" time: "De tijd in %{timezone} is %{time}"
summarize: "<a href='%{url}'>%{title}</a> samengevat" summarize: "<a href='%{url}'>%{title}</a> samengevat"

View File

@ -150,7 +150,7 @@ pl_PL:
topic_not_found: "Podsumowanie niedostępne, nie znaleziono tematu!" topic_not_found: "Podsumowanie niedostępne, nie znaleziono tematu!"
summarizing: "Podsumowanie tematu" summarizing: "Podsumowanie tematu"
searching: "Wyszukiwanie: '%{query}'" searching: "Wyszukiwanie: '%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "Maksymalna liczba wyników" name: "Maksymalna liczba wyników"
@ -158,7 +158,7 @@ pl_PL:
base_query: base_query:
name: "Podstawowe zapytanie wyszukiwania" name: "Podstawowe zapytanie wyszukiwania"
description: "Podstawowe zapytanie używane podczas wyszukiwania. Przykład: '#pilne' spowoduje dodanie '#pilne' do zapytania wyszukiwania i uwzględnienie tylko tematów z kategorią lub tagiem pilne." description: "Podstawowe zapytanie używane podczas wyszukiwania. Przykład: '#pilne' spowoduje dodanie '#pilne' do zapytania wyszukiwania i uwzględnienie tylko tematów z kategorią lub tagiem pilne."
command_summary: tool_summary:
random_picker: "Losowy selektor" random_picker: "Losowy selektor"
categories: "Wymień kategorie" categories: "Wymień kategorie"
search: "Szukaj" search: "Szukaj"
@ -172,7 +172,7 @@ pl_PL:
schema: "Wyszukaj schemat bazy danych" schema: "Wyszukaj schemat bazy danych"
search_settings: "Wyszukiwanie ustawień witryny" search_settings: "Wyszukiwanie ustawień witryny"
dall_e: "Wygeneruj obraz" dall_e: "Wygeneruj obraz"
command_help: tool_help:
random_picker: "Wybierz losową liczbę lub losowy element listy" random_picker: "Wybierz losową liczbę lub losowy element listy"
categories: "Wyświetl wszystkie publicznie widoczne kategorie na forum" categories: "Wyświetl wszystkie publicznie widoczne kategorie na forum"
search: "Przeszukaj wszystkie publiczne tematy na forum" search: "Przeszukaj wszystkie publiczne tematy na forum"
@ -186,7 +186,7 @@ pl_PL:
schema: "Wyszukaj schemat bazy danych" schema: "Wyszukaj schemat bazy danych"
search_settings: "Wyszukaj ustawienia witryny" search_settings: "Wyszukaj ustawienia witryny"
dall_e: "Wygeneruj obraz za pomocą DALL-E 3" dall_e: "Wygeneruj obraz za pomocą DALL-E 3"
command_description: tool_description:
random_picker: "Wybieranie z %{options}, wybrane: %{result}" random_picker: "Wybieranie z %{options}, wybrane: %{result}"
read: "Czytanie: <a href='%{url}'>%{title}</a>" read: "Czytanie: <a href='%{url}'>%{title}</a>"
time: "Czas w %{timezone} wynosi %{time}" time: "Czas w %{timezone} wynosi %{time}"

View File

@ -10,7 +10,7 @@ pt:
yaxis: "Data" yaxis: "Data"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Pesquisar" search: "Pesquisar"
time: "Hora" time: "Hora"
summarize: "Resumir" summarize: "Resumir"

View File

@ -151,7 +151,7 @@ pt_BR:
topic_not_found: "Resumo indisponível, tópico não encontrado!" topic_not_found: "Resumo indisponível, tópico não encontrado!"
summarizing: "Resumindo tópico" summarizing: "Resumindo tópico"
searching: "Pesquisando: \"%{query}\"" searching: "Pesquisando: \"%{query}\""
command_options: tool_options:
search: search:
max_results: max_results:
name: "O número máximo de resultados" name: "O número máximo de resultados"
@ -159,7 +159,7 @@ pt_BR:
base_query: base_query:
name: "Consulta de pesquisa básica" name: "Consulta de pesquisa básica"
description: "A consulta de base para usar ao pesquisar. Exemplo: \"#urgent\" precederá \"#urgent\" para a consulta de pesquisa e incluirá apenas tópicos com a etiqueta ou categoria urgente." description: "A consulta de base para usar ao pesquisar. Exemplo: \"#urgent\" precederá \"#urgent\" para a consulta de pesquisa e incluirá apenas tópicos com a etiqueta ou categoria urgente."
command_summary: tool_summary:
categories: "Listar categorias" categories: "Listar categorias"
search: "Pesquisar" search: "Pesquisar"
tags: "Listar etiquetas" tags: "Listar etiquetas"
@ -172,7 +172,7 @@ pt_BR:
schema: "Procurar esquema de banco de dados" schema: "Procurar esquema de banco de dados"
search_settings: "Pesquisando configurações do site" search_settings: "Pesquisando configurações do site"
dall_e: "Gerar imagem" dall_e: "Gerar imagem"
command_help: tool_help:
categories: "Listar todas as categorias visíveis publicamente no fórum" categories: "Listar todas as categorias visíveis publicamente no fórum"
search: "Pesquisar todos os tópicos públicos no fórum" search: "Pesquisar todos os tópicos públicos no fórum"
tags: "Listar todas as etiquetas no fórum" tags: "Listar todas as etiquetas no fórum"
@ -185,7 +185,7 @@ pt_BR:
schema: "Procurar esquema de banco de dados" schema: "Procurar esquema de banco de dados"
search_settings: "Pesquisar configurações do site" search_settings: "Pesquisar configurações do site"
dall_e: "Gerar imagem usando DALL-E 3" dall_e: "Gerar imagem usando DALL-E 3"
command_description: tool_description:
read: "Lendo: <a href='%{url}'>%{title}</a>" read: "Lendo: <a href='%{url}'>%{title}</a>"
time: "A hora em %{timezone} é %{time}" time: "A hora em %{timezone} é %{time}"
summarize: "Resumo de <a href='%{url}'>%{title}</a>" summarize: "Resumo de <a href='%{url}'>%{title}</a>"

View File

@ -10,7 +10,7 @@ ro:
yaxis: "Dată" yaxis: "Dată"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Caută" search: "Caută"
time: "Oră" time: "Oră"
summarize: "Rezumat" summarize: "Rezumat"

View File

@ -151,7 +151,7 @@ ru:
topic_not_found: "Сводка недоступна: тема не найдена!" topic_not_found: "Сводка недоступна: тема не найдена!"
summarizing: "Аналитик темы" summarizing: "Аналитик темы"
searching: "Поиск: '%{query}'" searching: "Поиск: '%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "Максимальное количество результатов" name: "Максимальное количество результатов"
@ -159,7 +159,7 @@ ru:
base_query: base_query:
name: "Базовый поисковый запрос" name: "Базовый поисковый запрос"
description: "Базовый запрос, используемый при поиске. Пример: '#urgent' добавит '#urgent' к поисковому запросу и будет включать только темы со срочной категорией или тегом." description: "Базовый запрос, используемый при поиске. Пример: '#urgent' добавит '#urgent' к поисковому запросу и будет включать только темы со срочной категорией или тегом."
command_summary: tool_summary:
categories: "Вывод списка категорий" categories: "Вывод списка категорий"
search: "Поиск" search: "Поиск"
tags: "Вывод списка тегов" tags: "Вывод списка тегов"
@ -172,7 +172,7 @@ ru:
schema: "Найти схему базы данных" schema: "Найти схему базы данных"
search_settings: "Поиск настроек сайта" search_settings: "Поиск настроек сайта"
dall_e: "Сгенерировать изображение" dall_e: "Сгенерировать изображение"
command_help: tool_help:
categories: "Вывод всех общедоступных категорий на форуме" categories: "Вывод всех общедоступных категорий на форуме"
search: "Поиск по всем общедоступным темам на форуме" search: "Поиск по всем общедоступным темам на форуме"
tags: "Вывод всех тегов на форуме" tags: "Вывод всех тегов на форуме"
@ -185,7 +185,7 @@ ru:
schema: "Найти схему базы данных" schema: "Найти схему базы данных"
search_settings: "Настройки поиска по сайту" search_settings: "Настройки поиска по сайту"
dall_e: "Создать изображение с помощью DALL-E 3" dall_e: "Создать изображение с помощью DALL-E 3"
command_description: tool_description:
read: "Чтение: <a href='%{url}'>%{title}</a>" read: "Чтение: <a href='%{url}'>%{title}</a>"
time: "Время по часовому поясу %{timezone} — %{time}" time: "Время по часовому поясу %{timezone} — %{time}"
summarize: "Получена сводка: <a href='%{url}'>%{title}</a>" summarize: "Получена сводка: <a href='%{url}'>%{title}</a>"

View File

@ -10,7 +10,7 @@ sk:
yaxis: "Dátum" yaxis: "Dátum"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Hľadať" search: "Hľadať"
tags: "Zoznam značiek" tags: "Zoznam značiek"
time: "Čas" time: "Čas"

View File

@ -10,6 +10,6 @@ sl:
yaxis: "Datum" yaxis: "Datum"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Išči" search: "Išči"
time: "Čas" time: "Čas"

View File

@ -7,6 +7,6 @@
sq: sq:
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Kërko" search: "Kërko"
time: "Koha" time: "Koha"

View File

@ -7,6 +7,6 @@
sr: sr:
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Pretraži" search: "Pretraži"
time: "Vreme" time: "Vreme"

View File

@ -10,7 +10,7 @@ sv:
yaxis: "Datum" yaxis: "Datum"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Sök" search: "Sök"
time: "Tid" time: "Tid"
summarize: "Sammanfatta" summarize: "Sammanfatta"

View File

@ -10,6 +10,6 @@ sw:
yaxis: "Tarehe" yaxis: "Tarehe"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Tafuta" search: "Tafuta"
time: "Muda" time: "Muda"

View File

@ -10,7 +10,7 @@ te:
yaxis: "తేదీ" yaxis: "తేదీ"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "వెతుకు" search: "వెతుకు"
time: "కాలం" time: "కాలం"
sentiment: sentiment:

View File

@ -10,6 +10,6 @@ th:
yaxis: "วันที่" yaxis: "วันที่"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "ค้นหา" search: "ค้นหา"
time: "เวลา" time: "เวลา"

View File

@ -187,7 +187,7 @@ tr_TR:
topic_not_found: "Özet mevcut değil, konu bulunamadı!" topic_not_found: "Özet mevcut değil, konu bulunamadı!"
summarizing: "Konu özetleniyor" summarizing: "Konu özetleniyor"
searching: "Aranıyor: '%{query}'" searching: "Aranıyor: '%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "Maksimum sonuç sayısı" name: "Maksimum sonuç sayısı"
@ -195,7 +195,7 @@ tr_TR:
base_query: base_query:
name: "Temel Arama Sorgusu" name: "Temel Arama Sorgusu"
description: "Arama yaparken kullanılacak temel sorgu. Örnek: '#urgent', arama sorgusuna '#urgent' ekler ve yalnızca acil kategorisine veya etiketine sahip konuları içerir." description: "Arama yaparken kullanılacak temel sorgu. Örnek: '#urgent', arama sorgusuna '#urgent' ekler ve yalnızca acil kategorisine veya etiketine sahip konuları içerir."
command_summary: tool_summary:
web_browser: "Web'e Gözat" web_browser: "Web'e Gözat"
github_search_code: "GitHub kodu arama" github_search_code: "GitHub kodu arama"
github_file_content: "GitHub dosya içeriği" github_file_content: "GitHub dosya içeriği"
@ -214,7 +214,7 @@ tr_TR:
search_settings: "Site ayarları aranıyor" search_settings: "Site ayarları aranıyor"
dall_e: "Görüntü oluştur" dall_e: "Görüntü oluştur"
search_meta_discourse: "Discourse Metada arama yapın" search_meta_discourse: "Discourse Metada arama yapın"
command_help: tool_help:
web_browser: "Yapay Zeka Botunu kullanarak web sayfasına göz atın" web_browser: "Yapay Zeka Botunu kullanarak web sayfasına göz atın"
github_search_code: "GitHub deposunda kod arama" github_search_code: "GitHub deposunda kod arama"
github_file_content: "Bir GitHub deposundan dosyaların içeriğini alma" github_file_content: "Bir GitHub deposundan dosyaların içeriğini alma"
@ -233,7 +233,7 @@ tr_TR:
search_settings: "Site ayarlarını ara" search_settings: "Site ayarlarını ara"
dall_e: "DALL-E 3 kullanarak görüntü oluştur" dall_e: "DALL-E 3 kullanarak görüntü oluştur"
search_meta_discourse: "Discourse Metada arama yapın" search_meta_discourse: "Discourse Metada arama yapın"
command_description: tool_description:
web_browser: "Okunuyor <a href='%{url}'>%{url}</a>" web_browser: "Okunuyor <a href='%{url}'>%{url}</a>"
github_search_code: "%{repo} içinde '%{query}' araması yapıldı" github_search_code: "%{repo} içinde '%{query}' araması yapıldı"
github_pull_request_diff: "<a href='%{url}'>%{repo} %{pull_id}</a>" github_pull_request_diff: "<a href='%{url}'>%{repo} %{pull_id}</a>"

View File

@ -10,7 +10,7 @@ ug:
yaxis: "چېسلا" yaxis: "چېسلا"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "ئىزدە" search: "ئىزدە"
tags: "بەلگە تىزىمىنى كۆرسىتىدۇ" tags: "بەلگە تىزىمىنى كۆرسىتىدۇ"
time: "ۋاقىت" time: "ۋاقىت"

View File

@ -10,7 +10,7 @@ uk:
yaxis: "Дата" yaxis: "Дата"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Пошук" search: "Пошук"
tags: "Список тегів" tags: "Список тегів"
time: "Час" time: "Час"

View File

@ -10,7 +10,7 @@ ur:
yaxis: "تاریخ" yaxis: "تاریخ"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "تلاش کریں" search: "تلاش کریں"
time: "وقت" time: "وقت"
summarize: "خلاصہ" summarize: "خلاصہ"

View File

@ -10,7 +10,7 @@ vi:
yaxis: "Ngày" yaxis: "Ngày"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "Tìm kiếm" search: "Tìm kiếm"
time: "Thời gian" time: "Thời gian"
summarize: "Tóm tắt" summarize: "Tóm tắt"

View File

@ -154,7 +154,7 @@ zh_CN:
topic_not_found: "总结不可用,找不到话题!" topic_not_found: "总结不可用,找不到话题!"
summarizing: "正在总结话题" summarizing: "正在总结话题"
searching: "搜索:'%{query}'" searching: "搜索:'%{query}'"
command_options: tool_options:
search: search:
max_results: max_results:
name: "最大结果数" name: "最大结果数"
@ -162,7 +162,7 @@ zh_CN:
base_query: base_query:
name: "基本搜索查询" name: "基本搜索查询"
description: "搜索时要使用的基本查询。示例:'#urgent' 会在搜索查询前面加上 '#urgent',并且仅包含具有紧急类别或标签的话题。" description: "搜索时要使用的基本查询。示例:'#urgent' 会在搜索查询前面加上 '#urgent',并且仅包含具有紧急类别或标签的话题。"
command_summary: tool_summary:
categories: "列出类别" categories: "列出类别"
search: "搜索" search: "搜索"
tags: "列出标签" tags: "列出标签"
@ -175,7 +175,7 @@ zh_CN:
schema: "查找数据库架构" schema: "查找数据库架构"
search_settings: "正在搜索站点设置" search_settings: "正在搜索站点设置"
dall_e: "生成图片" dall_e: "生成图片"
command_help: tool_help:
categories: "列出论坛上所有公开可见的类别" categories: "列出论坛上所有公开可见的类别"
search: "搜索论坛上的所有公共话题" search: "搜索论坛上的所有公共话题"
tags: "列出论坛上的所有标签" tags: "列出论坛上的所有标签"
@ -188,7 +188,7 @@ zh_CN:
schema: "查找数据库架构" schema: "查找数据库架构"
search_settings: "搜索站点设置" search_settings: "搜索站点设置"
dall_e: "使用 DALL-E 3 生成图片" dall_e: "使用 DALL-E 3 生成图片"
command_description: tool_description:
read: "阅读:<a href='%{url}'>%{title}</a>" read: "阅读:<a href='%{url}'>%{title}</a>"
time: "%{timezone} 的时间为 %{time}" time: "%{timezone} 的时间为 %{time}"
summarize: "已总结 <a href='%{url}'>%{title}</a>" summarize: "已总结 <a href='%{url}'>%{title}</a>"

View File

@ -10,7 +10,7 @@ zh_TW:
yaxis: "日期" yaxis: "日期"
discourse_ai: discourse_ai:
ai_bot: ai_bot:
command_summary: tool_summary:
search: "搜尋" search: "搜尋"
time: "時間" time: "時間"
summarize: "總結" summarize: "總結"

View File

@ -32,7 +32,7 @@ DiscourseAi::AiBot::Personas::Persona.system_personas.each do |persona_class, id
persona.system = true persona.system = true
instance = persona_class.new instance = persona_class.new
persona.commands = instance.tools.map { |tool| tool.to_s.split("::").last } persona.tools = instance.tools.map { |tool| tool.to_s.split("::").last }
persona.system_prompt = instance.system_prompt persona.system_prompt = instance.system_prompt
persona.top_p = instance.top_p persona.top_p = instance.top_p
persona.temperature = instance.temperature persona.temperature = instance.temperature

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
class ToolDetailsAndCommandRemoval < ActiveRecord::Migration[7.0]
def change
add_column :ai_personas, :tool_details, :boolean, default: true, null: false
add_column :ai_personas, :tools, :json, null: false, default: []
Migration::ColumnDropper.mark_readonly(:ai_personas, :commands)
execute <<~SQL
UPDATE ai_personas
SET tools = commands
SQL
end
end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
class DropCommandsFromAiPersonas < ActiveRecord::Migration[7.0]
def down
raise ActiveRecord::IrreversibleMigration
end
def up
Migration::ColumnDropper.execute_drop(:ai_personas, [:commands])
end
end

View File

@ -420,11 +420,13 @@ module DiscourseAi
Discourse.redis.setex(redis_stream_key, 60, 1) Discourse.redis.setex(redis_stream_key, 60, 1)
end end
context[:skip_tool_details] ||= !bot.persona.class.tool_details
new_custom_prompts = new_custom_prompts =
bot.reply(context) do |partial, cancel, placeholder| bot.reply(context) do |partial, cancel, placeholder|
reply << partial reply << partial
raw = reply.dup raw = reply.dup
raw << "\n\n" << placeholder if placeholder.present? raw << "\n\n" << placeholder if placeholder.present? && !context[:skip_tool_details]
if stream_reply && !Discourse.redis.get(redis_stream_key) if stream_reply && !Discourse.redis.get(redis_stream_key)
cancel&.call cancel&.call

View File

@ -13,11 +13,11 @@ module DiscourseAi
end end
def localized_name def localized_name
I18n.t("discourse_ai.ai_bot.command_options.#{tool.signature[:name]}.#{name}.name") I18n.t("discourse_ai.ai_bot.tool_options.#{tool.signature[:name]}.#{name}.name")
end end
def localized_description def localized_description
I18n.t("discourse_ai.ai_bot.command_options.#{tool.signature[:name]}.#{name}.description") I18n.t("discourse_ai.ai_bot.tool_options.#{tool.signature[:name]}.#{name}.description")
end end
end end
end end

View File

@ -22,7 +22,7 @@ module DiscourseAi
end end
def help def help
I18n.t("discourse_ai.ai_bot.command_help.#{signature[:name]}") I18n.t("discourse_ai.ai_bot.tool_help.#{signature[:name]}")
end end
def custom_system_message def custom_system_message
@ -54,15 +54,15 @@ module DiscourseAi
end end
def summary def summary
I18n.t("discourse_ai.ai_bot.command_summary.#{name}") I18n.t("discourse_ai.ai_bot.tool_summary.#{name}")
end end
def details def details
I18n.t("discourse_ai.ai_bot.command_description.#{name}", description_args) I18n.t("discourse_ai.ai_bot.tool_description.#{name}", description_args)
end end
def help def help
I18n.t("discourse_ai.ai_bot.command_help.#{name}") I18n.t("discourse_ai.ai_bot.tool_help.#{name}")
end end
def options def options

View File

@ -226,7 +226,7 @@ RSpec.describe DiscourseAi::AiBot::Personas::Persona do
name: "zzzpun_bot", name: "zzzpun_bot",
description: "you write puns", description: "you write puns",
system_prompt: "you are pun bot", system_prompt: "you are pun bot",
commands: ["ImageCommand"], tools: ["Image"],
allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]],
) )

View File

@ -290,7 +290,7 @@ RSpec.describe DiscourseAi::AiBot::Playground do
end end
it "can run tools" do it "can run tools" do
persona.update!(commands: ["TimeCommand"]) persona.update!(tools: ["Time"])
responses = [ responses = [
"<function_calls><invoke><tool_name>time</tool_name><tool_id>time</tool_id><parameters><timezone>Buenos Aires</timezone></parameters></invoke></function_calls>", "<function_calls><invoke><tool_name>time</tool_name><tool_id>time</tool_id><parameters><timezone>Buenos Aires</timezone></parameters></invoke></function_calls>",
@ -590,6 +590,34 @@ RSpec.describe DiscourseAi::AiBot::Playground do
expect(last_post.raw).to include("I found stuff") expect(last_post.raw).to include("I found stuff")
end end
it "supports disabling tool details" do
persona = Fabricate(:ai_persona, tool_details: false, tools: ["Search"])
bot = DiscourseAi::AiBot::Bot.as(bot_user, persona: persona.class_instance.new)
playground = described_class.new(bot)
response1 = (<<~TXT).strip
<function_calls>
<invoke>
<tool_name>search</tool_name>
<tool_id>search</tool_id>
<parameters>
<search_query>testing various things</search_query>
</parameters>
</invoke>
</function_calls>
TXT
response2 = "I found stuff"
DiscourseAi::Completions::Llm.with_prepared_responses([response1, response2]) do
playground.reply_to(third_post)
end
last_post = third_post.topic.reload.posts.order(:post_number).last
expect(last_post.raw).to eq("I found stuff")
end
it "does not include placeholders in conversation context but includes all completions" do it "does not include placeholders in conversation context but includes all completions" do
response1 = (<<~TXT).strip response1 = (<<~TXT).strip
<function_calls> <function_calls>

View File

@ -7,7 +7,7 @@ RSpec.describe AiPersona do
name: "test", name: "test",
description: "test", description: "test",
system_prompt: "test", system_prompt: "test",
commands: [], tools: [],
allowed_group_ids: [], allowed_group_ids: [],
) )
@ -30,7 +30,7 @@ RSpec.describe AiPersona do
name: "test", name: "test",
description: "test", description: "test",
system_prompt: "test", system_prompt: "test",
commands: [], tools: [],
allowed_group_ids: [], allowed_group_ids: [],
) )
@ -47,7 +47,7 @@ RSpec.describe AiPersona do
name: "test", name: "test",
description: "test", description: "test",
system_prompt: "test", system_prompt: "test",
commands: [], tools: [],
allowed_group_ids: [], allowed_group_ids: [],
rag_chunk_tokens: 10, rag_chunk_tokens: 10,
rag_chunk_overlap_tokens: 5, rag_chunk_overlap_tokens: 5,
@ -94,7 +94,7 @@ RSpec.describe AiPersona do
name: "test", name: "test",
description: "test", description: "test",
system_prompt: "test", system_prompt: "test",
commands: [], tools: [],
allowed_group_ids: [], allowed_group_ids: [],
default_llm: "anthropic:claude-2", default_llm: "anthropic:claude-2",
max_context_posts: 3, max_context_posts: 3,
@ -135,7 +135,7 @@ RSpec.describe AiPersona do
name: "pun_bot", name: "pun_bot",
description: "you write puns", description: "you write puns",
system_prompt: "you are pun bot", system_prompt: "you are pun bot",
commands: ["ImageCommand"], tools: ["ImageCommand"],
allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]], allowed_group_ids: [Group::AUTO_GROUPS[:trust_level_0]],
) )

View File

@ -17,7 +17,7 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do
expect(response).to be_successful expect(response).to be_successful
expect(response.parsed_body["ai_personas"].length).to eq(AiPersona.count) expect(response.parsed_body["ai_personas"].length).to eq(AiPersona.count)
expect(response.parsed_body["meta"]["commands"].length).to eq( expect(response.parsed_body["meta"]["tools"].length).to eq(
DiscourseAi::AiBot::Personas::Persona.all_available_tools.length, DiscourseAi::AiBot::Personas::Persona.all_available_tools.length,
) )
end end
@ -33,13 +33,13 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do
) )
end end
it "returns commands options with each command" do it "returns tool options with each tool" do
persona1 = Fabricate(:ai_persona, name: "search1", commands: ["SearchCommand"]) persona1 = Fabricate(:ai_persona, name: "search1", tools: ["SearchCommand"])
persona2 = persona2 =
Fabricate( Fabricate(
:ai_persona, :ai_persona,
name: "search2", name: "search2",
commands: [["SearchCommand", { base_query: "test" }]], tools: [["SearchCommand", { base_query: "test" }]],
mentionable: true, mentionable: true,
default_llm: "anthropic:claude-2", default_llm: "anthropic:claude-2",
) )
@ -56,36 +56,36 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do
expect(serializer_persona2["user_id"]).to eq(persona2.user_id) expect(serializer_persona2["user_id"]).to eq(persona2.user_id)
expect(serializer_persona2["user"]["id"]).to eq(persona2.user_id) expect(serializer_persona2["user"]["id"]).to eq(persona2.user_id)
commands = response.parsed_body["meta"]["commands"] tools = response.parsed_body["meta"]["tools"]
search_command = commands.find { |c| c["id"] == "Search" } search_tool = tools.find { |c| c["id"] == "Search" }
expect(search_command["help"]).to eq(I18n.t("discourse_ai.ai_bot.command_help.search")) expect(search_tool["help"]).to eq(I18n.t("discourse_ai.ai_bot.tool_help.search"))
expect(search_command["options"]).to eq( expect(search_tool["options"]).to eq(
{ {
"base_query" => { "base_query" => {
"type" => "string", "type" => "string",
"name" => I18n.t("discourse_ai.ai_bot.command_options.search.base_query.name"), "name" => I18n.t("discourse_ai.ai_bot.tool_options.search.base_query.name"),
"description" => "description" =>
I18n.t("discourse_ai.ai_bot.command_options.search.base_query.description"), I18n.t("discourse_ai.ai_bot.tool_options.search.base_query.description"),
}, },
"max_results" => { "max_results" => {
"type" => "integer", "type" => "integer",
"name" => I18n.t("discourse_ai.ai_bot.command_options.search.max_results.name"), "name" => I18n.t("discourse_ai.ai_bot.tool_options.search.max_results.name"),
"description" => "description" =>
I18n.t("discourse_ai.ai_bot.command_options.search.max_results.description"), I18n.t("discourse_ai.ai_bot.tool_options.search.max_results.description"),
}, },
"search_private" => { "search_private" => {
"type" => "boolean", "type" => "boolean",
"name" => I18n.t("discourse_ai.ai_bot.command_options.search.search_private.name"), "name" => I18n.t("discourse_ai.ai_bot.tool_options.search.search_private.name"),
"description" => "description" =>
I18n.t("discourse_ai.ai_bot.command_options.search.search_private.description"), I18n.t("discourse_ai.ai_bot.tool_options.search.search_private.description"),
}, },
}, },
) )
expect(serializer_persona1["commands"]).to eq(["SearchCommand"]) expect(serializer_persona1["tools"]).to eq(["SearchCommand"])
expect(serializer_persona2["commands"]).to eq([["SearchCommand", { "base_query" => "test" }]]) expect(serializer_persona2["tools"]).to eq([["SearchCommand", { "base_query" => "test" }]])
end end
context "with translations" do context "with translations" do
@ -160,7 +160,7 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do
name: "superbot", name: "superbot",
description: "Assists with tasks", description: "Assists with tasks",
system_prompt: "you are a helpful bot", system_prompt: "you are a helpful bot",
commands: [["search", { "base_query" => "test" }]], tools: [["search", { "base_query" => "test" }]],
top_p: 0.1, top_p: 0.1,
temperature: 0.5, temperature: 0.5,
mentionable: true, mentionable: true,
@ -186,7 +186,7 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do
persona = AiPersona.find(persona_json["id"]) persona = AiPersona.find(persona_json["id"])
expect(persona.commands).to eq([["search", { "base_query" => "test" }]]) expect(persona.tools).to eq([["search", { "base_query" => "test" }]])
expect(persona.top_p).to eq(0.1) expect(persona.top_p).to eq(0.1)
expect(persona.temperature).to eq(0.5) expect(persona.temperature).to eq(0.5)
}.to change(AiPersona, :count).by(1) }.to change(AiPersona, :count).by(1)
@ -286,7 +286,7 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do
ai_persona: { ai_persona: {
name: "SuperBot", name: "SuperBot",
enabled: false, enabled: false,
commands: ["search"], tools: ["search"],
}, },
} }
@ -296,7 +296,7 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do
ai_persona.reload ai_persona.reload
expect(ai_persona.name).to eq("SuperBot") expect(ai_persona.name).to eq("SuperBot")
expect(ai_persona.enabled).to eq(false) expect(ai_persona.enabled).to eq(false)
expect(ai_persona.commands).to eq(["search"]) expect(ai_persona.tools).to eq(["search"])
end end
end end
@ -314,11 +314,11 @@ RSpec.describe DiscourseAi::Admin::AiPersonasController do
expect(response.parsed_body["errors"].join).not_to include("en.discourse") expect(response.parsed_body["errors"].join).not_to include("en.discourse")
end end
it "does not allow editing of commands" do it "does not allow editing of tools" do
put "/admin/plugins/discourse-ai/ai-personas/#{DiscourseAi::AiBot::Personas::Persona.system_personas.values.first}.json", put "/admin/plugins/discourse-ai/ai-personas/#{DiscourseAi::AiBot::Personas::Persona.system_personas.values.first}.json",
params: { params: {
ai_persona: { ai_persona: {
commands: %w[SearchCommand ImageCommand], tools: %w[SearchCommand ImageCommand],
}, },
} }

View File

@ -38,9 +38,9 @@ RSpec.describe "AI personas", type: :system, js: true do
find(".ai-persona-editor__description").fill_in(with: "I am a test persona") find(".ai-persona-editor__description").fill_in(with: "I am a test persona")
find(".ai-persona-editor__system_prompt").fill_in(with: "You are a helpful bot") find(".ai-persona-editor__system_prompt").fill_in(with: "You are a helpful bot")
command_selector = PageObjects::Components::SelectKit.new(".ai-persona-editor__commands") tool_selector = PageObjects::Components::SelectKit.new(".ai-persona-editor__tools")
command_selector.expand tool_selector.expand
command_selector.select_row_by_value("Read") tool_selector.select_row_by_value("Read")
find(".ai-persona-editor__save").click() find(".ai-persona-editor__save").click()
@ -52,7 +52,7 @@ RSpec.describe "AI personas", type: :system, js: true do
expect(persona.name).to eq("Test Persona") expect(persona.name).to eq("Test Persona")
expect(persona.description).to eq("I am a 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.system_prompt).to eq("You are a helpful bot")
expect(persona.commands).to eq(["Read"]) expect(persona.tools).to eq(["Read"])
end end
it "will not allow deletion or editing of system personas" do it "will not allow deletion or editing of system personas" do

View File

@ -4,26 +4,22 @@ import AiPersona from "discourse/plugins/discourse-ai/discourse/admin/models/ai-
module("Discourse AI | Unit | Model | ai-persona", function () { module("Discourse AI | Unit | Model | ai-persona", function () {
test("init properties", function (assert) { test("init properties", function (assert) {
const properties = { const properties = {
commands: [ tools: [
["CommandName", { option1: "value1", option2: "value2" }], ["ToolName", { option1: "value1", option2: "value2" }],
"CommandName2", "ToolName2",
"CommandName3", "ToolName3",
], ],
}; };
const aiPersona = AiPersona.create(properties); const aiPersona = AiPersona.create(properties);
assert.deepEqual(aiPersona.commands, [ assert.deepEqual(aiPersona.tools, ["ToolName", "ToolName2", "ToolName3"]);
"CommandName",
"CommandName2",
"CommandName3",
]);
assert.equal( assert.equal(
aiPersona.getCommandOption("CommandName", "option1").value, aiPersona.getToolOption("ToolName", "option1").value,
"value1" "value1"
); );
assert.equal( assert.equal(
aiPersona.getCommandOption("CommandName", "option2").value, aiPersona.getToolOption("ToolName", "option2").value,
"value2" "value2"
); );
}); });
@ -32,7 +28,7 @@ module("Discourse AI | Unit | Model | ai-persona", function () {
const properties = { const properties = {
id: 1, id: 1,
name: "Test", name: "Test",
commands: ["CommandName"], tools: ["ToolName"],
allowed_group_ids: [12], allowed_group_ids: [12],
system: false, system: false,
enabled: true, enabled: true,
@ -54,16 +50,17 @@ module("Discourse AI | Unit | Model | ai-persona", function () {
rag_conversation_chunks: 10, rag_conversation_chunks: 10,
question_consolidator_llm: "Question Consolidator LLM", question_consolidator_llm: "Question Consolidator LLM",
allow_chat: false, allow_chat: false,
tool_details: true,
}; };
const aiPersona = AiPersona.create({ ...properties }); const aiPersona = AiPersona.create({ ...properties });
aiPersona.getCommandOption("CommandName", "option1").value = "value1"; aiPersona.getToolOption("ToolName", "option1").value = "value1";
const updatedProperties = aiPersona.updateProperties(); const updatedProperties = aiPersona.updateProperties();
// perform remapping for save // perform remapping for save
properties.commands = [["CommandName", { option1: "value1" }]]; properties.tools = [["ToolName", { option1: "value1" }]];
assert.deepEqual(updatedProperties, properties); assert.deepEqual(updatedProperties, properties);
}); });
@ -72,7 +69,7 @@ module("Discourse AI | Unit | Model | ai-persona", function () {
const properties = { const properties = {
id: 1, id: 1,
name: "Test", name: "Test",
commands: ["CommandName"], tools: ["ToolName"],
allowed_group_ids: [12], allowed_group_ids: [12],
system: false, system: false,
enabled: true, enabled: true,
@ -94,15 +91,16 @@ module("Discourse AI | Unit | Model | ai-persona", function () {
rag_conversation_chunks: 10, rag_conversation_chunks: 10,
question_consolidator_llm: "Question Consolidator LLM", question_consolidator_llm: "Question Consolidator LLM",
allow_chat: false, allow_chat: false,
tool_details: true,
}; };
const aiPersona = AiPersona.create({ ...properties }); const aiPersona = AiPersona.create({ ...properties });
aiPersona.getCommandOption("CommandName", "option1").value = "value1"; aiPersona.getToolOption("ToolName", "option1").value = "value1";
const createdProperties = aiPersona.createProperties(); const createdProperties = aiPersona.createProperties();
properties.commands = [["CommandName", { option1: "value1" }]]; properties.tools = [["ToolName", { option1: "value1" }]];
assert.deepEqual(createdProperties, properties); assert.deepEqual(createdProperties, properties);
}); });
@ -110,18 +108,18 @@ module("Discourse AI | Unit | Model | ai-persona", function () {
test("working copy", function (assert) { test("working copy", function (assert) {
const aiPersona = AiPersona.create({ const aiPersona = AiPersona.create({
name: "Test", name: "Test",
commands: ["CommandName"], tools: ["ToolName"],
}); });
aiPersona.getCommandOption("CommandName", "option1").value = "value1"; aiPersona.getToolOption("ToolName", "option1").value = "value1";
const workingCopy = aiPersona.workingCopy(); const workingCopy = aiPersona.workingCopy();
assert.equal(workingCopy.name, "Test"); assert.equal(workingCopy.name, "Test");
assert.equal( assert.equal(
workingCopy.getCommandOption("CommandName", "option1").value, workingCopy.getToolOption("ToolName", "option1").value,
"value1" "value1"
); );
assert.deepEqual(workingCopy.commands, ["CommandName"]); assert.deepEqual(workingCopy.tools, ["ToolName"]);
}); });
}); });