Implement minimal translation system prompt following Russell's approach

This commit is contained in:
Russell Ballestrini 2025-07-01 15:32:12 -04:00
parent 12662d67c9
commit bd09659d3b
2 changed files with 122 additions and 16 deletions

View file

@ -1,5 +1,7 @@
// Translation functionality with code block preservation
import { sendMessage } from './chat.js';
import { API_KEY } from './config.js';
import { getSelectedModel, getSelectedModelEndpoint } from './models.js';
// Languages supported by Hermes 3 model (from Russell's implementation)
export const SUPPORTED_LANGUAGES = {
@ -24,12 +26,87 @@ export const SUPPORTED_LANGUAGES = {
'ko': 'Korean'
};
// Send message with custom history (for translation with minimal system prompt)
async function* sendMessageWithHistory(messageHistory) {
const apiUrl = `${getSelectedModelEndpoint()}/chat/completions`;
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
model: getSelectedModel(),
messages: messageHistory,
temperature: 0.3, // Lower temperature for more consistent translation
max_tokens: 8192,
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
for (let i = 0; i < lines.length - 1; i++) {
const line = lines[i].trim();
if (line.startsWith('data: ')) {
const jsonData = line.slice(6);
if (jsonData === '[DONE]') continue;
try {
const parsedData = JSON.parse(jsonData);
const content = parsedData.choices[0].delta.content;
if (content) {
yield content;
}
} catch (error) {
console.error('Error parsing JSON:', error);
}
}
}
buffer = lines[lines.length - 1];
}
}
// Preserve code blocks, URLs, and special formatting during translation
export function preserveSpecialContent(text) {
const preservations = [];
let preservedText = text;
// Code blocks - triple backticks (most important to preserve completely)
// HTML code elements (most important in HTML context)
preservedText = preservedText.replace(/<pre[\s\S]*?<\/pre>/gi, (match) => {
const index = preservations.length;
const placeholder = `__CODE_${index}__`;
preservations.push({ type: 'CODE', content: match, placeholder });
return placeholder;
});
preservedText = preservedText.replace(/<code[\s\S]*?<\/code>/gi, (match) => {
const index = preservations.length;
const placeholder = `__CODE_${index}__`;
preservations.push({ type: 'CODE', content: match, placeholder });
return placeholder;
});
// Other code-related HTML elements
preservedText = preservedText.replace(/<(kbd|samp|var)[\s\S]*?<\/\1>/gi, (match) => {
const index = preservations.length;
const placeholder = `__CODE_${index}__`;
preservations.push({ type: 'CODE', content: match, placeholder });
return placeholder;
});
// Markdown code blocks (for mixed content)
preservedText = preservedText.replace(/```[\s\S]*?```/g, (match) => {
const index = preservations.length;
const placeholder = `__CODE_${index}__`;
@ -37,7 +114,7 @@ export function preserveSpecialContent(text) {
return placeholder;
});
// Inline code
// Inline markdown code
preservedText = preservedText.replace(/`[^`\n]+`/g, (match) => {
const index = preservations.length;
const placeholder = `__CODE_${index}__`;
@ -61,7 +138,7 @@ export function preserveSpecialContent(text) {
return placeholder;
});
// HTML tags
// Other HTML tags (after code elements are preserved)
preservedText = preservedText.replace(/<[^>]+>/g, (match) => {
const index = preservations.length;
const placeholder = `__HTML_${index}__`;
@ -76,39 +153,67 @@ export function preserveSpecialContent(text) {
export function restoreSpecialContent(translatedText, preservations) {
let restoredText = translatedText;
preservations.forEach((item) => {
// Use the exact placeholder that was created for this item
restoredText = restoredText.replace(new RegExp(item.placeholder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), item.content);
});
// Simple replacement like Russell's approach
for (const item of preservations) {
restoredText = restoredText.replace(new RegExp(item.placeholder, 'g'), item.content);
}
return restoredText;
}
// Translate text using Hermes AI
// Translate text using Hermes AI with minimal system context
export async function translateText(text, targetLanguage) {
const { preservedText, preservations } = preserveSpecialContent(text);
const prompt = `Translate the following text to ${SUPPORTED_LANGUAGES[targetLanguage]}.
// Debug logging
console.log('=== TRANSLATION DEBUG ===');
console.log('Original text:', text.substring(0, 300) + (text.length > 300 ? '...' : ''));
console.log('Preserved text:', preservedText.substring(0, 300) + (preservedText.length > 300 ? '...' : ''));
console.log('Preservations found:', preservations.length);
preservations.forEach((item, index) => {
console.log(` ${index}: ${item.placeholder} -> ${item.content.substring(0, 50)}${item.content.length > 50 ? '...' : ''}`);
});
const prompt = `Translate the following HTML content into ${SUPPORTED_LANGUAGES[targetLanguage]}. The input is in English and formatted as HTML, which includes formatting, code blocks, and technical content. Ensure the output preserves the structure, syntax, and formatting of the original. Do not translate or modify placeholders like __CODE_0__, __URI_0__, __HTML_0__, etc., as they represent code blocks, URLs, or HTML elements that should remain unchanged. Only translate the surrounding text. Here is the text to translate:
CRITICAL: Do NOT translate or modify ANY placeholders that look like __CODE_X__, __URI_X__, or __HTML_X__ (where X is a number). These represent code blocks, URLs, and HTML tags that must remain EXACTLY as written.
${preservedText}`;
Text to translate:
${preservedText}
Translate only the regular text, keeping all placeholders unchanged. Provide only the translation without explanations.`;
// Use minimal system message for translation - focused purely on the task
const translationHistory = [
{
role: "system",
content: "You are a translation assistant. Translate text accurately while preserving all formatting and placeholders exactly as provided."
},
{
role: "user",
content: prompt
}
];
let translatedText = '';
try {
for await (const chunk of sendMessage(prompt)) {
for await (const chunk of sendMessageWithHistory(translationHistory)) {
translatedText += chunk;
}
// Clean up the response (remove any extra explanations)
translatedText = translatedText.trim();
console.log('AI response length:', translatedText.length);
console.log('AI response:', translatedText.substring(0, 300) + (translatedText.length > 300 ? '...' : ''));
// Check if placeholders are still in the response
preservations.forEach((item, index) => {
const found = translatedText.includes(item.placeholder);
console.log(` Placeholder ${item.placeholder} found in response: ${found}`);
});
// Restore preserved content
return restoreSpecialContent(translatedText, preservations);
const restored = restoreSpecialContent(translatedText, preservations);
console.log('Restored text length:', restored.length);
console.log('Restored text:', restored.substring(0, 300) + (restored.length > 300 ? '...' : ''));
return restored;
} catch (error) {
console.error('Translation error:', error);
throw new Error('Translation failed. Please try again.');

View file

@ -106,6 +106,7 @@ window.sendMessage = Chat.sendMessage;
window.speakText = TTS.speakText;
window.uploadFile = FileUpload.uploadFile;
window.openTTSModal = UI.openTTSModal;
window.openTranslateModal = UI.openTranslateModal;
window.toggleHermesModal = async () => await UI.toggleHermesModal();
window.extractWebpageContent = Content.extractWebpageContent;
window.getSelectedModel = Models.getSelectedModel;