@alnliang/tlv
Version:
Lightweight AI-powered tool for verifying and correcting translations in JSON language files using any OpenAI-compatible API
565 lines (521 loc) • 31.7 kB
JavaScript
import OpenAI from "openai";
const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai";
const DEFAULT_MODEL = "gemini-2.5-flash-preview-05-20";
const DEFAULT_SYSTEM_MESSAGE = `
You are an expert translator and JSON validator, tasked with comparing an English JSON language file against a single translated JSON language file. Your goal is to identify discrepancies AT ALL LEVELS (including within nested objects) and provide corrected translations, generating translations for missing structures where necessary.
You will be given:
1. An English JSON object (\`English JSON\`).
2. A single translated JSON object for a specific language (\`Translated JSON\`).
3. Optional \`Application Context\` providing details about the software these translations are for.
Perform the following analysis step-by-step, processing both top-level keys and keys within nested objects:
**Step 1: Top-Level Key Comparison**
* Compare the top-level keys (\`stringID\`s) present in the English JSON and the Translated JSON.
* Identify keys present in English but missing in the Translation. Note if the English value is a string or an object.
* Identify keys present only in the Translation (these are extra).
**Step 2: Process Matching Keys**
* For each top-level \`stringID\` present in BOTH English and Translated JSON:
* **If the value is a STRING in both:** Proceed to Step 3 (Placeholder Check) and Step 4 (Accuracy Check) for this string value.
* **If the value is an OBJECT in both:** Proceed to Step 5 (Nested Object Comparison).
* **If value types mismatch (e.g., string in English, object in Translation):** Report this as a type mismatch error. For the \`fixed-json\`, prioritize the structure from the English JSON and attempt to translate its content if it was a string, or recursively process/translate if it was an object.
**Step 3: Placeholder Verification (for STRING values)**
* For string values being compared (either top-level or nested), verify that **every** placeholder (\`{{variableName}}\`) in the English string exists **identically** in the translated string.
* Ensure **no placeholders are missing** and **no extra placeholders** are introduced. Report errors if discrepancies are found.
**Step 4: Translation Accuracy Assessment (for STRING values)**
* For string values being compared (with correct placeholders), assess if the translation accurately conveys the **meaning and intent** of the English original, considering context. Identify and report errors like incorrect word choice, grammar, tone, etc.
**Step 5: Nested Object Comparison (When Object Exists in Both)**
* For a \`stringID\` where both English and Translated values are objects:
* Recursively apply comparison logic to the keys (\`subKey\`s) *within* these objects.
* For each \`subKey\` in the English nested object:
* Check if the \`subKey\` exists in the Translated nested object.
* If the \`subKey\` exists and its value is a string in both: Apply Step 3 (Placeholders) and Step 4 (Accuracy) to these nested strings. Report any errors found.
* If the \`subKey\` exists and its value is another nested object: Recurse further by applying Step 5 again.
* If the \`subKey\` is missing in the Translated nested object: Report this missing sub-key error. Attempt to generate a translation suggestion for the English value associated with this \`subKey\`.
* Handle type mismatches at the sub-key level similar to Step 2. Report errors.
**Step 6: Error Reporting**
* For **every** discrepancy found at any level (top-level or nested), create an error entry:
* \`stringID\`: The full path to the key where the error occurred (e.g., \`"topLevelKey"\` or \`"objectKey.subKey1"\`). Use dot notation for nested keys.
* \`error\`: Clear description (e.g., "Missing key", "Missing placeholder", "Incorrect translation", "Type mismatch", "Missing sub-key within object").
* **IMPORTANT**: If the error is an incorrect translation, please include a brief explanation detailing why the translation is incorrect, explaining what the current translation means and how the suggestion fixes it.
* \`current\`: The current translated value (string, object, or \`null\` if missing).
* \`suggestion\`: Your suggested correction (string or potentially a translated object structure if fixing type mismatch).
* For missing keys/sub-keys (where English value was a string): Provide a best-effort translation suggestion string. If impossible, use an empty string "".
* For placeholder/accuracy errors: Provide the corrected string suggestion.
* **If an entire top-level object key was missing in the translation (identified in Step 1):** Create ONE error entry for the top-level object key (e.g., \`stringID: "timezones"\`) with an error like "Missing entire object structure" and \`current: null\`. The \`suggestion\` field for this specific error should be \`null\`, as the full suggested object will be built directly in the \`fixed-json\` in the next step. **Do NOT create individual error entries for every sub-key inside the missing object.**
**Step 7: Final JSON Output Assembly (\`fixed-json\`)**
* Compile all error entries from Step 6 into the \`"errors"\` array (must be \`[]\` if no errors).
* Construct the \`"fixed-json"\` object, mirroring the structure of the **English JSON**:
* Iterate through each top-level \`stringID\` in the English JSON.
* **If the English value is a STRING:**
* If no errors were reported for this \`stringID\`, use the original translated string found in the input \`Translated JSON\` (if the key existed there). If the key was missing in the translation, use the generated \`suggestion\` from its error report.
* If errors *were* reported (placeholder, accuracy), use the \`suggestion\` from the corresponding error entry.
* **If the English value is an OBJECT:**
* **CASE A: The object key was MISSING entirely in the Translation:**
* Create a new object for this \`stringID\` in \`fixed-json\`.
* Iterate through *all* keys (\`subKey\`s) and values in the *English* object.
* For each \`subKey\`:
* If the English value is a string, **generate a translation suggestion** for that English string and add the \`subKey\`: \`suggestion\` pair to the new object in \`fixed-json\`.
* If the English value is a nested object, recursively apply this translation generation logic to create the translated nested structure within \`fixed-json\`.
* **CASE B: The object key EXISTED in the Translation:**
* Create an object for this \`stringID\` in \`fixed-json\`.
* Iterate through the keys (\`subKey\`s) present in the *English* object structure.
* For each \`subKey\`:
* Determine the correct value: Check if an error was reported for the path \`stringID.subKey\`.
* If an error *was* reported for this specific \`subKey\`, use the \`suggestion\` from that error entry as the value in \`fixed-json\`.
* If no error was reported for \`stringID.subKey\`, use the original translated value for that \`subKey\` found in the input \`Translated JSON\`.
* If the value is itself a nested object, recursively apply this logic (check sub-sub-keys for errors, use suggestions or original values).
* Exclude any top-level keys present only in the original Translation input.
--- START EXAMPLES ---
**Example 1 (Missing Top-Level Object):**
English JSON: \`{ "greeting": "Hello {{name}}", "timezones": { "zone1": "Central Time", "zone2": "Eastern Time" } }\`
Translated JSON (German): \`{ "greeting": "Hallo {{name}}" }\`
{
"errors": [
{
"stringID": "timezones",
"error": "Missing entire object structure",
"current": null,
"suggestion": null
}
],
"fixed-json": {
"greeting": "Hallo {{name}}",
"timezones": {
"zone1": "Zentralzeit",
"zone2": "Östliche Zeit"
}
}
}
**Example 2 (Placeholder Error within Nested Object):**
English JSON: \`{ "options": { "label": "Name", "tooltip": "Enter your full {{name}}" } }\`
Translated JSON (Spanish): \`{ "options": { "label": "Nombre", "tooltip": "Ingrese su nombre completo" } }\`
{
"errors": [
{
"stringID": "options.tooltip",
"error": "Missing placeholder {{name}}",
"current": "Ingrese su nombre completo",
"suggestion": "Ingrese su {{name}} completo"
}
],
"fixed-json": {
"options": {
"label": "Nombre",
"tooltip": "Ingrese su {{name}} completo"
}
}
}
**Example 3 (Missing Top-Level String Key):**
English JSON: \`{ "common": { "ok": "OK", "cancel": "Cancel" }, "actions": { "save": "Save", "delete": "Delete" } }\`
Translated JSON (French): \`{ "common": { "ok": "OK", "cancel": "Annuler" }, "actions": { "save": "Enregistrer" } }\`
{
"errors": [
{
"stringID": "actions.delete",
"error": "Missing sub-key within object",
"current": null,
"suggestion": "Supprimer"
}
],
"fixed-json": {
"common": {
"ok": "OK",
"cancel": "Annuler"
},
"actions": {
"save": "Enregistrer",
"delete": "Supprimer"
}
}
}
**Example 4 (Incorrect Translation - Top Level & Extra Key):**
English JSON: \`{ "status": "Pending", "priority": "High" }\`
Translated JSON (German): \`{ "status": "Ausstehend", "priority": "Hoch", "extraKeyNotInEnglish": "Zusätzlicher Wert" }\`
{
"errors": [
{
"stringID": "priority",
"error": "Incorrect translation: While 'Hoch' means 'High', 'Hohe' is more idiomatic when referring to priority level in German. The suggestion 'Hohe' improves naturalness.",
"current": "Hoch",
"suggestion": "Hohe"
}
],
"fixed-json": {
"status": "Ausstehend",
"priority": "Hohe"
}
}
**Example 5 (Type Mismatch):**
English JSON: \`{ "menu": { "file": "File", "edit": "Edit" } }\`
Translated JSON (Italian): \`{ "menu": "Menu principale" }\`
{
"errors": [
{
"stringID": "menu",
"error": "Type mismatch: Expected object structure but found string",
"current": "Menu principale",
"suggestion": null
}
],
"fixed-json": {
"menu": {
"file": "File",
"edit": "Modifica"
}
}
}
**Example 6 (All Correct - No Errors):**
English JSON: \`{ "welcome": "Welcome {{user}}", "goodbye": "Goodbye" }\`
Translated JSON (Spanish): \`{ "welcome": "Bienvenido {{user}}", "goodbye": "Adiós" }\`
{
"errors": [],
"fixed-json": {
"welcome": "Bienvenido {{user}}",
"goodbye": "Adiós"
}
}
--- END EXAMPLES ---
Important formatting notes:
- Only return the JSON result - no extra text.
- Ensure the JSON is valid and parseable.
- Use double quotes for all JSON strings.
- The "fixed-json" should always follow the English JSON structure exactly, not the Translated JSON structure.
- Exclude any keys from the Translated JSON that are not present in the English JSON.
`;
export class TranslationVerifier {
constructor(options) {
if (!options.apiKey) {
throw new Error("API Key is required.");
}
this.model = options.model || DEFAULT_MODEL;
this.systemMessage = options.systemMessage || DEFAULT_SYSTEM_MESSAGE;
this.userContext = options.userContext || "";
this.ragProvider = options.ragProvider;
this.ragOptions = options.ragOptions;
this.openai = new OpenAI({
apiKey: options.apiKey,
baseURL: options.baseURL || DEFAULT_BASE_URL,
defaultHeaders: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${options.apiKey}`
}
});
console.log(`TranslationVerifier initialized with baseURL: ${this.openai.baseURL}`);
if (this.ragProvider) {
console.log('RAG provider enabled for enhanced translation verification');
}
}
async enhanceWithRAGContext(context) {
if (!this.ragProvider || !this.ragOptions?.enableTranslationMemory) {
return '';
}
try {
const searchQueries = [
context.sourceText,
`${context.sourceText} ${context.domain || ''}`,
...this.extractKeyTerms(context.sourceText)
];
const ragResults = await Promise.all(searchQueries.map(query => this.ragProvider.search(query, {
maxResults: 3,
scoreThreshold: 0.7,
languagePair: {
source: context.sourceLanguage,
target: context.targetLanguage
},
filters: context.domain ? { domain: context.domain } : undefined
})));
const relevantExamples = ragResults
.flat()
.filter(result => result.score > 0.7)
.slice(0, 5);
if (relevantExamples.length === 0) {
return '';
}
let ragContext = '\n\nRelevant Translation Examples from Memory:\n';
relevantExamples.forEach((example, index) => {
ragContext += `Example ${index + 1}: ${example.document.content}\n`;
ragContext += `Context: ${example.document.metadata.context || 'N/A'}\n`;
ragContext += `Confidence: ${(example.score * 100).toFixed(1)}%\n\n`;
});
return ragContext;
}
catch (error) {
console.warn('RAG enhancement failed:', error);
return '';
}
}
extractKeyTerms(text) {
const words = text.toLowerCase()
.replace(/[^\w\s]/g, ' ')
.split(/\s+/)
.filter(word => word.length > 3);
return [...new Set(words)];
}
// Auto-detects variable patterns in JSON content
detectVariablePattern(jsonContent) {
const allText = this.extractAllStrings(jsonContent).join(' ');
const patterns = [
{ name: '{{variable}}', regex: /\{\{[^{}]+\}\}/g, testRegex: /\{\{[^{}]+\}\}/ },
{ name: '${variable}', regex: /\$\{[^{}]+\}/g, testRegex: /\$\{[^{}]+\}/ },
{ name: '{variable}', regex: /(?<!\{)\{[^{}\$]+\}(?!\})/g, testRegex: /(?<!\{)\{[^{}\$]+\}(?!\})/ },
{ name: '%variable%', regex: /%[^%]+%/g, testRegex: /%[^%]+%/ },
{ name: '$variable$', regex: /\$[^${}]+\$/g, testRegex: /\$[^${}]+\$/ },
{ name: '[variable]', regex: /\[[^\[\]]+\]/g, testRegex: /\[[^\[\]]+\]/ },
];
let workingText = allText;
const patternCounts = patterns.map(p => {
const matches = workingText.match(p.regex) || [];
workingText = workingText.replace(p.regex, '');
return {
...p,
count: matches.length,
examples: matches.slice(0, 3)
};
});
const detectedPattern = patternCounts.reduce((max, current) => current.count > max.count ? current : max);
if (detectedPattern.count === 0) {
return {
pattern: '{{variable}}',
regex: '\\{\\{[^{}]+\\}\\}',
examples: ['{{example}}']
};
}
const regexString = detectedPattern.regex.source;
console.log(`Auto-detected variable pattern: ${detectedPattern.name} (${detectedPattern.count} occurrences)`);
console.log(`Examples found: ${detectedPattern.examples.join(', ')}`);
return {
pattern: detectedPattern.name,
regex: regexString,
examples: detectedPattern.examples
};
}
// Recursively extracts all string values from a JSON object
extractAllStrings(obj) {
const strings = [];
if (typeof obj === 'string') {
strings.push(obj);
}
else if (typeof obj === 'object' && obj !== null) {
for (const value of Object.values(obj)) {
strings.push(...this.extractAllStrings(value));
}
}
return strings;
}
// Generates dynamic system message based on detected variable pattern
generateSystemMessage(variableInfo) {
const exampleVar = variableInfo.examples[0] || variableInfo.pattern.replace('variable', 'name');
return `
You are an expert translator and JSON validator, tasked with comparing an English JSON language file against a single translated JSON language file. Your goal is to identify discrepancies AT ALL LEVELS (including within nested objects) and provide corrected translations, generating translations for missing structures where necessary.
**IMPORTANT: Variable Pattern Auto-Detection**
The system has detected that this JSON uses the "${variableInfo.pattern}" pattern for variables.
Examples found: ${variableInfo.examples.join(', ') || exampleVar}
You must preserve this exact pattern in all translations and validations.
You will be given:
1. An English JSON object (\`English JSON\`).
2. A single translated JSON object for a specific language (\`Translated JSON\`).
3. Optional \`Application Context\` providing details about the software these translations are for.
Perform the following analysis step-by-step, processing both top-level keys and keys within nested objects:
**Step 1: Top-Level Key Comparison**
* Compare the top-level keys (\`stringID\`s) present in the English JSON and the Translated JSON.
* Identify keys present in English but missing in the Translation. Note if the English value is a string or an object.
* Identify keys present only in the Translation (these are extra).
**Step 2: Process Matching Keys**
* For each top-level \`stringID\` present in BOTH English and Translated JSON:
* **If the value is a STRING in both:** Proceed to Step 3 (Placeholder Check) and Step 4 (Accuracy Check) for this string value.
* **If the value is an OBJECT in both:** Proceed to Step 5 (Nested Object Comparison).
* **If value types mismatch (e.g., string in English, object in Translation):** Report this as a type mismatch error. For the \`fixed-json\`, prioritize the structure from the English JSON and attempt to translate its content if it was a string, or recursively process/translate if it was an object.
**Step 3: Placeholder Verification (for STRING values)**
* For string values being compared (either top-level or nested), verify that **every** placeholder using the "${variableInfo.pattern}" pattern in the English string exists **identically** in the translated string.
* The regex pattern to match variables is: /${variableInfo.regex}/g
* Ensure **no placeholders are missing** and **no extra placeholders** are introduced. Report errors if discrepancies are found.
* Variables must maintain the exact same pattern format: ${exampleVar}
**Step 4: Translation Accuracy Assessment (for STRING values)**
* For string values being compared (with correct placeholders), assess if the translation accurately conveys the **meaning and intent** of the English original, considering context. Identify and report errors like incorrect word choice, grammar, tone, etc.
**Step 5: Nested Object Comparison (When Object Exists in Both)**
* For a \`stringID\` where both English and Translated values are objects:
* Recursively apply comparison logic to the keys (\`subKey\`s) *within* these objects.
* For each \`subKey\` in the English nested object:
* Check if the \`subKey\` exists in the Translated nested object.
* If the \`subKey\` exists and its value is a string in both: Apply Step 3 (Placeholders) and Step 4 (Accuracy) to these nested strings. Report any errors found.
* If the \`subKey\` exists and its value is another nested object: Recurse further by applying Step 5 again.
* If the \`subKey\` is missing in the Translated nested object: Report this missing sub-key error. Attempt to generate a translation suggestion for the English value associated with this \`subKey\`.
* Handle type mismatches at the sub-key level similar to Step 2. Report errors.
**Step 6: Error Reporting**
* For **every** discrepancy found at any level (top-level or nested), create an error entry:
* \`stringID\`: The full path to the key where the error occurred (e.g., \`"topLevelKey"\` or \`"objectKey.subKey1"\`). Use dot notation for nested keys.
* \`error\`: Clear description (e.g., "Missing key", "Missing placeholder", "Incorrect translation", "Type mismatch", "Missing sub-key within object").
* **IMPORTANT**: If the error is an incorrect translation, please include a brief explanation detailing why the translation is incorrect, explaining what the current translation means and how the suggestion fixes it.
* \`current\`: The current translated value (string, object, or \`null\` if missing).
* \`suggestion\`: Your suggested correction (string or potentially a translated object structure if fixing type mismatch).
* For missing keys/sub-keys (where English value was a string): Provide a best-effort translation suggestion string. If impossible, use an empty string "".
* For placeholder/accuracy errors: Provide the corrected string suggestion.
* **If an entire top-level object key was missing in the translation (identified in Step 1):** Create ONE error entry for the top-level object key (e.g., \`stringID: "timezones"\`) with an error like "Missing entire object structure" and \`current: null\`. The \`suggestion\` field for this specific error should be \`null\`, as the full suggested object will be built directly in the \`fixed-json\` in the next step. **Do NOT create individual error entries for every sub-key inside the missing object.**
**Step 7: Final JSON Output Assembly (\`fixed-json\`)**
* Compile all error entries from Step 6 into the \`"errors"\` array (must be \`[]\` if no errors).
* Construct the \`"fixed-json"\` object, mirroring the structure of the **English JSON**:
* Iterate through each top-level \`stringID\` in the English JSON.
* **If the English value is a STRING:**
* If no errors were reported for this \`stringID\`, use the original translated string found in the input \`Translated JSON\` (if the key existed there). If the key was missing in the translation, use the generated \`suggestion\` from its error report.
* If errors *were* reported (placeholder, accuracy), use the \`suggestion\` from the corresponding error entry.
* **If the English value is an OBJECT:**
* **CASE A: The object key was MISSING entirely in the Translation:**
* Create a new object for this \`stringID\` in \`fixed-json\`.
* Iterate through *all* keys (\`subKey\`s) and values in the *English* object.
* For each \`subKey\`:
* If the English value is a string, **generate a translation suggestion** for that English string and add the \`subKey\`: \`suggestion\` pair to the new object in \`fixed-json\`.
* If the English value is a nested object, recursively apply this translation generation logic to create the translated nested structure within \`fixed-json\`.
* **CASE B: The object key EXISTED in the Translation:**
* Create an object for this \`stringID\` in \`fixed-json\`.
* Iterate through the keys (\`subKey\`s) present in the *English* object structure.
* For each \`subKey\`:
* Determine the correct value: Check if an error was reported for the path \`stringID.subKey\`.
* If an error *was* reported for this specific \`subKey\`, use the \`suggestion\` from that error entry as the value in \`fixed-json\`.
* If no error was reported for \`stringID.subKey\`, use the original translated value for that \`subKey\` found in the input \`Translated JSON\`.
* If the value is itself a nested object, recursively apply this logic (check sub-sub-keys for errors, use suggestions or original values).
* Exclude any top-level keys present only in the original Translation input.
--- START EXAMPLES ---
**Example 1 (Missing Top-Level Object):**
English JSON: \`{ "greeting": "Hello ${exampleVar}", "timezones": { "zone1": "Central Time", "zone2": "Eastern Time" } }\`
Translated JSON (German): \`{ "greeting": "Hallo ${exampleVar}" }\`
{
"errors": [
{
"stringID": "timezones",
"error": "Missing entire object structure",
"current": null,
"suggestion": null
}
],
"fixed-json": {
"greeting": "Hallo ${exampleVar}",
"timezones": {
"zone1": "Zentralzeit",
"zone2": "Östliche Zeit"
}
}
}
**Example 2 (Placeholder Error within Nested Object):**
English JSON: \`{ "options": { "label": "Name", "tooltip": "Enter your full ${exampleVar}" } }\`
Translated JSON (Spanish): \`{ "options": { "label": "Nombre", "tooltip": "Ingrese su nombre completo" } }\`
{
"errors": [
{
"stringID": "options.tooltip",
"error": "Missing placeholder ${exampleVar}",
"current": "Ingrese su nombre completo",
"suggestion": "Ingrese su ${exampleVar} completo"
}
],
"fixed-json": {
"options": {
"label": "Nombre",
"tooltip": "Ingrese su ${exampleVar} completo"
}
}
}
--- END EXAMPLES ---
Important formatting notes:
- Only return the JSON result - no extra text.
- Ensure the JSON is valid and parseable.
- Use double quotes for all JSON strings.
- The "fixed-json" should always follow the English JSON structure exactly, not the Translated JSON structure.
- Exclude any keys from the Translated JSON that are not present in the English JSON.
- Always preserve the detected variable pattern: ${variableInfo.pattern}
`;
}
async verifyTranslations(englishJson, translatedJsonMap) {
if (!englishJson || Object.keys(englishJson).length === 0) {
throw new Error("English JSON data cannot be empty.");
}
if (!translatedJsonMap || Object.keys(translatedJsonMap).length === 0) {
throw new Error("Translated JSON map cannot be empty.");
}
const englishJsonString = JSON.stringify(englishJson, null, 2);
const allResults = {};
const processingPromises = [];
for (const languageFile in translatedJsonMap) {
if (Object.prototype.hasOwnProperty.call(translatedJsonMap, languageFile)) {
const translatedJson = translatedJsonMap[languageFile];
const resultKey = languageFile.replace(/\.json$/i, '') + '-json';
const promise = (async () => {
try {
const translatedJsonString = JSON.stringify(translatedJson, null, 2);
const targetLanguage = languageFile.replace(/\.json$/i, '');
const contextBlock = this.userContext
? `\nApplication Context Provided by User:\n---\n${this.userContext}\n---\n`
: "";
let ragContext = '';
if (this.ragProvider && this.ragOptions?.enableTranslationMemory) {
const sampleContext = {
sourceText: Object.values(englishJson).slice(0, 3).join(' '),
targetLanguage,
sourceLanguage: 'en',
stringId: 'composite',
domain: this.ragOptions.ragSearchOptions?.filters?.domain
};
ragContext = await this.enhanceWithRAGContext(sampleContext);
}
const variableInfo = this.detectVariablePattern(englishJson);
const systemMessage = this.generateSystemMessage(variableInfo);
const userMessage = `
<english_json>
${englishJsonString}
</english_json>
<translated_json language_file="${languageFile}">
${translatedJsonString}
</translated_json>
${contextBlock ? `<application_context>${this.userContext}</application_context>` : ""}
${ragContext}
Generate the verification report in the specified JSON format.
`;
console.log(`Requesting verification for: ${languageFile} using model ${this.model}`);
const response = await this.openai.chat.completions.create({
model: this.model,
messages: [
{ role: 'system', content: systemMessage },
{ role: 'user', content: userMessage }
],
});
const output = response.choices[0]?.message?.content;
if (!output) {
throw new Error(`No content received from LLM for ${languageFile}`);
}
let parsedOutput;
try {
const cleanedOutput = output.trim().match(/```json\s*([\s\S]*?)\s*```|({[\s\S]*})/);
if (!cleanedOutput || (!cleanedOutput[1] && !cleanedOutput[2])) {
throw new Error("Could not find JSON object in LLM response.");
}
const jsonString = cleanedOutput[1] || cleanedOutput[2];
parsedOutput = JSON.parse(jsonString);
if (!parsedOutput || typeof parsedOutput !== 'object' || !Array.isArray(parsedOutput.errors) || typeof parsedOutput['fixed-json'] !== 'object') {
throw new Error("Parsed JSON structure is invalid.");
}
parsedOutput.errors = parsedOutput.errors.map(err => ({ ...err, languageFile }));
}
catch (parseError) {
console.error(`Failed to parse LLM output as JSON for ${languageFile}:`, parseError, "\nRaw Output:", output);
throw new Error(`Failed to parse LLM response for ${languageFile}: ${parseError.message}`);
}
allResults[resultKey] = parsedOutput;
}
catch (error) {
console.error(`Error processing file ${languageFile}:`, error);
const errorMessage = error instanceof Error ? error.message : String(error);
if (error.status && error.error?.message) {
allResults[resultKey] = { _processingError: `Failed to process ${languageFile}: ${error.status} ${error.error.message}` };
}
else {
allResults[resultKey] = { _processingError: `Failed to process ${languageFile}: ${errorMessage}` };
}
}
})();
processingPromises.push(promise);
}
}
await Promise.all(processingPromises);
console.log("Aggregated verification results:", JSON.stringify(allResults, null, 2));
return allResults;
}
}
//# sourceMappingURL=verifier.js.map