import Component from "@glimmer/component"; import { tracked } from "@glimmer/tracking"; import { on } from "@ember/modifier"; import { action } from "@ember/object"; import didInsert from "@ember/render-modifiers/modifiers/did-insert"; import { inject as service } from "@ember/service"; import DToggleSwitch from "discourse/components/d-toggle-switch"; import { SEARCH_TYPE_DEFAULT } from "discourse/controllers/full-page-search"; import { ajax } from "discourse/lib/ajax"; import { withPluginApi } from "discourse/lib/plugin-api"; import { isValidSearchTerm, translateResults } from "discourse/lib/search"; import icon from "discourse-common/helpers/d-icon"; import I18n from "I18n"; export default class SemanticSearch extends Component { static shouldRender(_args, { siteSettings }) { return siteSettings.ai_embeddings_semantic_search_enabled; } @service appEvents; @service siteSettings; @service searchPreferencesManager; @tracked searching = false; @tracked AIResults = []; @tracked showingAIResults = false; @tracked preventAISearch = false; initialSearchTerm = this.args.outletArgs.search; get disableToggleSwitch() { if (this.searching || this.AIResults.length === 0 || this.preventAISearch) { return true; } } get searchStateText() { // Search results: if (this.AIResults.length > 0) { if (this.showingAIResults) { return I18n.t( "discourse_ai.embeddings.semantic_search_results.toggle", { count: this.AIResults.length, } ); } else { return I18n.t( "discourse_ai.embeddings.semantic_search_results.toggle_hidden", { count: this.AIResults.length, } ); } } // Search disabled for sort order: if (this.preventAISearch) { return I18n.t("discourse_ai.embeddings.semantic_search_disabled_sort"); } // Search loading: if (this.searching) { return I18n.t("discourse_ai.embeddings.semantic_search_loading"); } // Typing to search: if ( this.AIResults.length === 0 && this.searchTerm !== this.initialSearchTerm ) { return I18n.t("discourse_ai.embeddings.semantic_search_results.new"); } // No results: if (this.AIResults.length === 0) { return I18n.t("discourse_ai.embeddings.semantic_search_results.none"); } } get searchTerm() { if (this.initialSearchTerm !== this.args.outletArgs.search) { this.initialSearchTerm = undefined; } return this.args.outletArgs.search; } get searchEnabled() { return ( this.args.outletArgs.type === SEARCH_TYPE_DEFAULT && isValidSearchTerm(this.searchTerm, this.siteSettings) ); } @action toggleAIResults() { if (this.showingAIResults) { this.args.outletArgs.addSearchResults([], "topic_id"); } else { this.args.outletArgs.addSearchResults(this.AIResults, "topic_id"); } this.showingAIResults = !this.showingAIResults; } @action resetAIResults() { this.AIResults = []; this.showingAIResults = false; this.args.outletArgs.addSearchResults([], "topic_id"); } @action handleSearch() { if (!this.searchEnabled) { return; } if ( this.searchPreferencesManager?.sortOrder !== undefined && this.searchPreferencesManager?.sortOrder !== 0 ) { this.preventAISearch = true; return; } else { this.preventAISearch = false; } if (this.initialSearchTerm && !this.searching) { return this.performHyDESearch(); } withPluginApi("1.15.0", (api) => { api.onAppEvent("full-page-search:trigger-search", () => { if (!this.searching) { this.resetAIResults(); return this.performHyDESearch(); } }); }); } performHyDESearch() { this.searching = true; this.resetAIResults(); ajax("/discourse-ai/embeddings/semantic-search", { data: { q: this.searchTerm }, }) .then(async (results) => { const model = (await translateResults(results)) || {}; if (model.posts?.length === 0) { this.searching = false; return; } model.posts.forEach((post) => { post.generatedByAI = true; }); this.AIResults = model.posts; }) .finally(() => (this.searching = false)); } }