Unification of assistants

This commit is contained in:
Artur
2026-01-20 13:15:34 +03:00
parent b992ebfa43
commit 79affa543c
5 changed files with 166 additions and 181 deletions

View File

@ -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>";

View File

@ -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);

View File

@ -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);

View File

@ -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);
};

View File

@ -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
*/ */
/** /**