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:
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