Come aggiungere funzioni personalizzate al plugin AI di ONLYOFFICE

3 dicembre 2025Da Alice

Il plugin AI di ONLYOFFICE ha introdotto una nuova architettura pensata per gli sviluppatori. Se vuoi estendere le capacità del plugin, ad esempio aggiungendo una funzione AI specifica, non è più necessario modificare un file enorme e monolitico.

Ora il plugin dispone di un workspace dedicato situato nella cartella .dev. Questa guida ti mostrerà esattamente come usare questo workspace per aggiungere una nuova funzione personalizzata.

Come esempio, costruiremo la funzione Describe image per il Document Editor.

How to add custom features to the ONLYOFFICE AI plugin

Il flusso di sviluppo

Il concetto principale è semplice: lavori in .dev, e uno script genera il codice di produzione.

  • .dev/helpers/: Questo è il tuo sandbox. Qui crei nuovi file.
  • helpers.js: Questo è il file di produzione che il plugin legge realmente. Non modificarlo direttamente. Viene generato automaticamente.

Passo 1: Scegli l’ambito dell’editor

All’interno di .dev/helpers/ troverai tre cartelle corrispondenti agli editor ONLYOFFICE:

  • word/ (Document Editor)
  • cell/ (Spreadsheet Editor)
  • slide/ (Presentation Editor)

Poiché la nostra funzione describe image è per documenti di testo, lavoreremo in .dev/helpers/word/.

Passo 2: Crea la tua funzione

Crea un nuovo file chiamato describe-image.js all’interno di .dev/helpers/word/.

Il plugin utilizza una classe specifica chiamata RegisteredFunction per definire le capacità. Questa struttura indica all’AI cosa fa la funzione e come richiamarla.

Consiglio: Assicurati che la sintassi sia corretta (attenzione a eventuali parentesi mancanti).

Il codice:

(function () {
  let func = new RegisteredFunction({
    name: "describeImage",
    description:
      "Allows users to select an image and generate a meaningful title, description, caption, or alt text for it using AI.",
    // Define parameters so the AI knows what to ask for
    parameters: {
      type: "object",
      properties: {
        prompt: {
          type: "string",
          description:
            "instruction for the AI (e.g., 'Add a short title for this chart.')",
        },
      },
      required: ["prompt"],
    },
    // Provide examples to train the AI on usage
    examples: [
      {
        prompt: "Add a short title for this chart.",
        arguments: { prompt: "Add a short title for this chart." },
      },
      {
        prompt: "Write me a 1–2 sentence description of this photo.",
        arguments: {
          prompt: "Write me a 1–2 sentence description of this photo.",
        },
      },
      {
        prompt: "Generate a descriptive caption for this organizational chart.",
        arguments: {
          prompt:
            "Generate a descriptive caption for this organizational chart.",
        },
      },
      {
        prompt: "Provide accessibility-friendly alt text for this infographic.",
        arguments: {
          prompt:
            "Provide accessibility-friendly alt text for this infographic.",
        },
      },
    ],
  });

   // The actual logic executed inside the editor
  func.call = async function (params) {
    let prompt = params.prompt;

    async function insertMessage(message) {
      Asc.scope._message = String(message || "");
      // 3. Insert the result into the document
      await Asc.Editor.callCommand(function () {
        const msg = Asc.scope._message || "";
        const doc = Api.GetDocument();
        const selected =
          (doc.GetSelectedDrawings && doc.GetSelectedDrawings()) || [];
        if (selected.length > 0) {
          for (let i = 0; i < selected.length; i++) {
            const drawing = selected[i];
            const para = Api.CreateParagraph();
            para.AddText(msg);
            drawing.InsertParagraph(para, "after", true);
          }
        } else {
          const para = Api.CreateParagraph();
          para.AddText(msg);
          let range = doc.GetCurrentParagraph();
          range.InsertParagraph(para, "after", true);
        }
        Asc.scope._message = "";
      }, true);
    }

    try {
    // 1. Get the selected image
      let imageData = await new Promise((resolve) => {
        window.Asc.plugin.executeMethod(
          "GetImageDataFromSelection",
          [],
          function (result) {
            resolve(result);
          }
        );
      });

      if (!imageData || !imageData.src) {
        await insertMessage("Please select a valid image first.");
        return;
      }

      const whiteRectangleBase64 =
        "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==";
      if (imageData.src === whiteRectangleBase64) {
        await insertMessage("Please select a valid image first.");
        return;
      }

      let argPrompt = prompt + " (for the selected image)";
      // 2. Send image + prompt to the AI engine
      let requestEngine = AI.Request.create(AI.ActionType.Chat);
      if (!requestEngine) {
        await insertMessage("AI request engine not available.");
        return;
      }

      const allowVision = /(vision|gemini|gpt-4o|gpt-4v|gpt-4-vision)/i;
      if (!allowVision.test(requestEngine.modelUI.name)) {
        await insertMessage(
          "⚠ This model may not support images. Please choose a vision-capable model (e.g. GPT-4V, Gemini, etc.)."
        );
        return;
      }

      await Asc.Editor.callMethod("StartAction", [
        "Block",
        "AI (" + requestEngine.modelUI.name + ")",
      ]);
      await Asc.Editor.callMethod("StartAction", ["GroupActions"]);

      let messages = [
        {
          role: "user",
          content: [
            { type: "text", text: argPrompt },
            {
              type: "image_url",
              image_url: { url: imageData.src, detail: "high" },
            },
          ],
        },
      ];

      let resultText = "";
      await requestEngine.chatRequest(messages, false, async function (data) {
        if (data) {
          resultText += data;
        }
      });

      await Asc.Editor.callMethod("EndAction", ["GroupActions"]);
      await Asc.Editor.callMethod("EndAction", [
        "Block",
        "AI (" + requestEngine.modelUI.name + ")",
      ]);

      Asc.scope.text = resultText || "";

      if (!Asc.scope.text.trim()) {
        await insertMessage(
          "⚠ AI request failed (maybe the model cannot handle images)."
        );
        return;
      }
      await insertMessage(Asc.scope.text);
    } catch (e) {
      try {
        await Asc.Editor.callMethod("EndAction", ["GroupActions"]);
        await Asc.Editor.callMethod("EndAction", [
          "Block",
          "AI (describeImage)",
        ]);
      } catch (ee) {
        /* ignore */
      }
      console.error("describeImage error:", e);
      await insertMessage(
        "An unexpected error occurred while describing the image."
      );
    }
  };

  return func;
})();

Passo 3: Compila le tue modifiche

Questo è il passo più importante del nuovo flusso di lavoro. Il plugin non può ancora leggere direttamente il tuo nuovo file, devi eseguire lo script helper per unire il tuo file nella logica principale del plugin.

  1. Apri il terminale.
  2. Vai nella directory helpers:
cd .dev/helpers

3. Esegui lo script Python di build:

python3 helpers.py

Lo script helpers.py cercherà nelle cartelle word/, cell/ e slide/, troverà il tuo nuovo describe-image.js e lo unirà nel file principale helpers.js.

Passo 4: Testa la tua funzione

  1. Ricarica il plugin in ONLYOFFICE.
  2. Seleziona un’immagine nel tuo documento.
  3. Chiedi all’AI: “Descrivi questa immagine” o “Scrivi una didascalia per questa.”

L’AI riconoscerà ora la tua nuova funzione personalizzata ed eseguirà la logica che hai scritto.

Crea il tuo account ONLYOFFICE gratuito

Visualizza, modifica e collabora su documenti, fogli, diapositive, moduli e file PDF online.