tor-browser

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

Chat.sys.mjs (5238B)


      1 /**
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      5 */
      6 
      7 import { ToolRoleOpts } from "moz-src:///browser/components/aiwindow/ui/modules/ChatMessage.sys.mjs";
      8 import {
      9  MODEL_FEATURES,
     10  openAIEngine,
     11 } from "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs";
     12 import {
     13  toolsConfig,
     14  getOpenTabs,
     15  searchBrowsingHistory,
     16  GetPageContent,
     17 } from "moz-src:///browser/components/aiwindow/models/Tools.sys.mjs";
     18 
     19 /**
     20 * Chat
     21 */
     22 export const Chat = {
     23  toolMap: {
     24    get_open_tabs: getOpenTabs,
     25    search_browsing_history: searchBrowsingHistory,
     26    get_page_content: GetPageContent.getPageContent.bind(GetPageContent),
     27  },
     28 
     29  /**
     30   * Stream assistant output with tool-call support.
     31   * Yields assistant text chunks as they arrive. If the model issues tool calls,
     32   * we execute them locally, append results to the conversation, and continue
     33   * streaming the model’s follow-up answer. Repeats until no more tool calls.
     34   *
     35   * @param {ChatConversation} conversation
     36   * @yields {string} Assistant text chunks
     37   */
     38  async *fetchWithHistory(conversation) {
     39    // Note FXA token fetching disabled for now - this is still in progress
     40    // We can flip this switch on when more realiable
     41    const fxAccountToken = await openAIEngine.getFxAccountToken();
     42 
     43    // @todo Bug 2007046
     44    // Update this with correct model id
     45    // Move engineInstance initialization up to access engineInstance.model
     46    const modelId = "qwen3-235b-a22b-instruct-2507-maas";
     47 
     48    const toolRoleOpts = new ToolRoleOpts(modelId);
     49    const currentTurn = conversation.currentTurnIndex();
     50    const engineInstance = await openAIEngine.build(MODEL_FEATURES.CHAT);
     51    const config = engineInstance.getConfig(engineInstance.feature);
     52    const inferenceParams = config?.parameters || {};
     53 
     54    // Helper to run the model once (streaming) on current convo
     55    const streamModelResponse = () =>
     56      engineInstance.runWithGenerator({
     57        streamOptions: { enabled: true },
     58        fxAccountToken,
     59        tool_choice: "auto",
     60        tools: toolsConfig,
     61        args: conversation.getMessagesInOpenAiFormat(),
     62        ...inferenceParams,
     63      });
     64 
     65    // Keep calling until the model finishes without requesting tools
     66    while (true) {
     67      let pendingToolCalls = null;
     68 
     69      // 1) First pass: stream tokens; capture any toolCalls
     70      for await (const chunk of streamModelResponse()) {
     71        // Stream assistant text to the UI
     72        if (chunk?.text) {
     73          yield chunk.text;
     74        }
     75 
     76        // Capture tool calls (do not echo raw tool plumbing to the user)
     77        if (chunk?.toolCalls?.length) {
     78          pendingToolCalls = chunk.toolCalls;
     79        }
     80      }
     81 
     82      // 2) Watch for tool calls; if none, we are done
     83      if (!pendingToolCalls || pendingToolCalls.length === 0) {
     84        return;
     85      }
     86 
     87      // 3) Build the assistant tool_calls message exactly as expected by the API
     88      //
     89      // @todo Bug 2006159 - Implement parallel tool calling
     90      // Temporarily only include the first tool call due to quality issue
     91      // with subsequent tool call responses, will include all later once above
     92      // ticket is resolved.
     93      const tool_calls = pendingToolCalls.slice(0, 1).map(toolCall => ({
     94        id: toolCall.id,
     95        type: "function",
     96        function: {
     97          name: toolCall.function.name,
     98          arguments: toolCall.function.arguments,
     99        },
    100      }));
    101      conversation.addAssistantMessage("function", { tool_calls });
    102 
    103      // 4) Execute each tool locally and create a tool message with the result
    104      // TODO: Temporarily only execute the first tool call, will run all later
    105      for (const toolCall of pendingToolCalls) {
    106        const { id, function: functionSpec } = toolCall;
    107        const name = functionSpec?.name || "";
    108        let toolParams = {};
    109 
    110        try {
    111          toolParams = functionSpec?.arguments
    112            ? JSON.parse(functionSpec.arguments)
    113            : {};
    114        } catch {
    115          const content = {
    116            tool_call_id: id,
    117            body: { error: "Invalid JSON arguments" },
    118          };
    119          conversation.addToolCallMessage(content, currentTurn, toolRoleOpts);
    120          continue;
    121        }
    122 
    123        let result;
    124        try {
    125          // Call the appropriate tool by name
    126          const toolFunc = this.toolMap[name];
    127          if (typeof toolFunc !== "function") {
    128            throw new Error(`No such tool: ${name}`);
    129          }
    130 
    131          result = await toolFunc(toolParams);
    132 
    133          // Create special tool call log message to show in the UI log panel
    134          const content = { tool_call_id: id, body: result };
    135          conversation.addToolCallMessage(content, currentTurn, toolRoleOpts);
    136        } catch (e) {
    137          result = { error: `Tool execution failed: ${String(e)}` };
    138          const content = { tool_call_id: id, body: result };
    139          conversation.addToolCallMessage(content, currentTurn, toolRoleOpts);
    140        }
    141 
    142        // Bug 	2006159 - Implement parallel tool calling, remove after implemented
    143        break;
    144      }
    145    }
    146  },
    147 };