Skip to content

Commit dd257cf

Browse files
committed
FEATURE: allow suppression of tool details
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.
1 parent 9860bcd commit dd257cf

File tree

84 files changed

+413
-396
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+413
-396
lines changed

app/controllers/discourse_ai/admin/ai_personas_controller.rb

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def index
2323
DiscourseAi::Configuration::LlmEnumerator.values.map do |hash|
2424
{ id: hash[:value], name: hash[:name] }
2525
end
26-
render json: { ai_personas: ai_personas, meta: { commands: tools, llms: llms } }
26+
render json: { ai_personas: ai_personas, meta: { tools: tools, llms: llms } }
2727
end
2828

2929
def show
@@ -126,28 +126,29 @@ def ai_persona_params
126126
:rag_conversation_chunks,
127127
:question_consolidator_llm,
128128
:allow_chat,
129+
:tool_details,
129130
allowed_group_ids: [],
130131
rag_uploads: [:id],
131132
)
132133

133-
if commands = params.dig(:ai_persona, :commands)
134-
permitted[:commands] = permit_commands(commands)
134+
if tools = params.dig(:ai_persona, :tools)
135+
permitted[:tools] = permit_tools(tools)
135136
end
136137

137138
permitted
138139
end
139140

140-
def permit_commands(commands)
141-
return [] if !commands.is_a?(Array)
141+
def permit_tools(tools)
142+
return [] if !tools.is_a?(Array)
142143

143-
commands.filter_map do |command, options|
144-
break nil if !command.is_a?(String)
144+
tools.filter_map do |tool, options|
145+
break nil if !tool.is_a?(String)
145146
options&.permit! if options && options.is_a?(ActionController::Parameters)
146147

147148
if options
148-
[command, options]
149+
[tool, options]
149150
else
150-
command
151+
tool
151152
end
152153
end
153154
end

app/models/ai_persona.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# frozen_string_literal: true
22

33
class AiPersona < ActiveRecord::Base
4+
# TODO remove this line 01-11-2024
5+
self.ignored_columns = [:commands]
6+
47
# places a hard limit, so per site we cache a maximum of 500 classes
58
MAX_PERSONAS_PER_SITE = 500
69

@@ -117,6 +120,7 @@ def class_instance
117120
name
118121
description
119122
allowed_group_ids
123+
tool_details
120124
]
121125

122126
persona_class = DiscourseAi::AiBot::Personas::Persona.system_personas_by_id[self.id]
@@ -136,11 +140,10 @@ def class_instance
136140
end
137141

138142
options = {}
139-
tools = self.respond_to?(:commands) ? self.commands : self.tools
140143
tools =
141-
tools.filter_map do |element|
144+
self.tools.filter_map do |element|
142145
inner_name, current_options = element.is_a?(Array) ? element : [element, nil]
143-
inner_name = inner_name.gsub("Command", "")
146+
inner_name = inner_name.gsub("Tool", "")
144147
inner_name = "List#{inner_name}" if %w[Categories Tags].include?(inner_name)
145148

146149
begin
@@ -231,7 +234,7 @@ def chat_preconditions
231234
end
232235

233236
def system_persona_unchangeable
234-
if top_p_changed? || temperature_changed? || system_prompt_changed? || commands_changed? ||
237+
if top_p_changed? || temperature_changed? || system_prompt_changed? || tools_changed? ||
235238
name_changed? || description_changed?
236239
errors.add(:base, I18n.t("discourse_ai.ai_bot.personas.cannot_edit_system_persona"))
237240
end
@@ -252,7 +255,7 @@ def ensure_not_system
252255
# id :bigint not null, primary key
253256
# name :string(100) not null
254257
# description :string(2000) not null
255-
# commands :json not null
258+
# tools :json not null
256259
# system_prompt :string(10000000) not null
257260
# allowed_group_ids :integer default([]), not null, is an Array
258261
# created_by_id :integer
@@ -282,6 +285,7 @@ def ensure_not_system
282285
# role_max_responses_per_hour :integer default(50), not null
283286
# question_consolidator_llm :text
284287
# allow_chat :boolean default(FALSE), not null
288+
# tool_details :boolean default(TRUE), not null
285289
#
286290
# Indexes
287291
#

app/serializers/localized_ai_persona_serializer.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class LocalizedAiPersonaSerializer < ApplicationSerializer
99
:enabled,
1010
:system,
1111
:priority,
12-
:commands,
12+
:tools,
1313
:system_prompt,
1414
:allowed_group_ids,
1515
:temperature,
@@ -24,7 +24,8 @@ class LocalizedAiPersonaSerializer < ApplicationSerializer
2424
:rag_chunk_overlap_tokens,
2525
:rag_conversation_chunks,
2626
:question_consolidator_llm,
27-
:allow_chat
27+
:allow_chat,
28+
:tool_details
2829

2930
has_one :user, serializer: BasicUserSerializer, embed: :object
3031
has_many :rag_uploads, serializer: UploadSerializer, embed: :object

