From 748be263405f501e465fb5bd331ca06aaddf6104 Mon Sep 17 00:00:00 2001 From: "dylan.wang" Date: Thu, 3 Sep 2026 08:25:54 +0800 Subject: [PATCH 1/2] Prompt audit: drop pressure language from LLM prompts, fix stale notes - background.js / messages.js: reword translation, batch, fast-batch and math-placeholder prompts to plain instructions; every functional rule (delimiter contract, segment count, placeholder/tag preservation, return-unchanged) is kept, only the capitalised emphasis goes. - ocr.js: same for the OCR system prompt. - api-compat.js / api-compat.test.mjs: claude-opus-4-1 retired on 2026-08-05, say so in the past tense. - CLAUDE.md: request format no longer lists temperature unconditionally; api-compat.js decides per model. Co-Authored-By: Claude Fable 5.1 --- CLAUDE.md | 4 +++- background/background.js | 36 +++++++++++++++++------------------ i18n/messages.js | 12 ++++++------ shared/api-compat.js | 2 +- shared/ocr.js | 4 ++-- test/unit/api-compat.test.mjs | 2 +- 6 files changed, 31 insertions(+), 29 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7e0831e..a1a2228 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -215,7 +215,9 @@ Two things it deliberately does not own: Works with any OpenAI Chat Completions-compatible API, plus Anthropic's native Messages API: - OpenAI, Anthropic, Google Gemini, DeepSeek, OpenRouter, Ollama, LM Studio -- Request format: `{model, messages, temperature, max_tokens}` +- Request format: `{model, messages, max_tokens}` plus whatever per-model + parameters `shared/api-compat.js` decides (see the rules below — + `temperature` is only sent where the model still honours it) - Response: `{choices[0].message.content}`, or `{content[0].text}` for Anthropic **All of it lives in `shared/api-compat.js`** — the provider catalog, every diff --git a/background/background.js b/background/background.js index aaeac8e..a1aea9f 100644 --- a/background/background.js +++ b/background/background.js @@ -87,7 +87,7 @@ const languageNames = { // Math placeholder rule - always appended to prompts (cannot be overridden by custom prompts) const MATH_PLACEHOLDER_RULE = ` -IMPORTANT: Keep placeholders like {{1}}, {{2}} etc. exactly as they are - do not translate, modify, or add line breaks around them.`; +Placeholders such as {{1}}, {{2}} stand for formulas the page renders itself. Keep each one exactly as written, in place, with no line breaks added around it.`; // Single word/phrase prompt template (no math placeholder rule) const SINGLE_WORD_PROMPT = `You are a bilingual dictionary. Translate the given word or short phrase to {targetLang}. @@ -102,27 +102,27 @@ Return JSON only with keys "translation" and "phonetic". // Default prompt template const DEFAULT_PROMPT = `You are a professional translator. Translate the given text to {targetLang}. Rules: -1. Provide ONLY the translation, no explanations or notes +1. Reply with the translation only, no explanations or notes 2. Maintain the original formatting (line breaks, punctuation) 3. Keep technical terms, brand names, and proper nouns in their original form when appropriate -4. If the text is already in the target language, return it EXACTLY as is (no paraphrasing or reordering) +4. If the text is already in the target language, return it unchanged (no paraphrasing or reordering) 5. Translate naturally, not literally`; // Default batch prompt template const DEFAULT_BATCH_PROMPT = `You are a professional translator. Translate the given numbered texts to {targetLang}. Rules: -1. Return translations in the SAME numbered format: [1] translation1 [2] translation2 etc. +1. Return translations in the same numbered format: [1] translation1 [2] translation2 etc. 2. Keep the numbering system exactly as given 3. Maintain original formatting within each translation 4. Keep technical terms, brand names, and proper nouns in their original form when appropriate -5. If a text is already in the target language, return it EXACTLY as is (no paraphrasing or reordering) +5. If a text is already in the target language, return it unchanged (no paraphrasing or reordering) 6. Translate naturally, not literally`; // Batch output rules appended when using custom prompts const BATCH_OUTPUT_RULES = `BATCH FORMAT RULES: -1. Return translations in the SAME numbered format: [1] translation1 [2] translation2 etc. +1. Return translations in the same numbered format: [1] translation1 [2] translation2 etc. 2. Keep the numbering system exactly as given -3. Output ONLY the translations, nothing else`; +3. Output the translations and nothing else`; // Get browser language and map to supported language function getBrowserLanguage() { @@ -1818,16 +1818,16 @@ async function translateBatchWithAI(texts, targetLang, settings) { // Fast batch prompt template const FAST_BATCH_PROMPT = `You are a professional translator. Translate multiple text segments to {targetLang}. -CRITICAL RULES: +The segments are parsed by a program, so the output format is a contract: 1. Input segments are separated by "{delimiter}" -2. Output translations MUST be separated by "{delimiter}" in the EXACT same order -3. Output ONLY the translations, nothing else +2. Output translations separated by "{delimiter}", in the same order +3. Output the translations and nothing else 4. Keep technical terms, brand names, proper nouns in original form -5. If already in target language, return EXACTLY as is (no paraphrasing or reordering) -6. MUST have exactly the same number of output segments as input -7. Preserve placeholders and inline tags EXACTLY: keep {{1}}-style placeholders unchanged, and keep paired tags like ... or ... with the same names and numbers, wrapping the translated text they originally wrapped. Never invent, drop, or renumber tags. +5. If a segment is already in the target language, return it unchanged (no paraphrasing or reordering) +6. The number of output segments equals the number of input segments; an empty segment stays empty +7. Preserve placeholders and inline tags: keep {{1}}-style placeholders unchanged, and keep paired tags like ... or ... with the same names and numbers, wrapping the translated text they originally wrapped. Do not invent, drop, or renumber tags. -Example: +Example (illustrative; here the target language happens to be Chinese): Input: Hello{delimiter}Read the docs first{delimiter}Thank you Output: 你好{delimiter}请先阅读文档{delimiter}谢谢`; @@ -1835,10 +1835,10 @@ Output: 你好{delimiter}请先阅读文档{delimiter}谢谢`; function getFastBatchOutputRules(delimiter) { return `BATCH FORMAT RULES: 1. Input segments are separated by "${delimiter}" -2. Output translations MUST be separated by "${delimiter}" in the EXACT same order -3. Output ONLY the translations, nothing else -4. MUST have exactly the same number of output segments as input -5. Preserve placeholders and inline tags EXACTLY: keep {{1}}-style placeholders unchanged, and keep paired tags like ... with the same names and numbers, wrapping the translated text they originally wrapped. Never invent, drop, or renumber tags.`; +2. Output translations separated by "${delimiter}", in the same order +3. Output the translations and nothing else +4. The number of output segments equals the number of input segments; an empty segment stays empty +5. Preserve placeholders and inline tags: keep {{1}}-style placeholders unchanged, and keep paired tags like ... with the same names and numbers, wrapping the translated text they originally wrapped. Do not invent, drop, or renumber tags.`; } // Fast batch translation with delimiter diff --git a/i18n/messages.js b/i18n/messages.js index 8d63947..8ee8f98 100644 --- a/i18n/messages.js +++ b/i18n/messages.js @@ -311,25 +311,25 @@ const I18N_MESSAGES = { presetApplied: 'Preset applied', promptStandard: `You are a professional translator. Translate the given text to {targetLang}. Rules: -1. Provide ONLY the translation, no explanations or notes +1. Reply with the translation only, no explanations or notes 2. Maintain the original formatting (line breaks, punctuation) 3. Keep technical terms, brand names, and proper nouns in their original form when appropriate -4. If the text is already in the target language, return it EXACTLY as is (no paraphrasing or reordering) +4. If the text is already in the target language, return it unchanged (no paraphrasing or reordering) 5. Translate naturally, not literally`, promptLiteral: `You are a precise translator. Translate the given text to {targetLang}. Rules: -1. Provide ONLY the translation, no explanations +1. Reply with the translation only, no explanations 2. Translate as literally as possible while maintaining grammatical correctness 3. Preserve the original sentence structure when possible 4. Keep all technical terms, names, and proper nouns unchanged -5. If the text is already in the target language, return it EXACTLY as is (no paraphrasing or reordering)`, +5. If the text is already in the target language, return it unchanged (no paraphrasing or reordering)`, promptCreative: `You are a creative translator and language artist. Translate the given text to {targetLang}. Rules: -1. Provide ONLY the translation, no explanations +1. Reply with the translation only, no explanations 2. Prioritize natural, fluent expression in the target language 3. Adapt idioms and cultural references to equivalent expressions in the target culture 4. Maintain the tone and emotion of the original text -5. If the text is already in the target language, return it EXACTLY as is (no paraphrasing or reordering)`, +5. If the text is already in the target language, return it unchanged (no paraphrasing or reordering)`, // Popup translateCurrentPage: 'Translate Page', diff --git a/shared/api-compat.js b/shared/api-compat.js index 5408a4c..08b6475 100644 --- a/shared/api-compat.js +++ b/shared/api-compat.js @@ -276,7 +276,7 @@ name: 'Anthropic Claude', endpoint: 'https://api.anthropic.com/v1/messages', // Native Anthropic API accepts version aliases (no date suffix); aliases - // avoid stale/incorrect dates. claude-opus-4-1 is omitted: it retires + // avoid stale/incorrect dates. claude-opus-4-1 is omitted: it retired on // 2026-08-05. models: ['claude-opus-5', 'claude-sonnet-5', 'claude-fable-5', 'claude-haiku-4-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-sonnet-4-6', 'claude-opus-4-5', 'claude-sonnet-4-5'], defaultModel: 'claude-sonnet-5' diff --git a/shared/ocr.js b/shared/ocr.js index 5f8a406..0f99adc 100644 --- a/shared/ocr.js +++ b/shared/ocr.js @@ -576,10 +576,10 @@ // translation is step 2, and it does not happen here (see the file header). const OCR_SYSTEM_PROMPT = `You are an OCR engine. The user sends one image. Do both of the following: -1. Extract ALL human-readable text from the image, preserving reading order and line breaks. +1. Extract all human-readable text from the image, preserving reading order and line breaks. 2. Detect the language the extracted text is written in. -Return ONLY a JSON object with exactly these keys: +Return a JSON object, and nothing else, with exactly these keys: {"text": "", "language": ""} Rules: diff --git a/test/unit/api-compat.test.mjs b/test/unit/api-compat.test.mjs index 83e563a..cc73152 100644 --- a/test/unit/api-compat.test.mjs +++ b/test/unit/api-compat.test.mjs @@ -136,7 +136,7 @@ test('catalog defaults point at a model in their own list', () => { test('catalog lists no retired models', () => { // Append here as vendors retire ids, so they cannot be reintroduced. const retired = [ - 'claude-opus-4-1', // retires 2026-08-05 + 'claude-opus-4-1', // retired 2026-08-05 'anthropic/claude-opus-4.1', 'gemini-2.0-flash', // shut down 2026-06-01 'gemini-2.0-flash-lite', From 7b02a327aa031c263d2052683c413f0df7f006f2 Mon Sep 17 00:00:00 2001 From: "dylan.wang" Date: Thu, 3 Sep 2026 08:28:52 +0800 Subject: [PATCH 2/2] prompt: shorter example caption Co-Authored-By: Claude Fable 5.1 --- background/background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/background/background.js b/background/background.js index a1aea9f..c505bf7 100644 --- a/background/background.js +++ b/background/background.js @@ -1827,7 +1827,7 @@ The segments are parsed by a program, so the output format is a contract: 6. The number of output segments equals the number of input segments; an empty segment stays empty 7. Preserve placeholders and inline tags: keep {{1}}-style placeholders unchanged, and keep paired tags like ... or ... with the same names and numbers, wrapping the translated text they originally wrapped. Do not invent, drop, or renumber tags. -Example (illustrative; here the target language happens to be Chinese): +Example (target language shown as Chinese): Input: Hello{delimiter}Read the docs first{delimiter}Thank you Output: 你好{delimiter}请先阅读文档{delimiter}谢谢`;