tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 16afaac73ca2b78f4ef6a0cd73fa46c9efce6ee3
parent d3734204ad485cf7e8f33843c6787038b8e768de
Author: Nick Grato <ngrato@gmail.com>
Date:   Tue, 14 Oct 2025 22:22:20 +0000

Bug 1993365 -  hook up search intent model r=Mardak,firefox-ai-ml-reviewers

Add the intent model to the SmartAssistEngine and have it live update the UI as the user types their prompt in.

Differential Revision: https://phabricator.services.mozilla.com/D268039

Diffstat:
Mbrowser/components/genai/SmartAssistEngine.sys.mjs | 36++++++++++++++++--------------------
Mbrowser/components/genai/content/smart-assist.mjs | 27++++++++++++++++++---------
Mbrowser/components/genai/tests/xpcshell/test_smart_assist_engine.js | 25+++++++++++++++++++++++++
Mtoolkit/components/ml/content/EngineProcess.sys.mjs | 4++++
Mtoolkit/components/ml/metrics.yaml | 2++
5 files changed, 65 insertions(+), 29 deletions(-)

diff --git a/browser/components/genai/SmartAssistEngine.sys.mjs b/browser/components/genai/SmartAssistEngine.sys.mjs @@ -212,29 +212,25 @@ export const SmartAssistEngine = { }, /** + * Gets the intent of the prompt using a text classification model. * * @param {string} prompt * @returns {string} "search" | "chat" */ - async getPromptIntent(prompt) { - // TODO : this is mock logic to determine if the user wants to search or chat - // We will eventually want to use a model to determine this intent - const searchKeywords = [ - "search", - "find", - "look", - "query", - "locate", - "explore", - ]; - const formattedPrompt = prompt.toLowerCase(); - - const intent = searchKeywords.some(keyword => - formattedPrompt.includes(keyword) - ) - ? "search" - : "chat"; - - return intent; + + async getPromptIntent(query) { + try { + const engine = await this._createEngine({ + featureId: "smart-intent", + modelId: "mozilla/query-intent-detection", + modelRevision: "v0.1.0", + taskName: "text-classification", + }); + const resp = await engine.run({ args: [[query]] }); + return resp[0].label.toLowerCase(); + } catch (error) { + console.error("Error using intent detection model:", error); + throw error; + } }, }; diff --git a/browser/components/genai/content/smart-assist.mjs b/browser/components/genai/content/smart-assist.mjs @@ -17,6 +17,8 @@ ChromeUtils.defineESModuleGetters(lazy, { }); const FULL_PAGE_URL = "chrome://browser/content/genai/smartAssistPage.html"; +const ACTION_CHAT = "chat"; +const ACTION_SEARCH = "search"; /** * A custom element for managing the smart assistant sidebar. @@ -47,15 +49,14 @@ export class SmartAssist extends MozLitElement { this.overrideNewTab = Services.prefs.getBoolPref( "browser.ml.smartAssist.overrideNewTab" ); - this.actionKey = "chat"; - + this.actionKey = ACTION_CHAT; this._actions = { - chat: { + [ACTION_CHAT]: { label: "Submit", icon: "chrome://global/skin/icons/arrow-right.svg", run: this._actionChat, }, - search: { + [ACTION_SEARCH]: { label: "Search", icon: "chrome://global/skin/icons/search-glass.svg", run: this._actionSearch, @@ -87,11 +88,19 @@ export class SmartAssist extends MozLitElement { }; _handlePromptInput = async e => { - const value = e.target.value; - this.userPrompt = value; - - // Determine intent based on keywords in the prompt - this.actionKey = await lazy.SmartAssistEngine.getPromptIntent(value); + try { + const value = e.target.value; + this.userPrompt = value; + + const intent = await lazy.SmartAssistEngine.getPromptIntent(value); + this.actionKey = [ACTION_CHAT, ACTION_SEARCH].includes(intent) + ? intent + : ACTION_CHAT; + } catch (error) { + // Default to chat on error + this.actionKey = ACTION_CHAT; + console.error("Error determining prompt intent:", error); + } }; /** diff --git a/browser/components/genai/tests/xpcshell/test_smart_assist_engine.js b/browser/components/genai/tests/xpcshell/test_smart_assist_engine.js @@ -179,6 +179,7 @@ add_task(async function test_fetchWithHistory_propagates_stream_error() { }); add_task(async function test_getPromptIntent_basic() { + const sb = sinon.createSandbox(); const cases = [ { prompt: "please search for news on firefox", expected: "search" }, { prompt: "Can you FIND me the docs for PageAssist?", expected: "search" }, // case-insensitive @@ -187,6 +188,30 @@ add_task(async function test_getPromptIntent_basic() { { prompt: "tell me a joke", expected: "chat" }, ]; + const fakeEngine = { + run({ args: [[query]] }) { + const searchKeywords = [ + "search", + "find", + "look", + "query", + "locate", + "explore", + ]; + const formattedPrompt = query.toLowerCase(); + + const intent = searchKeywords.some(keyword => + formattedPrompt.includes(keyword) + ) + ? "search" + : "chat"; + + return [{ label: intent }]; + }, + }; + + sb.stub(SmartAssistEngine, "_createEngine").resolves(fakeEngine); + for (const { prompt, expected } of cases) { const intent = await SmartAssistEngine.getPromptIntent(prompt); Assert.equal( diff --git a/toolkit/components/ml/content/EngineProcess.sys.mjs b/toolkit/components/ml/content/EngineProcess.sys.mjs @@ -172,6 +172,10 @@ export const FEATURES = { engineId: "wllamapreview", fluentId: "mlmodel-link-preview", }, + // see browser/components/genai/SmartAssistEngine.sys.mjs + "smart-intent": { + engineId: "smart-intent", + }, }; /** diff --git a/toolkit/components/ml/metrics.yaml b/toolkit/components/ml/metrics.yaml @@ -38,6 +38,7 @@ firefox.ai.runtime: - default-engine - smart-tab-embedding-engine - smart-tab-topic-engine + - smart-intent engine_creation_failure: type: event @@ -119,6 +120,7 @@ firefox.ai.runtime: - default-engine - smart-tab-embedding-engine - smart-tab-topic-engine + - smart-intent model_download: type: event