assets/javascripts/discourse/admin/models/ai-persona.js

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const CREATE_ATTRIBUTES = [
66
"id",
77
"name",
88
"description",
9-
"commands",
9+
"tools",
1010
"system_prompt",
1111
"allowed_group_ids",
1212
"enabled",
@@ -27,6 +27,7 @@ const CREATE_ATTRIBUTES = [
2727
"rag_conversation_chunks",
2828
"question_consolidator_llm",
2929
"allow_chat",
30+
"tool_details",
3031
];
3132

3233
const SYSTEM_ATTRIBUTES = [
@@ -48,37 +49,37 @@ const SYSTEM_ATTRIBUTES = [
4849
"rag_conversation_chunks",
4950
"question_consolidator_llm",
5051
"allow_chat",
52+
"tool_details",
5153
];
5254

53-
class CommandOption {
55+
class ToolOption {
5456
@tracked value = null;
5557
}
5658

5759
export default class AiPersona extends RestModel {
5860
// this code is here to convert the wire schema to easier to work with object
59-
// on the wire we pass in/out commands as an Array.
60-
// [[CommandName, {option1: value, option2: value}], CommandName2, CommandName3]
61-
// So we rework this into a "commands" property and nested commandOptions
61+
// on the wire we pass in/out tools as an Array.
62+
// [[ToolName, {option1: value, option2: value}], ToolName2, ToolName3]
63+
// So we rework this into a "tools" property and nested toolOptions
6264
init(properties) {
63-
if (properties.commands) {
64-
properties.commands = properties.commands.map((command) => {
65-
if (typeof command === "string") {
66-
return command;
65+
if (properties.tools) {
66+
properties.tools = properties.tools.map((tool) => {
67+
if (typeof tool === "string") {
68+
return tool;
6769
} else {
68-
let [commandId, options] = command;
70+
let [toolId, options] = tool;
6971
for (let optionId in options) {
7072
if (!options.hasOwnProperty(optionId)) {
7173
continue;
7274
}
73-
this.getCommandOption(commandId, optionId).value =
74-
options[optionId];
75+
this.getToolOption(toolId, optionId).value = options[optionId];
7576
}
76-
return commandId;
77+
return toolId;
7778
}
7879
});
7980
}
8081
super.init(properties);
81-
this.commands = properties.commands;
82+
this.tools = properties.tools;
8283
}
8384

8485
async createUser() {
@@ -93,23 +94,23 @@ export default class AiPersona extends RestModel {
9394
return this.user;
9495
}
9596

96-
getCommandOption(commandId, optionId) {
97-
this.commandOptions ||= {};
98-
this.commandOptions[commandId] ||= {};
99-
return (this.commandOptions[commandId][optionId] ||= new CommandOption());
97+
getToolOption(toolId, optionId) {
98+
this.toolOptions ||= {};
99+
this.toolOptions[toolId] ||= {};
100+
return (this.toolOptions[toolId][optionId] ||= new ToolOption());
100101
}
101102

102-
populateCommandOptions(attrs) {
103-
if (!attrs.commands) {
103+
populateToolOptions(attrs) {
104+
if (!attrs.tools) {
104105
return;
105106
}
106-
let commandsWithOptions = [];
107-
attrs.commands.forEach((commandId) => {
108-
if (typeof commandId !== "string") {
109-
commandId = commandId[0];
107+
let toolsWithOptions = [];
108+
attrs.tools.forEach((toolId) => {
109+
if (typeof toolId !== "string") {
110+
toolId = toolId[0];
110111
}
111-
if (this.commandOptions && this.commandOptions[commandId]) {
112-
let options = this.commandOptions[commandId];
112+
if (this.toolOptions && this.toolOptions[toolId]) {
113+
let options = this.toolOptions[toolId];
113114
let optionsWithValues = {};
114115
for (let optionId in options) {
115116
if (!options.hasOwnProperty(optionId)) {
@@ -118,33 +119,33 @@ export default class AiPersona extends RestModel {
118119
let option = options[optionId];
119120
optionsWithValues[optionId] = option.value;
120121
}
121-
commandsWithOptions.push([commandId, optionsWithValues]);
122+
toolsWithOptions.push([toolId, optionsWithValues]);
122123
} else {
123-
commandsWithOptions.push(commandId);
124+
toolsWithOptions.push(toolId);
124125
}
125126
});
126-
attrs.commands = commandsWithOptions;
127+
attrs.tools = toolsWithOptions;
127128
}
128129

129130
updateProperties() {
130131
let attrs = this.system
131132
? this.getProperties(SYSTEM_ATTRIBUTES)
132133
: this.getProperties(CREATE_ATTRIBUTES);
133134
attrs.id = this.id;
134-
this.populateCommandOptions(attrs);
135+
this.populateToolOptions(attrs);
135136

136137
return attrs;
137138
}
138139

139140
createProperties() {
140141
let attrs = this.getProperties(CREATE_ATTRIBUTES);
141-
this.populateCommandOptions(attrs);
142+
this.populateToolOptions(attrs);
142143
return attrs;
143144
}
144145

145146
workingCopy() {
146147
let attrs = this.getProperties(CREATE_ATTRIBUTES);
147-
this.populateCommandOptions(attrs);
148+
this.populateToolOptions(attrs);
148149
return AiPersona.create(attrs);
149150
}
150151
}

assets/javascripts/discourse/components/ai-persona-command-options.gjs

Lines changed: 0 additions & 81 deletions
This file was deleted.

0 commit comments

Comments
 (0)