@@ -228,6 +228,23 @@ function readBody(req: IncomingMessage): Promise<string> {
|
228 | 228 | }); |
229 | 229 | } |
230 | 230 | |
| 231 | +function parseOpenAiJsonBody(raw: string): Record<string, unknown> | null { |
| 232 | +try { |
| 233 | +return raw ? (JSON.parse(raw) as Record<string, unknown>) : {}; |
| 234 | +} catch { |
| 235 | +return null; |
| 236 | +} |
| 237 | +} |
| 238 | + |
| 239 | +function writeOpenAiMalformedJsonError(res: ServerResponse, label: string) { |
| 240 | +writeJson(res, 400, { |
| 241 | +error: { |
| 242 | +type: "invalid_request_error", |
| 243 | +message: `Malformed JSON body for ${label} request.`, |
| 244 | +}, |
| 245 | +}); |
| 246 | +} |
| 247 | + |
231 | 248 | function transcriptionTextForAudioRequest(rawBody: string) { |
232 | 249 | if (rawBody.length >= QA_GROUP_AUDIO_MIN_MULTIPART_BODY_CHARS) { |
233 | 250 | return QA_GROUP_AUDIO_TRANSCRIPTION_TEXT; |
@@ -3418,7 +3435,11 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n
|
3418 | 3435 | } |
3419 | 3436 | if (req.method === "POST" && url.pathname === "/v1/images/generations") { |
3420 | 3437 | const raw = await readBody(req); |
3421 | | -const body = raw ? (JSON.parse(raw) as Record<string, unknown>) : {}; |
| 3438 | +const body = parseOpenAiJsonBody(raw); |
| 3439 | +if (!body) { |
| 3440 | +writeOpenAiMalformedJsonError(res, "OpenAI Images"); |
| 3441 | +return; |
| 3442 | +} |
3422 | 3443 | imageGenerationRequests.push(body); |
3423 | 3444 | if (imageGenerationRequests.length > 20) { |
3424 | 3445 | imageGenerationRequests.splice(0, imageGenerationRequests.length - 20); |
@@ -3442,7 +3463,11 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n
|
3442 | 3463 | } |
3443 | 3464 | if (req.method === "POST" && url.pathname === "/v1/embeddings") { |
3444 | 3465 | const raw = await readBody(req); |
3445 | | -const body = raw ? (JSON.parse(raw) as Record<string, unknown>) : {}; |
| 3466 | +const body = parseOpenAiJsonBody(raw); |
| 3467 | +if (!body) { |
| 3468 | +writeOpenAiMalformedJsonError(res, "OpenAI Embeddings"); |
| 3469 | +return; |
| 3470 | +} |
3446 | 3471 | const inputs = extractEmbeddingInputTexts(body.input); |
3447 | 3472 | writeJson(res, 200, { |
3448 | 3473 | object: "list", |
@@ -3464,7 +3489,11 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n
|
3464 | 3489 | } |
3465 | 3490 | if (req.method === "POST" && url.pathname === "/v1/responses") { |
3466 | 3491 | const raw = await readBody(req); |
3467 | | -const body = raw ? (JSON.parse(raw) as Record<string, unknown>) : {}; |
| 3492 | +const body = parseOpenAiJsonBody(raw); |
| 3493 | +if (!body) { |
| 3494 | +writeOpenAiMalformedJsonError(res, "OpenAI Responses"); |
| 3495 | +return; |
| 3496 | +} |
3468 | 3497 | const input = Array.isArray(body.input) ? (body.input as ResponsesInputItem[]) : []; |
3469 | 3498 | const events = await buildResponsesPayload(body, scenarioState); |
3470 | 3499 | const resolvedModel = typeof body.model === "string" ? body.model : ""; |
|