FEATURE: Add ability to disable search discoveries (#1177)
This update adds the ability to disable search discoveries. This can be done through a tooltip when search discoveries are shown. It can also be done in the AI user preferences, which has also been updated to accommodate more than just the one image caption setting.
This commit is contained in:
parent
339251a371
commit
bb32d0d737
|
@ -0,0 +1,49 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import DTooltip from "float-kit/components/d-tooltip";
|
||||
|
||||
export default class AiSearchDiscoveriesTooltip extends Component {
|
||||
@service discobotDiscoveries;
|
||||
|
||||
<template>
|
||||
<span class="ai-search-discoveries-tooltip">
|
||||
<DTooltip @placement="top-end" @interactive={{true}}>
|
||||
<:trigger>
|
||||
{{icon "circle-info"}}
|
||||
</:trigger>
|
||||
<:content>
|
||||
<div class="ai-search-discoveries-tooltip__content">
|
||||
<div class="ai-search-discoveries-tooltip__header">
|
||||
{{i18n "discourse_ai.discobot_discoveries.tooltip.header"}}
|
||||
</div>
|
||||
|
||||
<div class="ai-search-discoveries-tooltip__description">
|
||||
{{#if this.discobotDiscoveries.modelUsed}}
|
||||
{{i18n
|
||||
"discourse_ai.discobot_discoveries.tooltip.content"
|
||||
model=this.discobotDiscoveries.modelUsed
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="ai-search-discoveries-tooltip__actions">
|
||||
<DButton
|
||||
class="btn-transparent btn-primary"
|
||||
@label="discourse_ai.discobot_discoveries.tooltip.actions.info"
|
||||
@href="https://meta.discourse.org/t/conversational-ai-search-coming-to-discourse-ai/355939"
|
||||
/>
|
||||
<DButton
|
||||
class="btn-transparent btn-danger"
|
||||
@label="discourse_ai.discobot_discoveries.tooltip.actions.disable"
|
||||
@action={{this.discobotDiscoveries.disableDiscoveries}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</:content>
|
||||
</DTooltip>
|
||||
</span>
|
||||
</template>
|
||||
}
|
|
@ -2,14 +2,15 @@ import Component from "@glimmer/component";
|
|||
import { service } from "@ember/service";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import DTooltip from "float-kit/components/d-tooltip";
|
||||
import AiSearchDiscoveries from "../../components/ai-search-discoveries";
|
||||
import AiSearchDiscoveriesTooltip from "../../components/ai-search-discoveries-tooltip";
|
||||
|
||||
export default class AiFullPageDiscobotDiscoveries extends Component {
|
||||
static shouldRender(_args, { siteSettings, currentUser }) {
|
||||
return (
|
||||
siteSettings.ai_bot_discover_persona &&
|
||||
currentUser?.can_use_ai_bot_discover_persona
|
||||
currentUser?.can_use_ai_bot_discover_persona &&
|
||||
currentUser?.user_option?.ai_search_discoveries
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -29,29 +30,7 @@ export default class AiFullPageDiscobotDiscoveries extends Component {
|
|||
{{i18n "discourse_ai.discobot_discoveries.main_title"}}
|
||||
</span>
|
||||
|
||||
<span class="ai-search-discoveries-tooltip">
|
||||
<DTooltip @placement="top-end">
|
||||
<:trigger>
|
||||
{{icon "circle-info"}}
|
||||
</:trigger>
|
||||
<:content>
|
||||
<div class="ai-search-discoveries-tooltip__content">
|
||||
<div class="ai-search-discoveries-tooltip__header">
|
||||
{{i18n "discourse_ai.discobot_discoveries.tooltip.header"}}
|
||||
</div>
|
||||
|
||||
<div class="ai-search-discoveries-tooltip__content">
|
||||
{{#if this.discobotDiscoveries.modelUsed}}
|
||||
{{i18n
|
||||
"discourse_ai.discobot_discoveries.tooltip.content"
|
||||
model=this.discobotDiscoveries.modelUsed
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</:content>
|
||||
</DTooltip>
|
||||
</span>
|
||||
<AiSearchDiscoveriesTooltip />
|
||||
</h3>
|
||||
<div class="full-page-discoveries">
|
||||
<AiSearchDiscoveries @searchTerm={{@outletArgs.search}} />
|
||||
|
|
|
@ -2,15 +2,16 @@ import Component from "@glimmer/component";
|
|||
import { service } from "@ember/service";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import DTooltip from "float-kit/components/d-tooltip";
|
||||
import AiSearchDiscoveries from "../../components/ai-search-discoveries";
|
||||
import AiSearchDiscoveriesTooltip from "../../components/ai-search-discoveries-tooltip";
|
||||
|
||||
export default class AiDiscobotDiscoveries extends Component {
|
||||
static shouldRender(args, { siteSettings, currentUser }) {
|
||||
return (
|
||||
args.resultType.type === "topic" &&
|
||||
siteSettings.ai_bot_discover_persona &&
|
||||
currentUser?.can_use_ai_bot_discover_persona
|
||||
currentUser?.can_use_ai_bot_discover_persona &&
|
||||
currentUser?.user_option?.ai_search_discoveries
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -24,29 +25,7 @@ export default class AiDiscobotDiscoveries extends Component {
|
|||
{{i18n "discourse_ai.discobot_discoveries.main_title"}}
|
||||
</span>
|
||||
|
||||
<span class="ai-search-discoveries-tooltip">
|
||||
<DTooltip @placement="top-end">
|
||||
<:trigger>
|
||||
{{icon "circle-info"}}
|
||||
</:trigger>
|
||||
<:content>
|
||||
<div class="ai-search-discoveries-tooltip__content">
|
||||
<div class="ai-search-discoveries-tooltip__header">
|
||||
{{i18n "discourse_ai.discobot_discoveries.tooltip.header"}}
|
||||
</div>
|
||||
|
||||
<div class="ai-search-discoveries-tooltip__content">
|
||||
{{#if this.discobotDiscoveries.modelUsed}}
|
||||
{{i18n
|
||||
"discourse_ai.discobot_discoveries.tooltip.content"
|
||||
model=this.discobotDiscoveries.modelUsed
|
||||
}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</:content>
|
||||
</DTooltip>
|
||||
</span>
|
||||
<AiSearchDiscoveriesTooltip />
|
||||
</h3>
|
||||
|
||||
<AiSearchDiscoveries @discoveryPreviewLength={{50}} />
|
||||
|
|
|
@ -5,21 +5,54 @@ import { service } from "@ember/service";
|
|||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { isTesting } from "discourse/lib/environment";
|
||||
|
||||
const AI_ATTRS = ["auto_image_caption"];
|
||||
|
||||
export default class PreferencesAiController extends Controller {
|
||||
@service siteSettings;
|
||||
@tracked saved = false;
|
||||
|
||||
get showAutoImageCaptionSetting() {
|
||||
const aiHelperEnabledFeatures =
|
||||
this.siteSettings.ai_helper_enabled_features.split("|");
|
||||
get booleanSettings() {
|
||||
return [
|
||||
{
|
||||
key: "auto_image_caption",
|
||||
label: "discourse_ai.ai_helper.image_caption.automatic_caption_setting",
|
||||
settingName: "auto-image-caption",
|
||||
checked: this.model.user_option.auto_image_caption,
|
||||
isIncluded: (() => {
|
||||
const aiHelperEnabledFeatures =
|
||||
this.siteSettings.ai_helper_enabled_features.split("|");
|
||||
|
||||
return (
|
||||
this.model?.user_allowed_ai_auto_image_captions &&
|
||||
aiHelperEnabledFeatures.includes("image_caption") &&
|
||||
this.siteSettings.ai_helper_enabled
|
||||
);
|
||||
return (
|
||||
this.model?.user_allowed_ai_auto_image_captions &&
|
||||
aiHelperEnabledFeatures.includes("image_caption") &&
|
||||
this.siteSettings.ai_helper_enabled
|
||||
);
|
||||
})(),
|
||||
},
|
||||
{
|
||||
key: "ai_search_discoveries",
|
||||
label: "discourse_ai.discobot_discoveries.user_setting",
|
||||
settingName: "ai-search-discoveries",
|
||||
checked: this.model.user_option.ai_search_discoveries,
|
||||
isIncluded: (() => {
|
||||
return (
|
||||
this.siteSettings.ai_bot_discover_persona &&
|
||||
this.model?.can_use_ai_bot_discover_persona &&
|
||||
this.siteSettings.ai_bot_enabled
|
||||
);
|
||||
})(),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
get userSettingAttributes() {
|
||||
const attrs = [];
|
||||
|
||||
this.booleanSettings.forEach((setting) => {
|
||||
if (setting.isIncluded) {
|
||||
attrs.push(setting.key);
|
||||
}
|
||||
});
|
||||
|
||||
return attrs;
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -27,7 +60,7 @@ export default class PreferencesAiController extends Controller {
|
|||
this.saved = false;
|
||||
|
||||
return this.model
|
||||
.save(AI_ATTRS)
|
||||
.save(this.userSettingAttributes)
|
||||
.then(() => {
|
||||
this.saved = true;
|
||||
if (!isTesting()) {
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import Service from "@ember/service";
|
||||
import { action } from "@ember/object";
|
||||
import Service, { service } from "@ember/service";
|
||||
|
||||
export default class DiscobotDiscoveries extends Service {
|
||||
// We use this to retain state after search menu gets closed.
|
||||
// Similar to discourse/discourse#25504
|
||||
@service currentUser;
|
||||
|
||||
@tracked discovery = "";
|
||||
@tracked lastQuery = "";
|
||||
@tracked discoveryTimedOut = false;
|
||||
|
@ -14,4 +17,11 @@ export default class DiscobotDiscoveries extends Service {
|
|||
this.discoveryTimedOut = false;
|
||||
this.modelUsed = "";
|
||||
}
|
||||
|
||||
@action
|
||||
async disableDiscoveries() {
|
||||
this.currentUser.user_option.ai_search_discoveries = false;
|
||||
await this.currentUser.save(["ai_search_discoveries"]);
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,24 +1,29 @@
|
|||
{{!
|
||||
Later when we have more preferences,
|
||||
move the conditional (showAutoImageCaptionSetting)
|
||||
to be only around the auto-image-caption preference.
|
||||
}}
|
||||
{{#if this.showAutoImageCaptionSetting}}
|
||||
<label class="control-label">{{i18n "discourse_ai.title"}}</label>
|
||||
<div class="ai-user-preferences">
|
||||
<legend class="control-label">{{i18n "discourse_ai.title"}}</legend>
|
||||
|
||||
<div class="control-group ai-setting">
|
||||
<PreferenceCheckbox
|
||||
@labelKey="discourse_ai.ai_helper.image_caption.automatic_caption_setting"
|
||||
@checked={{this.model.user_option.auto_image_caption}}
|
||||
data-setting-name="auto-image-caption"
|
||||
class="pref-auto-image-caption"
|
||||
{{#each this.booleanSettings as |setting|}}
|
||||
{{#if setting.isIncluded}}
|
||||
<div class="control-group ai-setting">
|
||||
<PreferenceCheckbox
|
||||
@labelKey={{setting.label}}
|
||||
@checked={{get this.model.user_option setting.key}}
|
||||
data-setting-name={{setting.settingName}}
|
||||
class="pref-{{setting.settingName}}"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
||||
{{#if (eq this.userSettingAttributes.length 0)}}
|
||||
{{i18n "discourse_ai.user_preferences.empty"}}
|
||||
{{/if}}
|
||||
|
||||
{{#unless (eq this.userSettingAttributes.length 0)}}
|
||||
<SaveControls
|
||||
@id="user_ai_preference_save"
|
||||
@model={{this.model}}
|
||||
@action={{this.save}}
|
||||
@saved={{this.saved}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<SaveControls
|
||||
@id="user_ai_preference_save"
|
||||
@model={{this.model}}
|
||||
@action={{this.save}}
|
||||
@saved={{this.saved}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</div>
|
|
@ -0,0 +1,15 @@
|
|||
import { apiInitializer } from "discourse/lib/api";
|
||||
|
||||
export default apiInitializer((api) => {
|
||||
const currentUser = api.getCurrentUser();
|
||||
const settings = api.container.lookup("service:site-settings");
|
||||
|
||||
if (
|
||||
!settings.ai_bot_enabled ||
|
||||
!currentUser?.can_use_ai_bot_discover_persona
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
api.addSaveableUserOptionField("ai_search_discoveries");
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
.user-preferences .ai-user-preferences {
|
||||
legend {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.control-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
|
@ -63,11 +63,26 @@
|
|||
}
|
||||
|
||||
.ai-search-discoveries-tooltip {
|
||||
&__content {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
&__header {
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
|
||||
.btn {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fk-d-tooltip__trigger {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,6 @@ en:
|
|||
label: "Tool"
|
||||
description: "Tool to use for triage (tool must have no parameters defined)"
|
||||
|
||||
|
||||
llm_persona_triage:
|
||||
fields:
|
||||
persona:
|
||||
|
@ -714,9 +713,16 @@ en:
|
|||
tell_me_more: "Tell me more..."
|
||||
collapse: "Collapse"
|
||||
timed_out: "Discobot couldn't find any discoveries. Something went wrong."
|
||||
user_setting: "Enable search discoveries"
|
||||
tooltip:
|
||||
header: "AI powered search"
|
||||
content: "Natural language search powered by %{model}"
|
||||
actions:
|
||||
info: "How does it work?"
|
||||
disable: "Disable"
|
||||
|
||||
user_preferences:
|
||||
empty: "There are no relevant settings available at this time"
|
||||
review:
|
||||
types:
|
||||
reviewable_ai_post:
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddAiSearchDiscoveriesToUserOptions < ActiveRecord::Migration[7.2]
|
||||
def change
|
||||
add_column :user_options, :ai_search_discoveries, :boolean, default: true, null: false
|
||||
end
|
||||
end
|
|
@ -180,6 +180,25 @@ module DiscourseAi
|
|||
scope.user.in_any_groups?(persona_allowed_groups)
|
||||
end
|
||||
|
||||
UserUpdater::OPTION_ATTR.push(:ai_search_discoveries)
|
||||
plugin.add_to_serializer(
|
||||
:user_option,
|
||||
:ai_search_discoveries,
|
||||
include_condition: -> do
|
||||
SiteSetting.ai_bot_enabled && SiteSetting.ai_bot_discover_persona.present? &&
|
||||
scope.authenticated?
|
||||
end,
|
||||
) { object.ai_search_discoveries }
|
||||
|
||||
plugin.add_to_serializer(
|
||||
:current_user_option,
|
||||
:ai_search_discoveries,
|
||||
include_condition: -> do
|
||||
SiteSetting.ai_bot_enabled && SiteSetting.ai_bot_discover_persona.present? &&
|
||||
scope.authenticated?
|
||||
end,
|
||||
) { object.ai_search_discoveries }
|
||||
|
||||
plugin.add_to_serializer(
|
||||
:topic_view,
|
||||
:ai_persona_name,
|
||||
|
|
|
@ -26,6 +26,7 @@ enabled_site_setting :discourse_ai_enabled
|
|||
|
||||
register_asset "stylesheets/common/streaming.scss"
|
||||
register_asset "stylesheets/common/ai-blinking-animation.scss"
|
||||
register_asset "stylesheets/common/ai-user-settings.scss"
|
||||
|
||||
register_asset "stylesheets/modules/ai-helper/common/ai-helper.scss"
|
||||
register_asset "stylesheets/modules/ai-helper/desktop/ai-helper-fk-modals.scss", :desktop
|
||||
|
|
|
@ -161,5 +161,14 @@ RSpec.describe DiscourseAi::AiBot::EntryPoint do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "will include ai_search_discoveries field in the user_option if discover persona is enabled" do
|
||||
SiteSetting.ai_bot_enabled = true
|
||||
SiteSetting.ai_bot_discover_persona = Fabricate(:ai_persona).id
|
||||
|
||||
serializer =
|
||||
CurrentUserSerializer.new(Fabricate(:user), scope: Guardian.new(Fabricate(:user)))
|
||||
expect(serializer.user_option.ai_search_discoveries).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe UserOption do
|
||||
fab!(:user)
|
||||
fab!(:llm_model)
|
||||
fab!(:group)
|
||||
fab!(:ai_persona) do
|
||||
Fabricate(:ai_persona, allowed_group_ids: [group.id], default_llm_id: llm_model.id)
|
||||
end
|
||||
|
||||
before do
|
||||
assign_fake_provider_to(:ai_helper_model)
|
||||
assign_fake_provider_to(:ai_helper_image_caption_model)
|
||||
SiteSetting.ai_helper_enabled = true
|
||||
SiteSetting.ai_helper_enabled_features = "image_caption"
|
||||
SiteSetting.ai_auto_image_caption_allowed_groups = "10" # tl0
|
||||
|
||||
SiteSetting.ai_bot_enabled = true
|
||||
end
|
||||
|
||||
describe "#auto_image_caption" do
|
||||
|
@ -14,4 +23,15 @@ RSpec.describe UserOption do
|
|||
expect(described_class.new.auto_image_caption).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#ai_search_discoveries" do
|
||||
before do
|
||||
SiteSetting.ai_bot_discover_persona = ai_persona.id
|
||||
group.add(user)
|
||||
end
|
||||
|
||||
it "is present" do
|
||||
expect(described_class.new.ai_search_discoveries).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue