mirror of
https://github.com/ONLYOFFICE/onlyoffice.github.io.git
synced 2026-04-07 14:04:30 +08:00
unification of custom assistants with text ones
This commit is contained in:
@ -34,9 +34,9 @@
|
||||
/// <reference path="./types.js" />
|
||||
|
||||
/** @param {localStorageCustomAssistantItem} assistantData */
|
||||
function AssistantHint(assistantData)
|
||||
function AssistantHint(annotationPopup, assistantData)
|
||||
{
|
||||
CustomAnnotator.call(this, assistantData);
|
||||
CustomAnnotator.call(this, annotationPopup, assistantData);
|
||||
}
|
||||
AssistantHint.prototype = Object.create(CustomAnnotator.prototype);
|
||||
AssistantHint.prototype.constructor = AssistantHint;
|
||||
@ -58,7 +58,7 @@ AssistantHint.prototype.annotateParagraph = async function(paraId, recalcId, tex
|
||||
let response = await this.chatRequest(argPrompt);
|
||||
if (!response)
|
||||
return false;
|
||||
|
||||
|
||||
let rangeId = 1;
|
||||
let ranges = [];
|
||||
|
||||
@ -216,36 +216,3 @@ AssistantHint.prototype.onAccept = async function(paraId, rangeId)
|
||||
await Asc.Editor.callMethod("EndAction", ["GroupActions"]);
|
||||
await Asc.Editor.callMethod("FocusEditor");
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} paraId
|
||||
* @param {string} rangeId
|
||||
*/
|
||||
AssistantHint.prototype.getAnnotationRangeObj = function(paraId, rangeId)
|
||||
{
|
||||
return {
|
||||
"paragraphId" : paraId,
|
||||
"rangeId" : rangeId,
|
||||
"name" : "customAssistant_" + this.assistantData.id
|
||||
};
|
||||
};
|
||||
AssistantHint.prototype._handleNewRangePositions = async function(range, paraId, text)
|
||||
{
|
||||
if (!range || range["name"] !== "customAssistant_" + this.assistantData.id || !this.paragraphs[paraId])
|
||||
return;
|
||||
|
||||
let rangeId = range["id"];
|
||||
let annot = this.getAnnotation(paraId, rangeId);
|
||||
|
||||
if (!annot)
|
||||
return;
|
||||
|
||||
let start = range["start"];
|
||||
let len = range["length"];
|
||||
|
||||
if (annot["original"] !== text.substring(start, start + len))
|
||||
{
|
||||
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
||||
Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]);
|
||||
}
|
||||
};
|
||||
|
||||
@ -34,9 +34,9 @@
|
||||
/// <reference path="./types.js" />
|
||||
|
||||
/** @param {localStorageCustomAssistantItem} assistantData */
|
||||
function AssistantReplaceHint(assistantData)
|
||||
function AssistantReplaceHint(annotationPopup, assistantData)
|
||||
{
|
||||
CustomAnnotator.call(this, assistantData);
|
||||
CustomAnnotator.call(this, annotationPopup, assistantData);
|
||||
}
|
||||
AssistantReplaceHint.prototype = Object.create(CustomAnnotator.prototype);
|
||||
AssistantReplaceHint.prototype.constructor = AssistantReplaceHint;
|
||||
@ -52,11 +52,12 @@ AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalc
|
||||
|
||||
if (text.length === 0)
|
||||
return false;
|
||||
|
||||
|
||||
const argPrompt = this._createPrompt(text);
|
||||
|
||||
let response = await this.chatRequest(argPrompt);
|
||||
if (!response)
|
||||
return false;
|
||||
return false;
|
||||
|
||||
let rangeId = 1;
|
||||
let ranges = [];
|
||||
@ -65,11 +66,11 @@ AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalc
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {ReplaceHintAiResponse[]} corrections
|
||||
* @param {ReplaceHintAiResponse[]} matches
|
||||
*/
|
||||
function convertToRanges(text, corrections)
|
||||
function convertToRanges(text, matches)
|
||||
{
|
||||
for (const { origin, suggestion, difference, reason, paragraph, occurrence, confidence } of corrections)
|
||||
for (const { origin, suggestion, difference, reason, paragraph, occurrence, confidence } of matches)
|
||||
{
|
||||
if (origin === suggestion || confidence <= 0.7)
|
||||
continue;
|
||||
@ -229,36 +230,3 @@ AssistantReplaceHint.prototype.onAccept = async function(paraId, rangeId)
|
||||
await Asc.Editor.callMethod("EndAction", ["GroupActions"]);
|
||||
await Asc.Editor.callMethod("FocusEditor");
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} paraId
|
||||
* @param {string} rangeId
|
||||
*/
|
||||
AssistantReplaceHint.prototype.getAnnotationRangeObj = function(paraId, rangeId)
|
||||
{
|
||||
return {
|
||||
"paragraphId" : paraId,
|
||||
"rangeId" : rangeId,
|
||||
"name" : "customAssistant_" + this.assistantData.id
|
||||
};
|
||||
};
|
||||
AssistantReplaceHint.prototype._handleNewRangePositions = async function(range, paraId, text)
|
||||
{
|
||||
if (!range || range["name"] !== "customAssistant_" + this.assistantData.id || !this.paragraphs[paraId])
|
||||
return;
|
||||
|
||||
let rangeId = range["id"];
|
||||
let annot = this.getAnnotation(paraId, rangeId);
|
||||
|
||||
if (!annot)
|
||||
return;
|
||||
|
||||
let start = range["start"];
|
||||
let len = range["length"];
|
||||
|
||||
if (annot["original"] !== text.substring(start, start + len))
|
||||
{
|
||||
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
||||
Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]);
|
||||
}
|
||||
};
|
||||
|
||||
@ -34,9 +34,9 @@
|
||||
/// <reference path="./types.js" />
|
||||
|
||||
/** @param {localStorageCustomAssistantItem} assistantData */
|
||||
function AssistantReplace(assistantData)
|
||||
function AssistantReplace(annotationPopup, assistantData)
|
||||
{
|
||||
CustomAnnotator.call(this, assistantData);
|
||||
CustomAnnotator.call(this, annotationPopup, assistantData);
|
||||
}
|
||||
AssistantReplace.prototype = Object.create(CustomAnnotator.prototype);
|
||||
AssistantReplace.prototype.constructor = AssistantReplace;
|
||||
@ -52,12 +52,13 @@ AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId,
|
||||
|
||||
if (text.length === 0)
|
||||
return false;
|
||||
|
||||
|
||||
const argPrompt = this._createPrompt(text);
|
||||
|
||||
let response = await this.chatRequest(argPrompt);
|
||||
if (!response)
|
||||
return false;
|
||||
|
||||
|
||||
let rangeId = 1;
|
||||
let ranges = [];
|
||||
|
||||
@ -65,11 +66,11 @@ AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId,
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {ReplaceAiResponse[]} corrections
|
||||
* @param {ReplaceAiResponse[]} matches
|
||||
*/
|
||||
function convertToRanges(text, corrections)
|
||||
function convertToRanges(text, matches)
|
||||
{
|
||||
for (const { origin, suggestion, paragraph, occurrence, confidence } of corrections)
|
||||
for (const { origin, suggestion, paragraph, occurrence, confidence } of matches)
|
||||
{
|
||||
if (origin === suggestion || confidence <= 0.7)
|
||||
continue;
|
||||
@ -216,36 +217,3 @@ AssistantReplace.prototype.onAccept = async function(paraId, rangeId)
|
||||
await Asc.Editor.callMethod("EndAction", ["GroupActions"]);
|
||||
await Asc.Editor.callMethod("FocusEditor");
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} paraId
|
||||
* @param {string} rangeId
|
||||
*/
|
||||
AssistantReplace.prototype.getAnnotationRangeObj = function(paraId, rangeId)
|
||||
{
|
||||
return {
|
||||
"paragraphId" : paraId,
|
||||
"rangeId" : rangeId,
|
||||
"name" : "customAssistant_" + this.assistantData.id
|
||||
};
|
||||
};
|
||||
AssistantReplace.prototype._handleNewRangePositions = async function(range, paraId, text)
|
||||
{
|
||||
if (!range || range["name"] !== "customAssistant_" + this.assistantData.id || !this.paragraphs[paraId])
|
||||
return;
|
||||
|
||||
let rangeId = range["id"];
|
||||
let annot = this.getAnnotation(paraId, rangeId);
|
||||
|
||||
if (!annot)
|
||||
return;
|
||||
|
||||
let start = range["start"];
|
||||
let len = range["length"];
|
||||
|
||||
if (annot["original"] !== text.substring(start, start + len))
|
||||
{
|
||||
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
||||
Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]);
|
||||
}
|
||||
};
|
||||
|
||||
@ -32,210 +32,44 @@
|
||||
|
||||
/// <reference path="./types.js" />
|
||||
|
||||
/** @param {localStorageCustomAssistantItem} assistantData */
|
||||
function CustomAnnotator(assistantData)
|
||||
function CustomAnnotator(annotationPopup, assistantData)
|
||||
{
|
||||
this.paragraphs = {};
|
||||
/** @type {Object.<string, {recalcId: number, text: string}>} */
|
||||
this.waitParagraphs = {};
|
||||
this.paraToCheck = new Set();
|
||||
this.checked = new Set(); // was checked on the previous request
|
||||
|
||||
this.type = assistantData.type; // 2
|
||||
this.assistantData = assistantData;
|
||||
TextAnnotator.call(this, annotationPopup);
|
||||
this.assistantData = assistantData;
|
||||
this.type = assistantData.type;
|
||||
}
|
||||
CustomAnnotator.prototype = Object.create(TextAnnotator.prototype);
|
||||
CustomAnnotator.prototype.constructor = CustomAnnotator;
|
||||
|
||||
/**
|
||||
* @param {string} paraId
|
||||
* @param {number} recalcId
|
||||
* @param {string} text
|
||||
* @param {string[]} ranges
|
||||
* @param {string} rangeId
|
||||
*/
|
||||
CustomAnnotator.prototype.onChangeParagraph = async function(paraId, recalcId, text, ranges)
|
||||
{
|
||||
this._handleNewRanges(ranges, paraId, text);
|
||||
this.waitParagraphs[paraId] = {
|
||||
recalcId : recalcId,
|
||||
text : text
|
||||
};
|
||||
|
||||
this._checkParagraph(paraId);
|
||||
};
|
||||
/**
|
||||
* @param {string} paraId
|
||||
* @param {string[]} ranges
|
||||
*/
|
||||
CustomAnnotator.prototype.onClick = function(paraId, ranges)
|
||||
{
|
||||
if (!ranges || !ranges.length)
|
||||
this._closePopup();
|
||||
else
|
||||
this._openPopup(paraId, ranges[0]);
|
||||
};
|
||||
CustomAnnotator.prototype.onBlur = function()
|
||||
{
|
||||
this._closePopup();
|
||||
};
|
||||
/**
|
||||
* @param {string[]} paraIds
|
||||
*/
|
||||
CustomAnnotator.prototype.checkParagraphs = async function(paraIds)
|
||||
{
|
||||
this.paraToCheck.clear()
|
||||
paraIds.forEach(function(paraId) {
|
||||
if (!this.checked.has(paraId) || this.waitParagraphs[paraId]) {
|
||||
this.paraToCheck.add(paraId);
|
||||
this._checkParagraph(paraId);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
CustomAnnotator.prototype._checkParagraph = async function(paraId)
|
||||
{
|
||||
if (!this.paraToCheck.has(paraId) || !this.waitParagraphs[paraId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
let recalcId = this.waitParagraphs[paraId].recalcId;
|
||||
let text = this.waitParagraphs[paraId].text;
|
||||
|
||||
// TODO: Temporarily for simplicity
|
||||
let range = this.getAnnotationRangeObj(paraId);
|
||||
range["rangeId"] = undefined;
|
||||
range["all"] = true;
|
||||
await Asc.Editor.callMethod("RemoveAnnotationRange", [range]);
|
||||
await this.annotateParagraph(paraId, recalcId, text);
|
||||
|
||||
delete this.waitParagraphs[paraId];
|
||||
this.paraToCheck.delete(paraId);
|
||||
|
||||
this.checked.add(paraId);
|
||||
};
|
||||
CustomAnnotator.prototype.annotateParagraph = async function(paraId, recalcId, text)
|
||||
{
|
||||
};
|
||||
CustomAnnotator.prototype._openPopup = async function(paraId, rangeId)
|
||||
{
|
||||
if (!customAnnotationPopup)
|
||||
return;
|
||||
|
||||
/** @type {InfoForPopup} */
|
||||
const popupInfo = this.getInfoForPopup(paraId, rangeId);
|
||||
|
||||
let popup = customAnnotationPopup.open(this.type, paraId, rangeId, popupInfo);
|
||||
if (!popup)
|
||||
return;
|
||||
|
||||
let _t = this;
|
||||
popup.onAccept = async function() {
|
||||
await _t.onAccept(paraId, rangeId);
|
||||
_t._closePopup();
|
||||
};
|
||||
popup.onReject = async function() {
|
||||
await _t.onReject(paraId, rangeId);
|
||||
_t._closePopup();
|
||||
};
|
||||
};
|
||||
CustomAnnotator.prototype._closePopup = function()
|
||||
{
|
||||
if (!customAnnotationPopup)
|
||||
return;
|
||||
|
||||
customAnnotationPopup.close(this.type);
|
||||
};
|
||||
CustomAnnotator.prototype.getInfoForPopup = function(paraId, rangeId)
|
||||
{
|
||||
return {};
|
||||
};
|
||||
CustomAnnotator.prototype.onAccept = async function(paraId, rangeId)
|
||||
{
|
||||
};
|
||||
CustomAnnotator.prototype.onReject = async function(paraId, rangeId)
|
||||
{
|
||||
let range = this.getAnnotationRangeObj(paraId, rangeId);
|
||||
await Asc.Editor.callMethod("RemoveAnnotationRange", [range]);
|
||||
};
|
||||
CustomAnnotator.prototype.getAnnotation = function(paraId, rangeId)
|
||||
{
|
||||
if (!paraId || !rangeId || !this.paragraphs[paraId] || !this.paragraphs[paraId][rangeId])
|
||||
return {};
|
||||
|
||||
return this.paragraphs[paraId][rangeId];
|
||||
};
|
||||
CustomAnnotator.prototype.getAnnotationRangeObj = function(paraId, rangeId)
|
||||
{
|
||||
return {
|
||||
"paragraphId" : paraId,
|
||||
"rangeId" : rangeId
|
||||
"rangeId" : rangeId,
|
||||
"name" : "customAssistant_" + this.assistantData.id
|
||||
};
|
||||
};
|
||||
CustomAnnotator.prototype._handleNewRanges = function(ranges, paraId, text)
|
||||
CustomAnnotator.prototype._handleNewRangePositions = async function(range, paraId, text)
|
||||
{
|
||||
if (!ranges || !Array.isArray(ranges))
|
||||
if (!range || range["name"] !== "customAssistant_" + this.assistantData.id || !this.paragraphs[paraId])
|
||||
return;
|
||||
|
||||
for (let i = 0; i < ranges.length; ++i)
|
||||
{
|
||||
this._handleNewRangePositions(ranges[i], paraId, text);
|
||||
}
|
||||
};
|
||||
CustomAnnotator.prototype._handleNewRangePositions = function(range, paraId, text)
|
||||
{
|
||||
};
|
||||
CustomAnnotator.prototype.chatRequest = async function(prompt)
|
||||
{
|
||||
let requestEngine = AI.Request.create(AI.ActionType.Chat);
|
||||
if (!requestEngine)
|
||||
return null;
|
||||
let rangeId = range["id"];
|
||||
let annot = this.getAnnotation(paraId, rangeId);
|
||||
|
||||
let response = await requestEngine.chatRequest(prompt, false);
|
||||
return this.normalizeResponse(response);
|
||||
};
|
||||
/**
|
||||
* Normalizes AI response by removing markdown code block wrappers
|
||||
* @param {string} response - The raw AI response that might be wrapped in ```json``` blocks
|
||||
* @returns {string} - The normalized response with markdown code blocks removed
|
||||
*/
|
||||
CustomAnnotator.prototype.normalizeResponse = function(response) {
|
||||
if (typeof response !== 'string') {
|
||||
return response;
|
||||
}
|
||||
if (!annot)
|
||||
return;
|
||||
|
||||
let start = range["start"];
|
||||
let len = range["length"];
|
||||
|
||||
// Trim whitespace
|
||||
let normalized = response.trim();
|
||||
|
||||
// Check if response is wrapped in markdown code blocks
|
||||
// Patterns: ```json\n{...}\n``` or ```\n{...}\n```
|
||||
const codeBlockPattern = /^```(?:json)?\s*\n?([\s\S]*?)\n?```$/;
|
||||
const match = normalized.match(codeBlockPattern);
|
||||
|
||||
if (match) {
|
||||
// Extract content between code block markers
|
||||
normalized = match[1].trim();
|
||||
if (annot["original"] !== text.substring(start, start + len))
|
||||
{
|
||||
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
||||
Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
};
|
||||
/**
|
||||
* @param {string} str
|
||||
* @param {string} searchStr
|
||||
* @param {string} [fromIndex]
|
||||
* @returns {number}
|
||||
*/
|
||||
CustomAnnotator.prototype.simpleGraphemeIndexOf = function(str, searchStr, fromIndex = 0) {
|
||||
const codeUnitIndex = str.indexOf(searchStr, fromIndex);
|
||||
if (codeUnitIndex < 2) {
|
||||
return codeUnitIndex;
|
||||
}
|
||||
const adjustedIndex = adjustIndexForSurrogates(str, codeUnitIndex);
|
||||
|
||||
function adjustIndexForSurrogates(str, codeUnitIndex) {
|
||||
let surrogateCount = 0;
|
||||
for (let i = 0; i < codeUnitIndex; i++) {
|
||||
const code = str.charCodeAt(i);
|
||||
if (code >= 0xD800 && code <= 0xDBFF) {
|
||||
surrogateCount++;
|
||||
}
|
||||
}
|
||||
return codeUnitIndex - surrogateCount;
|
||||
}
|
||||
return adjustedIndex;
|
||||
}
|
||||
};
|
||||
@ -36,11 +36,13 @@
|
||||
/// <reference path="./assistant-hint.js" />
|
||||
/// <reference path="./assistant-replace-hint.js" />
|
||||
/// <reference path="./assistant-replace.js" />
|
||||
/// <reference path="../text-annotations/text-annotator.js" />
|
||||
/// <reference path="./annotation-popup.js" />
|
||||
|
||||
class CustomAssistantManager {
|
||||
constructor() {
|
||||
/**
|
||||
* @type {Map<string, CustomAnnotator>}
|
||||
* @type {Map<string, TextAnnotator>}
|
||||
*/
|
||||
this._customAssistants = new Map();
|
||||
this._isCustomAssistantInit = new Map();
|
||||
@ -62,13 +64,13 @@ class CustomAssistantManager {
|
||||
}
|
||||
switch (assistantData.type) {
|
||||
case 0:
|
||||
assistant = new AssistantHint(assistantData);
|
||||
assistant = new AssistantHint(customAnnotationPopup, assistantData);
|
||||
break;
|
||||
case 1:
|
||||
assistant = new AssistantReplaceHint(assistantData);
|
||||
assistant = new AssistantReplaceHint(customAnnotationPopup, assistantData);
|
||||
break;
|
||||
case 2:
|
||||
assistant = new AssistantReplace(assistantData);
|
||||
assistant = new AssistantReplace(customAnnotationPopup, assistantData);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
|
||||
Reference in New Issue
Block a user