mirror of
https://github.com/ONLYOFFICE/onlyoffice.github.io.git
synced 2026-04-07 14:04:30 +08:00
Unification of assistants
This commit is contained in:
@ -166,36 +166,36 @@ function CustomAnnotationPopup()
|
|||||||
|
|
||||||
if (data.type === 0) { // Hint
|
if (data.type === 0) { // Hint
|
||||||
this.content = `<div>
|
this.content = `<div>
|
||||||
<div class="ballon-color text-color border-color" style="font-size:12px; color:${textColor}; line-height:1.5; padding:10px;">${data.reason}</div>
|
<div class="ballon-color text-color border-color" style="font-size:12px; color:${textColor}; line-height:1.5; padding:10px;">${data.explanation}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
} else { // Replace + Hint or Replace
|
||||||
|
if (data.suggested) {
|
||||||
|
this.content = `<div class="back-color text-color" style="background:${backColor}; overflow:hidden; max-width:320px; min-width:280px;color:${textColor}; user-select:none;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;">
|
||||||
|
<div style="padding:16px 16px 0px 16px;">
|
||||||
|
|
||||||
if (data.suggested) {
|
<div style="margin-bottom:12px;">
|
||||||
this.content = `<div class="back-color text-color" style="background:${backColor}; overflow:hidden; max-width:320px; min-width:280px;color:${textColor}; user-select:none;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;">
|
<div class="text-color" style="font-size:11px; font-weight:700; color:${textColor}; margin-bottom:6px;">
|
||||||
<div style="padding:16px 16px 0px 16px;">
|
${window.Asc.plugin.tr("Suggested correction")}
|
||||||
|
|
||||||
<div style="margin-bottom:12px;">
|
|
||||||
<div class="text-color" style="font-size:11px; font-weight:700; color:${textColor}; margin-bottom:6px;">
|
|
||||||
${window.Asc.plugin.tr("Suggested correction")}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ballon-color text-color border-color" style="font-size:12px; color:${textColor}; line-height:1.5; background:${ballonColor}; border:1px solid ${borderColor}; border-radius:3px; padding:10px;">
|
|
||||||
<div style="display:flex; align-items:center; gap:8px;">
|
|
||||||
<span class="text-color" style="color:${textColor}; font-weight:normal;">${data.original}</span>
|
|
||||||
<span class="text-color" style="color:${textColor}; font-weight:bold;">→</span>
|
|
||||||
<span class="text-color" style="color:${textColor}; font-weight:normal;">${data.suggested}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
if (data.explanation) {
|
|
||||||
this.content += `<div style="margin-bottom:16px;">
|
|
||||||
<div class="text-color" class="text-color" style="font-size:11px; font-weight:700; color:${textColor}; margin-bottom:6px;">
|
|
||||||
${window.Asc.plugin.tr("Explanation")}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ballon-color text-color border-color" style="font-size:12px; color:${textColor}; line-height:1.5; background:${ballonColor}; border:1px solid ${borderColor}; border-radius:3px; padding:10px;">${data.explanation}</div>
|
<div class="ballon-color text-color border-color" style="font-size:12px; color:${textColor}; line-height:1.5; background:${ballonColor}; border:1px solid ${borderColor}; border-radius:3px; padding:10px;">
|
||||||
</div>`;
|
<div style="display:flex; align-items:center; gap:8px;">
|
||||||
|
<span class="text-color" style="color:${textColor}; font-weight:normal;">${data.original}</span>
|
||||||
|
<span class="text-color" style="color:${textColor}; font-weight:bold;">→</span>
|
||||||
|
<span class="text-color" style="color:${textColor}; font-weight:normal;">${data.suggested}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
if (data.explanation) {
|
||||||
|
this.content += `<div style="margin-bottom:16px;">
|
||||||
|
<div class="text-color" class="text-color" style="font-size:11px; font-weight:700; color:${textColor}; margin-bottom:6px;">
|
||||||
|
${window.Asc.plugin.tr("Explanation")}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ballon-color text-color border-color" style="font-size:12px; color:${textColor}; line-height:1.5; background:${ballonColor}; border:1px solid ${borderColor}; border-radius:3px; padding:10px;">${data.explanation}</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.content += "</div></div>";
|
this.content += "</div></div>";
|
||||||
|
|||||||
@ -40,16 +40,13 @@ function AssistantHint(assistantData)
|
|||||||
this.type = assistantData.type; // 0
|
this.type = assistantData.type; // 0
|
||||||
this.assistantData = assistantData;
|
this.assistantData = assistantData;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssistantHint.prototype = Object.create(CustomAnnotator.prototype);
|
AssistantHint.prototype = Object.create(CustomAnnotator.prototype);
|
||||||
AssistantHint.prototype.constructor = AssistantHint;
|
AssistantHint.prototype.constructor = AssistantHint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param {string} paraId
|
* @param {string} paraId
|
||||||
* @param {string} recalcId
|
* @param {string} recalcId
|
||||||
* @param {string} text
|
* @param {string} text
|
||||||
* @returns
|
|
||||||
*/
|
*/
|
||||||
AssistantHint.prototype.annotateParagraph = async function(paraId, recalcId, text)
|
AssistantHint.prototype.annotateParagraph = async function(paraId, recalcId, text)
|
||||||
{
|
{
|
||||||
@ -71,11 +68,9 @@ AssistantHint.prototype.annotateParagraph = async function(paraId, recalcId, tex
|
|||||||
let response = "";
|
let response = "";
|
||||||
await requestEngine.chatRequest(argPrompt, false, async function (/** @type {string} */data)
|
await requestEngine.chatRequest(argPrompt, false, async function (/** @type {string} */data)
|
||||||
{
|
{
|
||||||
if (!data) {
|
if (!data)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
await checkEndAction();
|
await checkEndAction();
|
||||||
|
|
||||||
response += data;
|
response += data;
|
||||||
});
|
});
|
||||||
await checkEndAction();
|
await checkEndAction();
|
||||||
@ -103,7 +98,7 @@ AssistantHint.prototype.annotateParagraph = async function(paraId, recalcId, tex
|
|||||||
{
|
{
|
||||||
const index = text.indexOf(origin, searchStart);
|
const index = text.indexOf(origin, searchStart);
|
||||||
if (index === -1) break;
|
if (index === -1) break;
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
if (count === occurrence)
|
if (count === occurrence)
|
||||||
{
|
{
|
||||||
@ -114,7 +109,7 @@ AssistantHint.prototype.annotateParagraph = async function(paraId, recalcId, tex
|
|||||||
});
|
});
|
||||||
_t.paragraphs[paraId][rangeId] = {
|
_t.paragraphs[paraId][rangeId] = {
|
||||||
"original" : origin,
|
"original" : origin,
|
||||||
"reason" : reason,
|
"reason" : reason
|
||||||
};
|
};
|
||||||
++rangeId;
|
++rangeId;
|
||||||
break;
|
break;
|
||||||
@ -140,8 +135,13 @@ AssistantHint.prototype.annotateParagraph = async function(paraId, recalcId, tex
|
|||||||
{ }
|
{ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} text
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
AssistantHint.prototype._createPrompt = function(text) {
|
AssistantHint.prototype._createPrompt = function(text) {
|
||||||
return `You are a text analysis specialist. Your task is to find text fragments that match the user's criteria.
|
let prompt = `You are a multi-disciplinary text analysis assistant.
|
||||||
|
Your task is to find text fragments that match the user's criteria.
|
||||||
|
|
||||||
MANDATORY RULES:
|
MANDATORY RULES:
|
||||||
1. Analyze ONLY the provided text.
|
1. Analyze ONLY the provided text.
|
||||||
@ -173,7 +173,7 @@ AssistantHint.prototype._createPrompt = function(text) {
|
|||||||
- "confidence": Value between 0 and 1 indicating certainty (1.0 = completely certain, 0.5 = uncertain)
|
- "confidence": Value between 0 and 1 indicating certainty (1.0 = completely certain, 0.5 = uncertain)
|
||||||
|
|
||||||
CRITICAL
|
CRITICAL
|
||||||
- Ouput should be in the exact this format
|
- Output should be in the exact this format
|
||||||
- No any comments are allowed
|
- No any comments are allowed
|
||||||
|
|
||||||
CRITICAL - Output Format:
|
CRITICAL - Output Format:
|
||||||
@ -182,15 +182,14 @@ AssistantHint.prototype._createPrompt = function(text) {
|
|||||||
- DO NOT include any explanatory text before or after the JSON
|
- DO NOT include any explanatory text before or after the JSON
|
||||||
- DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible
|
- DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible
|
||||||
- The response should start with [ and end with ]
|
- The response should start with [ and end with ]
|
||||||
|
`;
|
||||||
USER REQUEST: ${this.assistantData.query}
|
prompt += "\n\nUSER REQUEST:\n```" + this.assistantData.query + "\n```\n\n";
|
||||||
|
|
||||||
TEXT TO ANALYZE:
|
prompt += "TEXT TO ANALYZE:\n```\n" + text + "\n```\n\n";
|
||||||
"""
|
|
||||||
${text}
|
|
||||||
"""
|
|
||||||
|
|
||||||
Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`;
|
prompt += `Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`;
|
||||||
|
|
||||||
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -203,7 +202,7 @@ AssistantHint.prototype.getInfoForPopup = function(paraId, rangeId)
|
|||||||
let _s = this.getAnnotation(paraId, rangeId);
|
let _s = this.getAnnotation(paraId, rangeId);
|
||||||
return {
|
return {
|
||||||
original : _s["original"],
|
original : _s["original"],
|
||||||
reason : _s["reason"],
|
explanation : _s["reason"],
|
||||||
type : this.type
|
type : this.type
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -215,7 +214,7 @@ AssistantHint.prototype.getInfoForPopup = function(paraId, rangeId)
|
|||||||
AssistantHint.prototype.onAccept = async function(paraId, rangeId)
|
AssistantHint.prototype.onAccept = async function(paraId, rangeId)
|
||||||
{
|
{
|
||||||
await Asc.Editor.callMethod("StartAction", ["GroupActions"]);
|
await Asc.Editor.callMethod("StartAction", ["GroupActions"]);
|
||||||
|
|
||||||
let range = this.getAnnotationRangeObj(paraId, rangeId);
|
let range = this.getAnnotationRangeObj(paraId, rangeId);
|
||||||
await Asc.Editor.callMethod("SelectAnnotationRange", [range]);
|
await Asc.Editor.callMethod("SelectAnnotationRange", [range]);
|
||||||
|
|
||||||
@ -253,7 +252,7 @@ AssistantHint.prototype._handleNewRangePositions = async function(range, paraId,
|
|||||||
|
|
||||||
let start = range["start"];
|
let start = range["start"];
|
||||||
let len = range["length"];
|
let len = range["length"];
|
||||||
|
|
||||||
if (annot["original"] !== text.substring(start, start + len))
|
if (annot["original"] !== text.substring(start, start + len))
|
||||||
{
|
{
|
||||||
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
||||||
|
|||||||
@ -40,16 +40,20 @@ function AssistantReplaceHint(assistantData)
|
|||||||
this.type = assistantData.type; // 1
|
this.type = assistantData.type; // 1
|
||||||
this.assistantData = assistantData;
|
this.assistantData = assistantData;
|
||||||
}
|
}
|
||||||
|
|
||||||
AssistantReplaceHint.prototype = Object.create(CustomAnnotator.prototype);
|
AssistantReplaceHint.prototype = Object.create(CustomAnnotator.prototype);
|
||||||
AssistantReplaceHint.prototype.constructor = AssistantReplaceHint;
|
AssistantReplaceHint.prototype.constructor = AssistantReplaceHint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} paraId
|
||||||
|
* @param {string} recalcId
|
||||||
|
* @param {string} text
|
||||||
|
*/
|
||||||
AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalcId, text)
|
AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalcId, text)
|
||||||
{
|
{
|
||||||
this.paragraphs[paraId] = {};
|
this.paragraphs[paraId] = {};
|
||||||
|
|
||||||
let requestEngine = AI.Request.create(AI.ActionType.Chat);
|
let requestEngine = AI.Request.create(AI.ActionType.Chat);
|
||||||
if (!requestEngine)
|
if (!requestEngine || text.length === 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let isSendedEndLongAction = false;
|
let isSendedEndLongAction = false;
|
||||||
@ -59,15 +63,14 @@ AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalc
|
|||||||
isSendedEndLongAction = true;
|
isSendedEndLongAction = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let argPrompt = this._createPrompt(text);
|
const argPrompt = this._createPrompt(text);
|
||||||
|
|
||||||
let response = "";
|
let response = "";
|
||||||
await requestEngine.chatRequest(argPrompt, false, async function (data)
|
await requestEngine.chatRequest(argPrompt, false, async function (/** @type {string} */data)
|
||||||
{
|
{
|
||||||
if (!data)
|
if (!data)
|
||||||
return;
|
return;
|
||||||
await checkEndAction();
|
await checkEndAction();
|
||||||
|
|
||||||
response += data;
|
response += data;
|
||||||
});
|
});
|
||||||
await checkEndAction();
|
await checkEndAction();
|
||||||
@ -76,14 +79,14 @@ AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalc
|
|||||||
let ranges = [];
|
let ranges = [];
|
||||||
|
|
||||||
let _t = this;
|
let _t = this;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} text
|
* @param {string} text
|
||||||
* @param {ReplaceHintAiResponse[]} corrections
|
* @param {ReplaceHintAiResponse[]} corrections
|
||||||
*/
|
*/
|
||||||
function convertToRanges(text, corrections)
|
function convertToRanges(text, corrections)
|
||||||
{
|
{
|
||||||
for (const { origin, suggestion, difference, description, occurrence, confidence } of corrections)
|
for (const { origin, suggestion, difference, reason, paragraph, occurrence, confidence } of corrections)
|
||||||
{
|
{
|
||||||
if (origin === suggestion || confidence <= 0.7)
|
if (origin === suggestion || confidence <= 0.7)
|
||||||
continue;
|
continue;
|
||||||
@ -95,7 +98,7 @@ AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalc
|
|||||||
{
|
{
|
||||||
const index = text.indexOf(origin, searchStart);
|
const index = text.indexOf(origin, searchStart);
|
||||||
if (index === -1) break;
|
if (index === -1) break;
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
if (count === occurrence)
|
if (count === occurrence)
|
||||||
{
|
{
|
||||||
@ -108,7 +111,7 @@ AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalc
|
|||||||
"original" : origin,
|
"original" : origin,
|
||||||
"suggestion" : suggestion,
|
"suggestion" : suggestion,
|
||||||
"difference" : difference,
|
"difference" : difference,
|
||||||
"description" : description
|
"reason" : reason
|
||||||
};
|
};
|
||||||
++rangeId;
|
++rangeId;
|
||||||
break;
|
break;
|
||||||
@ -139,12 +142,12 @@ AssistantReplaceHint.prototype.annotateParagraph = async function(paraId, recalc
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
AssistantReplaceHint.prototype._createPrompt = function(text) {
|
AssistantReplaceHint.prototype._createPrompt = function(text) {
|
||||||
return `You are an intelligent text analysis and transformation assistant.
|
let prompt = `You are a multi-disciplinary text analysis and transformation assistant.
|
||||||
Your task is to analyze text and identify elements that match user-defined criteria for replacement.
|
Your task is to analyze text based on user's specific criteria and provide intelligent corrections.
|
||||||
|
|
||||||
MANDATORY RULES:
|
MANDATORY RULES:
|
||||||
1. UNDERSTAND the user's intent from their criteria.
|
1. UNDERSTAND the user's intent from their criteria.
|
||||||
2. FIND all text elements matching the criteria.
|
2. Find words, phrases, or sentences that match the user's criteria.
|
||||||
3. For EACH match you find:
|
3. For EACH match you find:
|
||||||
- Provide the exact quote.
|
- Provide the exact quote.
|
||||||
- SUGGEST appropriate replacements.
|
- SUGGEST appropriate replacements.
|
||||||
@ -152,27 +155,14 @@ AssistantReplaceHint.prototype._createPrompt = function(text) {
|
|||||||
- Provide position information (paragraph number).
|
- Provide position information (paragraph number).
|
||||||
4. If no matches are found, return an empty array: [].
|
4. If no matches are found, return an empty array: [].
|
||||||
5. Format your response STRICTLY in JSON format.
|
5. Format your response STRICTLY in JSON format.
|
||||||
|
6. Support multiple languages (English, Russian, etc.)
|
||||||
ANALYSIS FRAMEWORK:
|
|
||||||
For each text element, consider:
|
|
||||||
- SEMANTIC: Does it match the meaning criteria?
|
|
||||||
- STYLISTIC: Does it match the style criteria?
|
|
||||||
- CONTEXTUAL: Is it appropriate for the context?
|
|
||||||
- FUNCTIONAL: Does it serve the intended purpose?
|
|
||||||
|
|
||||||
REPLACEMENT STRATEGIES:
|
|
||||||
1. Direct synonym replacement
|
|
||||||
2. Paraphrasing for better fit
|
|
||||||
3. Complete restructuring if needed
|
|
||||||
4. Adding/removing elements as required
|
|
||||||
5. Adjusting tone or register
|
|
||||||
|
|
||||||
Response format - return ONLY this JSON array with no additional text:
|
Response format - return ONLY this JSON array with no additional text:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"origin": "exact text fragment that matches the query",
|
"origin": "exact text fragment that matches the query",
|
||||||
"suggestion": "suggested replacement",
|
"suggestion": "suggested replacement",
|
||||||
"description": "detailed explanation why it matches the criteria",
|
"reason": "detailed explanation why it matches the criteria",
|
||||||
"difference":"difference between origin and suggestion"
|
"difference":"difference between origin and suggestion"
|
||||||
"paragraph": paragraph_number,
|
"paragraph": paragraph_number,
|
||||||
"occurrence": 1,
|
"occurrence": 1,
|
||||||
@ -185,16 +175,15 @@ AssistantReplaceHint.prototype._createPrompt = function(text) {
|
|||||||
- "suggestion": Your suggested replacement for the fragment.
|
- "suggestion": Your suggested replacement for the fragment.
|
||||||
* Ensure it aligns with the user's criteria.
|
* Ensure it aligns with the user's criteria.
|
||||||
* Maintain coherence with surrounding text.
|
* Maintain coherence with surrounding text.
|
||||||
- "description": Clear explanation of why this fragment matches the criteria.
|
- "reason": Clear explanation of why this fragment matches the criteria.
|
||||||
- "difference": The difference between origin and suggestion in html format: the differences wrapped with <strong> tag
|
- "difference": The difference between origin and suggestion in html format: the differences wrapped with <strong> tag
|
||||||
- "paragraph": Paragraph number where the fragment is found (0-based index)
|
- "paragraph": Paragraph number where the fragment is found (0-based index)
|
||||||
- "occurrence": Which occurrence of this sentence if it appears multiple times (1 for first, 2 for second, etc.)
|
- "occurrence": Which occurrence of this sentence if it appears multiple times (1 for first, 2 for second, etc.)
|
||||||
- "confidence": Value between 0 and 1 indicating certainty (1.0 = completely certain, 0.5 = uncertain)
|
- "confidence": Value between 0 and 1 indicating certainty (1.0 = completely certain, 0.5 = uncertain)
|
||||||
|
|
||||||
CRITICAL - Word Boundaries (MOST IMPORTANT):
|
CRITICAL:
|
||||||
- ONLY match complete, standalone words separated by spaces, punctuation, or at the start/end of text
|
- Output should be in the exact this format
|
||||||
- DO NOT match letters or substrings that are PART of other words
|
- No any comments are allowed
|
||||||
- A word is bounded by: spaces, punctuation (.,!?;:), quotes, or start/end of text
|
|
||||||
|
|
||||||
CRITICAL - Output Format:
|
CRITICAL - Output Format:
|
||||||
- Return ONLY the raw JSON array, nothing else
|
- Return ONLY the raw JSON array, nothing else
|
||||||
@ -202,14 +191,14 @@ AssistantReplaceHint.prototype._createPrompt = function(text) {
|
|||||||
- DO NOT include any explanatory text before or after the JSON
|
- DO NOT include any explanatory text before or after the JSON
|
||||||
- DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible
|
- DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible
|
||||||
- The response should start with [ and end with ]
|
- The response should start with [ and end with ]
|
||||||
|
`;
|
||||||
USER REQUEST: ${this.assistantData.query}
|
prompt += "\n\nUSER REQUEST:\n```" + this.assistantData.query + "\n```\n\n";
|
||||||
|
|
||||||
TEXT TO ANALYZE:
|
prompt += "TEXT TO ANALYZE:\n```\n" + text + "\n```\n\n";
|
||||||
"""
|
|
||||||
${text}
|
prompt += `Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`;
|
||||||
"""
|
|
||||||
`;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -221,18 +210,23 @@ AssistantReplaceHint.prototype.getInfoForPopup = function(paraId, rangeId)
|
|||||||
{
|
{
|
||||||
let _s = this.getAnnotation(paraId, rangeId);
|
let _s = this.getAnnotation(paraId, rangeId);
|
||||||
return {
|
return {
|
||||||
suggested : _s["difference"],
|
|
||||||
original : _s["original"],
|
original : _s["original"],
|
||||||
explanation : _s["description"],
|
suggested : _s["difference"],
|
||||||
|
explanation : _s["reason"],
|
||||||
type : this.type
|
type : this.type
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} paraId
|
||||||
|
* @param {string} rangeId
|
||||||
|
*/
|
||||||
AssistantReplaceHint.prototype.onAccept = async function(paraId, rangeId)
|
AssistantReplaceHint.prototype.onAccept = async function(paraId, rangeId)
|
||||||
{
|
{
|
||||||
let text = this.getAnnotation(paraId, rangeId)["suggestion"];
|
let text = this.getAnnotation(paraId, rangeId)["suggestion"];
|
||||||
|
|
||||||
await Asc.Editor.callMethod("StartAction", ["GroupActions"]);
|
await Asc.Editor.callMethod("StartAction", ["GroupActions"]);
|
||||||
|
|
||||||
let range = this.getAnnotationRangeObj(paraId, rangeId);
|
let range = this.getAnnotationRangeObj(paraId, rangeId);
|
||||||
await Asc.Editor.callMethod("SelectAnnotationRange", [range]);
|
await Asc.Editor.callMethod("SelectAnnotationRange", [range]);
|
||||||
|
|
||||||
@ -246,6 +240,11 @@ AssistantReplaceHint.prototype.onAccept = async function(paraId, rangeId)
|
|||||||
await Asc.Editor.callMethod("EndAction", ["GroupActions"]);
|
await Asc.Editor.callMethod("EndAction", ["GroupActions"]);
|
||||||
await Asc.Editor.callMethod("FocusEditor");
|
await Asc.Editor.callMethod("FocusEditor");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} paraId
|
||||||
|
* @param {string} rangeId
|
||||||
|
*/
|
||||||
AssistantReplaceHint.prototype.getAnnotationRangeObj = function(paraId, rangeId)
|
AssistantReplaceHint.prototype.getAnnotationRangeObj = function(paraId, rangeId)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
@ -267,7 +266,7 @@ AssistantReplaceHint.prototype._handleNewRangePositions = async function(range,
|
|||||||
|
|
||||||
let start = range["start"];
|
let start = range["start"];
|
||||||
let len = range["length"];
|
let len = range["length"];
|
||||||
|
|
||||||
if (annot["original"] !== text.substring(start, start + len))
|
if (annot["original"] !== text.substring(start, start + len))
|
||||||
{
|
{
|
||||||
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
||||||
|
|||||||
@ -43,12 +43,17 @@ function AssistantReplace(assistantData)
|
|||||||
AssistantReplace.prototype = Object.create(CustomAnnotator.prototype);
|
AssistantReplace.prototype = Object.create(CustomAnnotator.prototype);
|
||||||
AssistantReplace.prototype.constructor = AssistantReplace;
|
AssistantReplace.prototype.constructor = AssistantReplace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} paraId
|
||||||
|
* @param {string} recalcId
|
||||||
|
* @param {string} text
|
||||||
|
*/
|
||||||
AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId, text)
|
AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId, text)
|
||||||
{
|
{
|
||||||
this.paragraphs[paraId] = {};
|
this.paragraphs[paraId] = {};
|
||||||
|
|
||||||
let requestEngine = AI.Request.create(AI.ActionType.Chat);
|
let requestEngine = AI.Request.create(AI.ActionType.Chat);
|
||||||
if (!requestEngine)
|
if (!requestEngine || text.length === 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
let isSendedEndLongAction = false;
|
let isSendedEndLongAction = false;
|
||||||
@ -59,8 +64,9 @@ AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const argPrompt = this._createPrompt(text);
|
const argPrompt = this._createPrompt(text);
|
||||||
|
|
||||||
let response = "";
|
let response = "";
|
||||||
await requestEngine.chatRequest(argPrompt, false, async function (data)
|
await requestEngine.chatRequest(argPrompt, false, async function (/** @type {string} */data)
|
||||||
{
|
{
|
||||||
if (!data)
|
if (!data)
|
||||||
return;
|
return;
|
||||||
@ -80,9 +86,9 @@ AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId,
|
|||||||
*/
|
*/
|
||||||
function convertToRanges(text, corrections)
|
function convertToRanges(text, corrections)
|
||||||
{
|
{
|
||||||
for (const { wrong, correct, reason, paragraph, occurrence, confidence } of corrections)
|
for (const { origin, suggestion, paragraph, occurrence, confidence } of corrections)
|
||||||
{
|
{
|
||||||
if (wrong === correct)
|
if (origin === suggestion || confidence <= 0.7)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
@ -90,35 +96,29 @@ AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId,
|
|||||||
|
|
||||||
while (searchStart < text.length)
|
while (searchStart < text.length)
|
||||||
{
|
{
|
||||||
const index = text.indexOf(wrong, searchStart);
|
const index = text.indexOf(origin, searchStart);
|
||||||
if (index === -1) break;
|
if (index === -1) break;
|
||||||
|
|
||||||
const isStartBoundary = index === 0 || _t._isWordBoundary(text[index - 1]);
|
count++;
|
||||||
const isEndBoundary = index + wrong.length === text.length || _t._isWordBoundary(text[index + wrong.length]);
|
if (count === occurrence)
|
||||||
|
|
||||||
if (isStartBoundary && isEndBoundary)
|
|
||||||
{
|
{
|
||||||
count++;
|
ranges.push({
|
||||||
if (count === occurrence)
|
"start": index,
|
||||||
{
|
"length": origin.length,
|
||||||
ranges.push({
|
"id": rangeId
|
||||||
"start": index,
|
});
|
||||||
"length": wrong.length,
|
_t.paragraphs[paraId][rangeId] = {
|
||||||
"id": rangeId
|
"original" : origin,
|
||||||
});
|
"suggestion" : suggestion,
|
||||||
_t.paragraphs[paraId][rangeId] = {
|
};
|
||||||
"suggested" : correct,
|
++rangeId;
|
||||||
"original" : wrong
|
break;
|
||||||
};
|
|
||||||
++rangeId;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
searchStart = index + 1;
|
searchStart = index + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
convertToRanges(text, JSON.parse(response));
|
convertToRanges(text, JSON.parse(response));
|
||||||
@ -140,8 +140,8 @@ AssistantReplace.prototype.annotateParagraph = async function(paraId, recalcId,
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
AssistantReplace.prototype._createPrompt = function(text) {
|
AssistantReplace.prototype._createPrompt = function(text) {
|
||||||
return `You are an intelligent text analysis and transformation assistant.
|
let prompt = `You are a multi-disciplinary text analysis and transformation assistant.
|
||||||
Your task is to analyze text and identify words that match user-defined criteria for replacement.
|
Your task is to analyze text based on user's specific criteria and provide intelligent corrections.
|
||||||
|
|
||||||
MANDATORY RULES:
|
MANDATORY RULES:
|
||||||
1. UNDERSTAND the user's intent from their criteria.
|
1. UNDERSTAND the user's intent from their criteria.
|
||||||
@ -153,26 +153,13 @@ AssistantReplace.prototype._createPrompt = function(text) {
|
|||||||
- Provide position information (paragraph number).
|
- Provide position information (paragraph number).
|
||||||
4. If no matches are found, return an empty array: [].
|
4. If no matches are found, return an empty array: [].
|
||||||
5. Format your response STRICTLY in JSON format.
|
5. Format your response STRICTLY in JSON format.
|
||||||
|
6. Support multiple languages (English, Russian, etc.)
|
||||||
ANALYSIS FRAMEWORK:
|
|
||||||
For each text element, consider:
|
|
||||||
- SEMANTIC: Does it match the meaning criteria?
|
|
||||||
- STYLISTIC: Does it match the style criteria?
|
|
||||||
- CONTEXTUAL: Is it appropriate for the context?
|
|
||||||
- FUNCTIONAL: Does it serve the intended purpose?
|
|
||||||
|
|
||||||
REPLACEMENT STRATEGIES:
|
|
||||||
1. Direct synonym replacement
|
|
||||||
2. Paraphrasing for better fit
|
|
||||||
3. Complete restructuring if needed
|
|
||||||
4. Adding/removing elements as required
|
|
||||||
5. Adjusting tone or register
|
|
||||||
|
|
||||||
Response format - return ONLY this JSON array with no additional text:
|
Response format - return ONLY this JSON array with no additional text:
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"wrong": "exact text fragment that matches the query",
|
"origin": "exact text fragment that matches the query",
|
||||||
"correct": "suggested replacement",
|
"suggestion": "suggested replacement",
|
||||||
"paragraph": paragraph_number,
|
"paragraph": paragraph_number,
|
||||||
"occurrence": 1,
|
"occurrence": 1,
|
||||||
"confidence": 0.95
|
"confidence": 0.95
|
||||||
@ -180,18 +167,17 @@ AssistantReplace.prototype._createPrompt = function(text) {
|
|||||||
]
|
]
|
||||||
|
|
||||||
Guidelines for each field:
|
Guidelines for each field:
|
||||||
- "wrong": EXACT UNCHANGED original text fragment. Do not fix anything in this field.
|
- "origin": EXACT UNCHANGED original text fragment. Do not fix anything in this field.
|
||||||
- "correct": Your suggested replacement for the fragment.
|
- "suggestion": Your suggested replacement for the fragment.
|
||||||
* Ensure it aligns with the user's criteria.
|
* Ensure it aligns with the user's criteria.
|
||||||
* Maintain coherence with surrounding text.
|
* Maintain coherence with surrounding text.
|
||||||
- "paragraph": Paragraph number where the fragment is found (0-based index)
|
- "paragraph": Paragraph number where the fragment is found (0-based index)
|
||||||
- "occurrence": Which occurrence of this sentence if it appears multiple times (1 for first, 2 for second, etc.)
|
- "occurrence": Which occurrence of this sentence if it appears multiple times (1 for first, 2 for second, etc.)
|
||||||
- "confidence": Value between 0 and 1 indicating certainty (1.0 = completely certain, 0.5 = uncertain)
|
- "confidence": Value between 0 and 1 indicating certainty (1.0 = completely certain, 0.5 = uncertain)
|
||||||
|
|
||||||
CRITICAL - Word Boundaries (MOST IMPORTANT):
|
CRITICAL:
|
||||||
- ONLY match complete, standalone words separated by spaces, punctuation, or at the start/end of text
|
- Output should be in the exact this format
|
||||||
- DO NOT match letters or substrings that are PART of other words
|
- No any comments are allowed
|
||||||
- A word is bounded by: spaces, punctuation (.,!?;:), quotes, or start/end of text
|
|
||||||
|
|
||||||
CRITICAL - Output Format:
|
CRITICAL - Output Format:
|
||||||
- Return ONLY the raw JSON array, nothing else
|
- Return ONLY the raw JSON array, nothing else
|
||||||
@ -199,15 +185,14 @@ AssistantReplace.prototype._createPrompt = function(text) {
|
|||||||
- DO NOT include any explanatory text before or after the JSON
|
- DO NOT include any explanatory text before or after the JSON
|
||||||
- DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible
|
- DO NOT use escaped newlines (\\n) - return the JSON on a single line if possible
|
||||||
- The response should start with [ and end with ]
|
- The response should start with [ and end with ]
|
||||||
|
`;
|
||||||
USER REQUEST: ${this.assistantData.query}
|
prompt += "\n\nUSER REQUEST:\n```" + this.assistantData.query + "\n```\n\n";
|
||||||
|
|
||||||
TEXT TO ANALYZE:
|
prompt += "TEXT TO ANALYZE:\n```\n" + text + "\n```\n\n";
|
||||||
"""
|
|
||||||
${text}
|
|
||||||
"""
|
|
||||||
|
|
||||||
Please analyze this text and find all words that match the user's request. Be thorough but precise.`;
|
prompt += `Please analyze this text and find all fragments that match the user's request. Be thorough but precise.`;
|
||||||
|
|
||||||
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,33 +202,42 @@ AssistantReplace.prototype._createPrompt = function(text) {
|
|||||||
*/
|
*/
|
||||||
AssistantReplace.prototype.getInfoForPopup = function(paraId, rangeId)
|
AssistantReplace.prototype.getInfoForPopup = function(paraId, rangeId)
|
||||||
{
|
{
|
||||||
let anot = this.getAnnotation(paraId, rangeId);
|
let _s = this.getAnnotation(paraId, rangeId);
|
||||||
return {
|
return {
|
||||||
suggested : anot["suggested"],
|
original : _s["original"],
|
||||||
original : anot["original"],
|
suggested : _s["suggestion"],
|
||||||
type : this.type
|
type : this.type
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} paraId
|
||||||
|
* @param {string} rangeId
|
||||||
|
*/
|
||||||
AssistantReplace.prototype.onAccept = async function(paraId, rangeId)
|
AssistantReplace.prototype.onAccept = async function(paraId, rangeId)
|
||||||
{
|
{
|
||||||
let anot = this.getAnnotation(paraId, rangeId);
|
let text = this.getAnnotation(paraId, rangeId)["suggestion"];
|
||||||
if (!anot)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let range = this.getAnnotationRangeObj(paraId, rangeId);
|
|
||||||
await Asc.Editor.callMethod("StartAction", ["GroupActions"]);
|
await Asc.Editor.callMethod("StartAction", ["GroupActions"]);
|
||||||
|
|
||||||
|
let range = this.getAnnotationRangeObj(paraId, rangeId);
|
||||||
await Asc.Editor.callMethod("SelectAnnotationRange", [range]);
|
await Asc.Editor.callMethod("SelectAnnotationRange", [range]);
|
||||||
|
|
||||||
Asc.scope.text = anot["suggested"];
|
Asc.scope.text = text;
|
||||||
await Asc.Editor.callCommand(function(){
|
await Asc.Editor.callCommand(function(){
|
||||||
Api.ReplaceTextSmart([Asc.scope.text]);
|
Api.ReplaceTextSmart([Asc.scope.text]);
|
||||||
Api.GetDocument().RemoveSelection();
|
Api.GetDocument().RemoveSelection();
|
||||||
});
|
});
|
||||||
|
|
||||||
await Asc.Editor.callMethod("RemoveAnnotationRange", [range]);
|
await Asc.Editor.callMethod("RemoveAnnotationRange", [range]);
|
||||||
await Asc.Editor.callMethod("EndAction", ["GroupActions"]);
|
await Asc.Editor.callMethod("EndAction", ["GroupActions"]);
|
||||||
await Asc.Editor.callMethod("FocusEditor");
|
await Asc.Editor.callMethod("FocusEditor");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} paraId
|
||||||
|
* @param {string} rangeId
|
||||||
|
*/
|
||||||
AssistantReplace.prototype.getAnnotationRangeObj = function(paraId, rangeId)
|
AssistantReplace.prototype.getAnnotationRangeObj = function(paraId, rangeId)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
@ -265,17 +259,10 @@ AssistantReplace.prototype._handleNewRangePositions = async function(range, para
|
|||||||
|
|
||||||
let start = range["start"];
|
let start = range["start"];
|
||||||
let len = range["length"];
|
let len = range["length"];
|
||||||
|
|
||||||
const isStartBoundary = start === 0 || this._isWordBoundary(text[start - 1]);
|
if (annot["original"] !== text.substring(start, start + len))
|
||||||
const isEndBoundary = start + len === text.length || this._isWordBoundary(text[start + len]);
|
|
||||||
|
|
||||||
if (!isStartBoundary || !isEndBoundary || annot["original"] !== text.substring(start, start + len))
|
|
||||||
{
|
{
|
||||||
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
let annotRange = this.getAnnotationRangeObj(paraId, rangeId);
|
||||||
Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]);
|
Asc.Editor.callMethod("RemoveAnnotationRange", [annotRange]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
AssistantReplace.prototype._isWordBoundary = function(char)
|
|
||||||
{
|
|
||||||
return /[\s.,!?;:'"()\[\]{}\-–—\/\\]/.test(char);
|
|
||||||
};
|
|
||||||
|
|||||||
@ -7,20 +7,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} ReplaceHintAiResponse
|
* @typedef {Object} HintAiResponse
|
||||||
* @property {string} origin
|
* @property {string} origin
|
||||||
* @property {string} suggestion
|
|
||||||
* @property {string} description
|
|
||||||
* @property {string} difference
|
|
||||||
* @property {number} paragraph
|
|
||||||
* @property {number} occurrence
|
|
||||||
* @property {number} confidence
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} ReplaceAiResponse
|
|
||||||
* @property {string} wrong
|
|
||||||
* @property {string} correct
|
|
||||||
* @property {string} reason
|
* @property {string} reason
|
||||||
* @property {number} paragraph
|
* @property {number} paragraph
|
||||||
* @property {number} occurrence
|
* @property {number} occurrence
|
||||||
@ -28,8 +16,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} HintAiResponse
|
* @typedef {Object} HintInfoForPopup
|
||||||
|
* @property {string} original
|
||||||
|
* @property {string} explanation
|
||||||
|
* @property {number} type
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ReplaceAiResponse
|
||||||
* @property {string} origin
|
* @property {string} origin
|
||||||
|
* @property {string} suggestion
|
||||||
* @property {string} reason
|
* @property {string} reason
|
||||||
* @property {number} paragraph
|
* @property {number} paragraph
|
||||||
* @property {number} occurrence
|
* @property {number} occurrence
|
||||||
@ -44,10 +40,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} HintInfoForPopup
|
* @typedef {Object} ReplaceHintAiResponse
|
||||||
* @property {string} original
|
* @property {string} origin
|
||||||
|
* @property {string} suggestion
|
||||||
* @property {string} reason
|
* @property {string} reason
|
||||||
* @property {number} type
|
* @property {string} difference
|
||||||
|
* @property {number} paragraph
|
||||||
|
* @property {number} occurrence
|
||||||
|
* @property {number} confidence
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user