Refactor: Use native json_schema structured output, remove manual schema injection

This commit is contained in:
2026-04-18 02:02:16 +02:00
parent c3bc3bbcdd
commit b0873f3ed3
4 changed files with 14 additions and 19 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -14,12 +14,11 @@ type History = {
}; };
const INSTRUCTION = ` const INSTRUCTION = `
You are an expert quiz solver. You are an expert quiz solver.
Please solve the provided question based on its type and provide the correct result. Please solve the provided question based on its type and provide the correct result.
- For choice questions, output the exact index(es) of the correct answer(s). - For choice questions, output the exact index(es) of the correct answer(s).
- For text/numerical questions, provide the exact wording or number. - For text/numerical questions, provide the exact wording or number.
- For essay questions, provide a highly detailed and complete response, adapting exactly to the requested 'format' (HTML vs plain text) and building upon any 'initial_text' template if supplied. - For essay questions, provide a highly detailed and complete response, adapting exactly to the requested 'format' (HTML vs plain text) and building upon any 'initial_text' template if supplied.
Always output strict JSON according to the requested schema block.
`.trim(); `.trim();
const SYSTEM_INSTRUCTION_MESSAGE = { const SYSTEM_INSTRUCTION_MESSAGE = {
+11 -15
View File
@@ -21,7 +21,8 @@ async function getChatGPTResponse(
const timeoutControler = setTimeout(() => controller.abort(), (config.timeoutValue || 20) * 1000); const timeoutControler = setTimeout(() => controller.abort(), (config.timeoutValue || 20) * 1000);
// Get the content to send to chatgpt // Get the content to send to chatgpt
// Including the instructions to the AI, the images as base64 if needed, the question and the past conversation if history is set to true // Including the instructions to the AI, the images as base64 if needed,
// the question and the past conversation if history is set to true
const contentHandler = await getContentWithHistory(config, questionElement, question); const contentHandler = await getContentWithHistory(config, questionElement, question);
const client = new OpenAI({ const client = new OpenAI({
@@ -40,17 +41,16 @@ async function getChatGPTResponse(
const requestPayload: any = { const requestPayload: any = {
model: config.model, model: config.model,
messages: contentHandler.messages.map(msg => ({ ...msg })), messages: contentHandler.messages.map(msg => ({ ...msg })),
max_completion_tokens: config.maxTokens || 2000 // Maximum length of the response, max_completion_tokens: config.maxTokens || 2000
}; };
// Use the modern json_schema structured output when a schema is available.
// The model is guaranteed to return schema-valid JSON — no prompt hacks needed.
if (targetSchema) { if (targetSchema) {
requestPayload.response_format = { requestPayload.response_format = {
type: 'json_object' type: 'json_schema',
json_schema: targetSchema
}; };
if (requestPayload.messages.length > 0 && requestPayload.messages[0].role === 'system') {
requestPayload.messages[0].content += `\n\nYou MUST respond in JSON strictly adhering to the following schema. Do NOT wrap the JSON in markdown code blocks. Output raw JSON only.\n\n${JSON.stringify(targetSchema, null, 2)}`;
}
} }
const req = await client.chat.completions.create(fixeO(config.model, requestPayload), { const req = await client.chat.completions.create(fixeO(config.model, requestPayload), {
@@ -60,17 +60,13 @@ async function getChatGPTResponse(
clearTimeout(timeoutControler); clearTimeout(timeoutControler);
const rawResponse = req.choices[0].message.content ?? ''; const rawResponse = req.choices[0].message.content ?? '';
let structuredResponse: MoodleQuestionResponse | null = null;
if (targetSchema) { let structuredResponse: MoodleQuestionResponse | null = null;
if (targetSchema && rawResponse) {
try { try {
const cleanedResponse = rawResponse structuredResponse = JSON.parse(rawResponse);
.replace(/^```(json)?[\s\S]*?\n([\s\S]*?)```$/g, '$2')
.replace(/^```(json)?|```$/gm, '')
.trim();
structuredResponse = JSON.parse(cleanedResponse);
} catch (e) { } catch (e) {
console.error('Failed to parse structured JSON from GPT', e); console.error('Failed to parse structured JSON response', e);
} }
} }