fix(ai): pass system prompt via system option instead of system-role message

Some providers (OpenAI Responses API, others) reject system-role messages in
the messages array with AI_InvalidPromptError. Move the analyze/parse system
prompts to the generateText system option, matching the chat handler.
This commit is contained in:
Amruth Pillai
2026-07-04 18:31:40 +02:00
parent 0abb5a07e6
commit 44fa2badb4
+8 -14
View File
@@ -135,23 +135,17 @@ type ParsePdfInput = z.infer<typeof aiCredentialsSchema> & {
};
type BuildResumeParsingMessagesInput = {
systemPrompt: string;
userPrompt: string;
file: z.infer<typeof fileInputSchema>;
mediaType: string;
};
function buildResumeParsingMessages({
systemPrompt,
userPrompt,
file,
mediaType,
}: BuildResumeParsingMessagesInput): ModelMessage[] {
function buildResumeParsingSystemPrompt(systemPrompt: string): string {
return `${systemPrompt}\n\nIMPORTANT: You must return ONLY raw valid JSON. Do not return markdown, do not return explanations. Just the JSON object. Use the following JSON as a template and fill in the extracted values. For arrays, you MUST use the exact key names shown in the template (e.g. use 'description' instead of 'summary', 'website' instead of 'url'):\n\n${JSON.stringify(aiExtractionTemplate, null, 2)}`;
}
function buildResumeParsingMessages({ userPrompt, file, mediaType }: BuildResumeParsingMessagesInput): ModelMessage[] {
return [
{
role: "system",
content: `${systemPrompt}\n\nIMPORTANT: You must return ONLY raw valid JSON. Do not return markdown, do not return explanations. Just the JSON object. Use the following JSON as a template and fill in the extracted values. For arrays, you MUST use the exact key names shown in the template (e.g. use 'description' instead of 'summary', 'website' instead of 'url'):\n\n${JSON.stringify(aiExtractionTemplate, null, 2)}`,
},
{
role: "user",
content: [
@@ -167,8 +161,8 @@ async function parsePdf(input: ParsePdfInput): Promise<ResumeData> {
const result = await generateText({
model,
system: buildResumeParsingSystemPrompt(pdfParserSystemPrompt),
messages: buildResumeParsingMessages({
systemPrompt: pdfParserSystemPrompt,
userPrompt: pdfParserUserPrompt,
file: input.file,
mediaType: "application/pdf",
@@ -188,8 +182,8 @@ async function parseDocx(input: ParseDocxInput): Promise<ResumeData> {
const result = await generateText({
model,
system: buildResumeParsingSystemPrompt(docxParserSystemPrompt),
messages: buildResumeParsingMessages({
systemPrompt: docxParserSystemPrompt,
userPrompt: docxParserUserPrompt,
file: input.file,
mediaType: input.mediaType,
@@ -255,8 +249,8 @@ async function analyzeResume(input: AnalyzeResumeInput): Promise<ResumeAnalysis>
const result = await generateText({
model,
system: systemPrompt,
messages: [
{ role: "system", content: systemPrompt },
{
role: "user",
content: