remove validateAndRepairJson function and tests

This commit is contained in:
Anas Syed 2026-01-22 20:32:13 +00:00
parent 263ff8adcc
commit 0aed4e06a2
4 changed files with 1 additions and 149 deletions

View File

@ -2,9 +2,6 @@
* Shared OpenRouter API helper for structured JSON responses.
*/
import { z } from 'zod';
import { getSetting } from '../repositories/settings.js';
const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
export interface JsonSchemaDefinition {
@ -170,118 +167,3 @@ export function parseJsonContent<T>(content: string, jobId?: string): T {
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Validate JSON against a Zod schema and repair with AI if invalid.
*
* @param data - The JSON object to validate
* @param schema - Zod schema to validate against
* @param context - Optional context for logging (e.g., job ID)
* @returns The validated (and possibly repaired) data
*/
export async function validateAndRepairJson<T>(
data: unknown,
schema: z.ZodSchema<T>,
context?: string
): Promise<{ success: true; data: T; repaired: boolean } | { success: false; error: string }> {
const label = context ?? 'unknown';
// First attempt: validate as-is
const result = schema.safeParse(data);
if (result.success) {
return { success: true, data: result.data, repaired: false };
}
// Validation failed - attempt AI repair
console.warn(`⚠️ [${label}] Schema validation failed, attempting AI repair...`);
const errors = result.error.issues.map((issue: z.ZodIssue) => ({
path: issue.path.join('.'),
message: issue.message,
code: issue.code,
}));
console.warn(` Validation errors:`, errors.slice(0, 5)); // Log first 5 errors
// Check if API key is available
if (!process.env.OPENROUTER_API_KEY) {
return {
success: false,
error: `Schema validation failed and no API key for repair: ${errors.map((e: { path: string; message: string }) => `${e.path}: ${e.message}`).join('; ')}`
};
}
const [overrideModel] = await Promise.all([getSetting('model')]);
const model = overrideModel || process.env.MODEL || 'openai/gpt-4o-mini';
const repairPrompt = buildRepairPrompt(data, errors);
try {
const response = await fetch(OPENROUTER_API_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'JobOps',
'X-Title': 'JobOpsSchemaRepair',
},
body: JSON.stringify({
model,
messages: [{ role: 'user', content: repairPrompt }],
stream: false,
plugins: [{ id: 'response-healing' }],
}),
});
if (!response.ok) {
const errorBody = await response.text().catch(() => 'No error body');
return { success: false, error: `AI repair request failed: ${response.status} - ${errorBody}` };
}
const responseData = await response.json();
const content = responseData.choices?.[0]?.message?.content;
if (!content) {
return { success: false, error: 'AI repair returned no content' };
}
// Parse the repaired JSON
const repaired = parseJsonContent<unknown>(content, label);
// Validate the repaired version
const repairedResult = schema.safeParse(repaired);
if (repairedResult.success) {
console.log(`✅ [${label}] AI successfully repaired the JSON`);
return { success: true, data: repairedResult.data, repaired: true };
}
// Still invalid after repair
const newErrors = repairedResult.error.issues.slice(0, 3).map((i: z.ZodIssue) => `${i.path.join('.')}: ${i.message}`).join('; ');
return { success: false, error: `AI repair did not fix all issues: ${newErrors}` };
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return { success: false, error: `AI repair failed: ${message}` };
}
}
function buildRepairPrompt(data: unknown, errors: Array<{ path: string; message: string; code: string }>): string {
const errorList = errors.slice(0, 10).map(e => `- Path "${e.path}": ${e.message}`).join('\n');
return `You are fixing a JSON object that failed schema validation.
VALIDATION ERRORS:
${errorList}
ORIGINAL JSON (may be truncated):
${JSON.stringify(data, null, 2).slice(0, 15000)}
INSTRUCTIONS:
1. Fix ONLY the validation errors listed above
2. Do NOT remove or modify data that isn't causing errors
3. For missing required fields, add them with sensible defaults (empty strings, empty arrays, etc.)
4. For type mismatches, convert to the correct type
5. Preserve all existing valid data
Return ONLY the fixed JSON object, no explanation.`;
}

View File

@ -61,14 +61,6 @@ vi.mock('./resumeProjects.js', () => ({
})
}));
vi.mock('./openrouter.js', () => ({
validateAndRepairJson: vi.fn().mockImplementation(async (data: unknown) => ({
success: true,
data,
repaired: false
}))
}));
vi.mock('child_process', () => ({
spawn: vi.fn().mockImplementation(() => ({
stdout: { on: vi.fn() },

View File

@ -73,13 +73,6 @@ vi.mock('./resumeProjects.js', () => ({
})
}));
// Mock validateAndRepairJson to always return success (bypass validation in tests)
vi.mock('./openrouter.js', () => ({
validateAndRepairJson: vi.fn().mockImplementation((data: unknown) =>
Promise.resolve({ success: true, data, repaired: false })
),
}));
vi.mock('child_process', () => ({
spawn: vi.fn().mockImplementation(() => ({
stdout: { on: vi.fn() },

View File

@ -15,8 +15,6 @@ import { pickProjectIdsForJob } from './projectSelection.js';
import { extractProjectsFromProfile, resolveResumeProjectsSettings } from './resumeProjects.js';
import { getDataDir } from '../config/dataDir.js';
import { getProfile } from './profile.js';
import { validateAndRepairJson } from './openrouter.js';
import { resumeDataSchema } from '../../shared/rxresume-schema.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
@ -168,22 +166,9 @@ export async function generatePdf(
console.warn(` ⚠️ Project visibility step failed for job ${jobId}:`, err);
}
// Validate and repair the resume JSON before PDF generation
const validationResult = await validateAndRepairJson(baseResume, resumeDataSchema, `pdf-${jobId}`);
if (!validationResult.success) {
console.error(`❌ [Job ${jobId}] Resume validation failed: ${validationResult.error}`);
return { success: false, error: `Resume validation failed: ${validationResult.error}` };
}
if (validationResult.repaired) {
console.log(`🔧 [Job ${jobId}] Resume JSON was repaired by AI`);
}
const validatedResume = validationResult.data;
// Write modified resume to temp file
const tempResumePath = join(RESUME_GEN_DIR, `temp_resume_${jobId}.json`);
await writeFile(tempResumePath, JSON.stringify(validatedResume, null, 2));
await writeFile(tempResumePath, JSON.stringify(baseResume, null, 2));
// Generate PDF using Python script - output directly to our data folder
const outputFilename = `resume_${jobId}.pdf`;