Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/ayath225_ai-interview-chatbot.json

1 line
17 KiB
JSON

{"repo": "ayath225/ai-interview-chatbot", "files": {"app/api/interview/questions/route.ts": {"150426d4909c77da1216c786c664eef5d503ce52": {"t": 1785745574.6640546, "n": 5455, "h": "2e7e93741b46073f", "c": "import { NextResponse } from 'next/server'\n\nconst DEFAULT_MODEL = 'deepseek-chat'\nconst MAX_QUESTIONS = 5\nconst FALLBACK_NAME = 'Candidate'\nconst FALLBACK_QUESTIONS = [\n 'Can you briefly introduce yourself and your core strengths?',\n 'Which project on your CV are you most proud of, and why?',\n 'Tell me about a technical challenge you solved recently.',\n 'How do you approach learning a new technology quickly?',\n 'Why are you a strong fit for this role?',\n]\n\ninterface DeepSeekMessage {\n content?: string\n}\n\ninterface DeepSeekChoice {\n message?: DeepSeekMessage\n}\n\ninterface DeepSeekResponse {\n choices?: DeepSeekChoice[]\n}\n\ninterface QuestionPayload {\n candidateName?: string\n questions?: string[]\n}\n\ninterface QuestionResponse {\n candidateName: string\n questions: string[]\n source: 'deepseek' | 'fallback'\n warning?: string\n}\n\nconst extractJsonText = (rawText: string): string => {\n const fencedMatch = rawText.match(/```(?:json)?\\s*([\\s\\S]*?)```/i)\n if (fencedMatch?.[1]) {\n return fencedMatch[1]\n }\n\n const firstBrace = rawText.indexOf('{')\n const lastBrace = rawText.lastIndexOf('}')\n if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {\n return rawText.slice(firstBrace, lastBrace + 1)\n }\n\n return rawText\n}\n\nconst inferNameFromCv = (cvContent: string): string => {\n const firstNonEmptyLine = cvContent\n .split('\\n')\n .map((line) => line.trim())\n .find(Boolean)\n\n if (!firstNonEmptyLine) {\n return FALLBACK_NAME\n }\n\n if (\n firstNonEmptyLine.length > 60 ||\n /@|http|linkedin|github|portfolio/i.test(firstNonEmptyLine)\n ) {\n return FALLBACK_NAME\n }\n\n const cleaned = firstNonEmptyLine.replace(/[^a-zA-Z0-9 .'-]/g, '').trim()\n return cleaned || FALLBACK_NAME\n}\n\nconst buildFallbackQuestions = (): string[] => {\n return [...FALLBACK_QUESTIONS].slice(0, MAX_QUESTIONS)\n}\n\nconst fallbackSuccessResponse = (\n cvContent: string,\n warning: string\n): NextResponse<QuestionResponse> => {\n return NextResponse.json({\n candidateName: inferNameFromCv(cvContent),\n questions: buildFallbackQuestions(),\n source: 'fallback',\n warning,\n })\n}\n\nexport async function POST(request: Request) {\n try {\n const { cvContent } = await request.json()\n\n if (typeof cvContent !== 'string' || !cvContent.trim()) {\n return NextResponse.json(\n { error: 'cvContent is required.' },\n { status: 400 }\n )\n }\n\n const apiKey = process.env.DEEPSEEK_API_KEY\n if (!apiKey) {\n return fallbackSuccessResponse(\n cvContent,\n 'DEEPSEEK_API_KEY is missing. Returned fallback interview questions.'\n )\n }\n\n const model = process.env.DEEPSEEK_MODEL || DEFAULT_MODEL\n\n const prompt = `You are a professional interviewer.\n\n From the CV, return STRICT JSON only in this exact shape:\n {\n \"candidateName\": \"string\",\n \"questions\": [\"q1\", \"q2\", \"q3\", \"q4\", \"q5\"]\n }\n\n Rules:\n - candidateName must be the candidate name from the CV.\n - questions must contain exactly 5 concise interview questions.\n - each question must be max 20 words.\n - no markdown, no explanation, JSON only.\n\n CV Text:\n ${cvContent}`\n\n const deepSeekResponse = await fetch(\n 'https://api.deepseek.com/v1/chat/completions',\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model,\n messages: [{ role: 'system', content: prompt }],\n max_tokens: 700,\n temperature: 0.4,\n }),\n cache: 'no-store',\n }\n )\n\n if (!deepSeekResponse.ok) {\n const details = await deepSeekResponse.text()\n return fallbackSuccessResponse(\n cvContent,\n `DeepSeek request failed. Returned fallback questions. Details: ${details}`\n )\n }\n\n const deepSeekJson = (await deepSeekResponse.json()) as DeepSeekResponse\n const rawText = deepSeekJson.choices?.[0]?.message?.content || ''\n\n let parsed: QuestionPayload = {}\n try {\n parsed = JSON.parse(extractJsonText(rawText)) as QuestionPayload\n } catch {\n return fallbackSuccessResponse(\n cvContent,\n `DeepSeek returned non-JSON output. Returned fallback questions. Raw output: ${rawText}`\n )\n }\n\n const candidateName =\n (parsed.candidateName && parsed.candidateName.trim()) ||\n inferNameFromCv(cvContent)\n\n const questions = (parsed.questions || [])\n .filter((item): item is string => typeof item === 'string')\n .map((item) => item.trim())\n .filter(Boolean)\n .slice(0, MAX_QUESTIONS)\n\n if (!questions.length) {\n return fallbackSuccessResponse(\n cvContent,\n 'DeepSeek returned no valid questions. Returned fallback questions.'\n )\n }\n\n while (questions.length < MAX_QUESTIONS) {\n questions.push(FALLBACK_QUESTIONS[questions.length])\n }\n\n return NextResponse.json({\n candidateName,\n questions,\n source: 'deepseek',\n })\n } catch (error) {\n const details = error instanceof Error ? error.message : 'Unexpected server error.'\n return NextResponse.json<QuestionResponse>(\n {\n candidateName: FALLBACK_NAME,\n questions: buildFallbackQuestions(),\n source: 'fallback',\n warning: `Failed to generate interview questions. Returned fallback questions. Details: ${details}`,\n },\n { status: 200 }\n )\n }\n}"}, "efbe81de2612664b7b644677e750bdd860ee9be3": {"t": 1785754384.8857603, "n": 5455, "h": "2e7e93741b46073f", "c": "import { NextResponse } from 'next/server'\n\nconst DEFAULT_MODEL = 'deepseek-chat'\nconst MAX_QUESTIONS = 5\nconst FALLBACK_NAME = 'Candidate'\nconst FALLBACK_QUESTIONS = [\n 'Can you briefly introduce yourself and your core strengths?',\n 'Which project on your CV are you most proud of, and why?',\n 'Tell me about a technical challenge you solved recently.',\n 'How do you approach learning a new technology quickly?',\n 'Why are you a strong fit for this role?',\n]\n\ninterface DeepSeekMessage {\n content?: string\n}\n\ninterface DeepSeekChoice {\n message?: DeepSeekMessage\n}\n\ninterface DeepSeekResponse {\n choices?: DeepSeekChoice[]\n}\n\ninterface QuestionPayload {\n candidateName?: string\n questions?: string[]\n}\n\ninterface QuestionResponse {\n candidateName: string\n questions: string[]\n source: 'deepseek' | 'fallback'\n warning?: string\n}\n\nconst extractJsonText = (rawText: string): string => {\n const fencedMatch = rawText.match(/```(?:json)?\\s*([\\s\\S]*?)```/i)\n if (fencedMatch?.[1]) {\n return fencedMatch[1]\n }\n\n const firstBrace = rawText.indexOf('{')\n const lastBrace = rawText.lastIndexOf('}')\n if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {\n return rawText.slice(firstBrace, lastBrace + 1)\n }\n\n return rawText\n}\n\nconst inferNameFromCv = (cvContent: string): string => {\n const firstNonEmptyLine = cvContent\n .split('\\n')\n .map((line) => line.trim())\n .find(Boolean)\n\n if (!firstNonEmptyLine) {\n return FALLBACK_NAME\n }\n\n if (\n firstNonEmptyLine.length > 60 ||\n /@|http|linkedin|github|portfolio/i.test(firstNonEmptyLine)\n ) {\n return FALLBACK_NAME\n }\n\n const cleaned = firstNonEmptyLine.replace(/[^a-zA-Z0-9 .'-]/g, '').trim()\n return cleaned || FALLBACK_NAME\n}\n\nconst buildFallbackQuestions = (): string[] => {\n return [...FALLBACK_QUESTIONS].slice(0, MAX_QUESTIONS)\n}\n\nconst fallbackSuccessResponse = (\n cvContent: string,\n warning: string\n): NextResponse<QuestionResponse> => {\n return NextResponse.json({\n candidateName: inferNameFromCv(cvContent),\n questions: buildFallbackQuestions(),\n source: 'fallback',\n warning,\n })\n}\n\nexport async function POST(request: Request) {\n try {\n const { cvContent } = await request.json()\n\n if (typeof cvContent !== 'string' || !cvContent.trim()) {\n return NextResponse.json(\n { error: 'cvContent is required.' },\n { status: 400 }\n )\n }\n\n const apiKey = process.env.DEEPSEEK_API_KEY\n if (!apiKey) {\n return fallbackSuccessResponse(\n cvContent,\n 'DEEPSEEK_API_KEY is missing. Returned fallback interview questions.'\n )\n }\n\n const model = process.env.DEEPSEEK_MODEL || DEFAULT_MODEL\n\n const prompt = `You are a professional interviewer.\n\n From the CV, return STRICT JSON only in this exact shape:\n {\n \"candidateName\": \"string\",\n \"questions\": [\"q1\", \"q2\", \"q3\", \"q4\", \"q5\"]\n }\n\n Rules:\n - candidateName must be the candidate name from the CV.\n - questions must contain exactly 5 concise interview questions.\n - each question must be max 20 words.\n - no markdown, no explanation, JSON only.\n\n CV Text:\n ${cvContent}`\n\n const deepSeekResponse = await fetch(\n 'https://api.deepseek.com/v1/chat/completions',\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model,\n messages: [{ role: 'system', content: prompt }],\n max_tokens: 700,\n temperature: 0.4,\n }),\n cache: 'no-store',\n }\n )\n\n if (!deepSeekResponse.ok) {\n const details = await deepSeekResponse.text()\n return fallbackSuccessResponse(\n cvContent,\n `DeepSeek request failed. Returned fallback questions. Details: ${details}`\n )\n }\n\n const deepSeekJson = (await deepSeekResponse.json()) as DeepSeekResponse\n const rawText = deepSeekJson.choices?.[0]?.message?.content || ''\n\n let parsed: QuestionPayload = {}\n try {\n parsed = JSON.parse(extractJsonText(rawText)) as QuestionPayload\n } catch {\n return fallbackSuccessResponse(\n cvContent,\n `DeepSeek returned non-JSON output. Returned fallback questions. Raw output: ${rawText}`\n )\n }\n\n const candidateName =\n (parsed.candidateName && parsed.candidateName.trim()) ||\n inferNameFromCv(cvContent)\n\n const questions = (parsed.questions || [])\n .filter((item): item is string => typeof item === 'string')\n .map((item) => item.trim())\n .filter(Boolean)\n .slice(0, MAX_QUESTIONS)\n\n if (!questions.length) {\n return fallbackSuccessResponse(\n cvContent,\n 'DeepSeek returned no valid questions. Returned fallback questions.'\n )\n }\n\n while (questions.length < MAX_QUESTIONS) {\n questions.push(FALLBACK_QUESTIONS[questions.length])\n }\n\n return NextResponse.json({\n candidateName,\n questions,\n source: 'deepseek',\n })\n } catch (error) {\n const details = error instanceof Error ? error.message : 'Unexpected server error.'\n return NextResponse.json<QuestionResponse>(\n {\n candidateName: FALLBACK_NAME,\n questions: buildFallbackQuestions(),\n source: 'fallback',\n warning: `Failed to generate interview questions. Returned fallback questions. Details: ${details}`,\n },\n { status: 200 }\n )\n }\n}"}, "baf51ef3ffc5e0d8f74e7a9760e372f3e9f68510": {"t": 1785754386.162531, "n": 5377, "h": "ae6755a32ef7ee6b", "c": "import { NextResponse } from 'next/server'\n\nconst DEFAULT_MODEL = 'openai/gpt-4o-mini'\nconst MAX_QUESTIONS = 5\nconst FALLBACK_NAME = 'Candidate'\nconst FALLBACK_QUESTIONS = [\n 'Can you briefly introduce yourself and your core strengths?',\n 'Which project on your CV are you most proud of, and why?',\n 'Tell me about a technical challenge you solved recently.',\n 'How do you approach learning a new technology quickly?',\n 'Why are you a strong fit for this role?',\n]\n\ninterface OpenRouterChoice {\n text?: string\n}\n\ninterface OpenRouterResponse {\n choices?: OpenRouterChoice[]\n}\n\ninterface QuestionPayload {\n candidateName?: string\n questions?: string[]\n}\n\ninterface QuestionResponse {\n candidateName: string\n questions: string[]\n source: 'openrouter' | 'fallback'\n warning?: string\n}\n\nconst extractJsonText = (rawText: string): string => {\n const fencedMatch = rawText.match(/```(?:json)?\\s*([\\s\\S]*?)```/i)\n if (fencedMatch?.[1]) {\n return fencedMatch[1]\n }\n\n const firstBrace = rawText.indexOf('{')\n const lastBrace = rawText.lastIndexOf('}')\n if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {\n return rawText.slice(firstBrace, lastBrace + 1)\n }\n\n return rawText\n}\n\nconst inferNameFromCv = (cvContent: string): string => {\n const firstNonEmptyLine = cvContent\n .split('\\n')\n .map((line) => line.trim())\n .find(Boolean)\n\n if (!firstNonEmptyLine) {\n return FALLBACK_NAME\n }\n\n if (\n firstNonEmptyLine.length > 60 ||\n /@|http|linkedin|github|portfolio/i.test(firstNonEmptyLine)\n ) {\n return FALLBACK_NAME\n }\n\n const cleaned = firstNonEmptyLine.replace(/[^a-zA-Z0-9 .'-]/g, '').trim()\n return cleaned || FALLBACK_NAME\n}\n\nconst buildFallbackQuestions = (): string[] => {\n return [...FALLBACK_QUESTIONS].slice(0, MAX_QUESTIONS)\n}\n\nconst fallbackSuccessResponse = (\n cvContent: string,\n warning: string\n): NextResponse<QuestionResponse> => {\n return NextResponse.json({\n candidateName: inferNameFromCv(cvContent),\n questions: buildFallbackQuestions(),\n source: 'fallback',\n warning,\n })\n}\n\nexport async function POST(request: Request) {\n try {\n const { cvContent } = await request.json()\n\n if (typeof cvContent !== 'string' || !cvContent.trim()) {\n return NextResponse.json(\n { error: 'cvContent is required.' },\n { status: 400 }\n )\n }\n\n const apiKey = process.env.OPENROUTER_API_KEY\n if (!apiKey) {\n return fallbackSuccessResponse(\n cvContent,\n 'OPENROUTER_API_KEY is missing. Returned fallback interview questions.'\n )\n }\n\n const model = process.env.OPENROUTER_MODEL || DEFAULT_MODEL\n\n const prompt = `You are a professional interviewer.\n\n From the CV, return STRICT JSON only in this exact shape:\n {\n \"candidateName\": \"string\",\n \"questions\": [\"q1\", \"q2\", \"q3\", \"q4\", \"q5\"]\n }\n\n Rules:\n - candidateName must be the candidate name from the CV.\n - questions must contain exactly 5 concise interview questions.\n - each question must be max 20 words.\n - no markdown, no explanation, JSON only.\n\n CV Text:\n ${cvContent}`\n\n const openRouterResponse = await fetch(\n 'https://openrouter.ai/api/v1/completions',\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model,\n prompt,\n max_tokens: 700,\n temperature: 0.4,\n }),\n cache: 'no-store',\n }\n )\n\n if (!openRouterResponse.ok) {\n const details = await openRouterResponse.text()\n return fallbackSuccessResponse(\n cvContent,\n `OpenRouter request failed. Returned fallback questions. Details: ${details}`\n )\n }\n\n const openRouterJson = (await openRouterResponse.json()) as OpenRouterResponse\n const rawText = openRouterJson.choices?.[0]?.text || ''\n\n let parsed: QuestionPayload = {}\n try {\n parsed = JSON.parse(extractJsonText(rawText)) as QuestionPayload\n } catch {\n return fallbackSuccessResponse(\n cvContent,\n `OpenRouter returned non-JSON output. Returned fallback questions. Raw output: ${rawText}`\n )\n }\n\n const candidateName =\n (parsed.candidateName && parsed.candidateName.trim()) ||\n inferNameFromCv(cvContent)\n\n const questions = (parsed.questions || [])\n .filter((item): item is string => typeof item === 'string')\n .map((item) => item.trim())\n .filter(Boolean)\n .slice(0, MAX_QUESTIONS)\n\n if (!questions.length) {\n return fallbackSuccessResponse(\n cvContent,\n 'OpenRouter returned no valid questions. Returned fallback questions.'\n )\n }\n\n while (questions.length < MAX_QUESTIONS) {\n questions.push(FALLBACK_QUESTIONS[questions.length])\n }\n\n return NextResponse.json({\n candidateName,\n questions,\n source: 'openrouter',\n })\n } catch (error) {\n const details = error instanceof Error ? error.message : 'Unexpected server error.'\n return NextResponse.json<QuestionResponse>(\n {\n candidateName: FALLBACK_NAME,\n questions: buildFallbackQuestions(),\n source: 'fallback',\n warning: `Failed to generate interview questions. Returned fallback questions. Details: ${details}`,\n },\n { status: 200 }\n )\n }\n}"}}}}