Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 256 additions & 15 deletions bun.lock

Large diffs are not rendered by default.

16 changes: 14 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
"@11labs/react": "^0.1.4",
"@ai-sdk/google": "^1.1.17",
"@deepgram/sdk": "^3.11.1",
"@langchain/community": "^0.3.51",
"@langchain/core": "^0.3.71",
"@langchain/google-genai": "^0.2.16",
"@langchain/langgraph": "^0.4.5",
"@lottiefiles/dotlottie-react": "^0.12.2",
"@mediapipe/face_mesh": "^0.4.1633559619",
"@monaco-editor/react": "^4.6.0",
Expand All @@ -38,10 +42,11 @@
"framer-motion": "^12.0.5",
"jspdf": "^3.0.1",
"jspdf-autotable": "^5.0.2",
"langchain": "^0.3.30",
"lucide-react": "^0.473.0",
"next": "^14.2.24",
"next-themes": "^0.4.4",
"puppeteer": "^24.3.1",
"pdf-parse": "1.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-markdown": "^10.1.0",
Expand All @@ -50,7 +55,8 @@
"styled-components": "^6.1.14",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.2",
"zod": "^3.23.8",
"zod4": "npm:zod@^4.0.17",
"zustand": "^5.0.3"
},
"devDependencies": {
Expand All @@ -64,5 +70,11 @@
"prisma": "^6.7.0",
"tailwindcss": "^3.4.1",
"typescript": "^5"
},
"resolutions": {
"zod": "^4.0.17"
},
"overrides": {
"@tensorflow-models/universal-sentence-encoder": "0.0.0"
}
}
75 changes: 0 additions & 75 deletions src/app/api/end/route.ts

This file was deleted.

36 changes: 0 additions & 36 deletions src/app/api/get-prompts/route.ts

This file was deleted.

18 changes: 18 additions & 0 deletions src/app/api/interview/helpers/speech.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,21 @@ const getAudioBuffer = async (response: any) => {

return Buffer.from(dataArray.buffer);
};


export const getText = async (audioFile: ArrayBuffer) => {
try {
const { result, error } = await deepgram.listen.prerecorded.transcribeFile(
Buffer.from(audioFile),
{
model: "nova-3",
smart_format: true,
}
);
if (error) throw error;
return result.results.channels[0].alternatives[0].transcript;
} catch (error) {
console.error("Error in getText:", error);
throw error;
}
}
57 changes: 31 additions & 26 deletions src/app/api/interview/route.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,61 @@
import { getChatHistory, saveToDbModel, saveToDbUser } from "@/db/dbFunctions";
import { aiGenerator } from "@/lib/helpers/aiGenerator";
import { getAudio } from "./helpers/speech";
import { graph } from "@/lib/ai/conversation/graph";
import { initialState } from "@/lib/ai/conversation/state";
import fs from "fs";

export async function POST(req: Request) {
try {
const formData = await req.formData();

const file = formData.get("file") as File;
const arrayBuffer = file ? await file.arrayBuffer() : null;
const interviewId = formData.get("interviewId") as string;
const timeLeft = formData.get("timeLeft") as string;
const text = formData.get("text") as string;
const round = formData.get("round") as string;

if (!interviewId || !timeLeft || !round) {
return new Response("Missing parameters", { status: 500 });
return new Response("Missing parameters", { status: 400 });
}
const history = await getChatHistory(interviewId);
const textGen = await aiGenerator(
round,
text,
history,
arrayBuffer,
timeLeft
);
if (!textGen?.reply || !textGen?.speechToText) {
return new Response("Error in reponse", { status: 500 });

const initiateState = {
...initialState,
interviewId: interviewId,
round: round,
timeLeft: timeLeft,
userInput: text || "",
audioFile: arrayBuffer,
}
const audio = await getAudio(textGen.reply);
if (!audio) throw "Not Found!";
await saveToDbUser(textGen.speechToText, interviewId);
await saveToDbModel(textGen.reply, interviewId);
const audioBase64 = audio.toString("base64");

const result = await graph.invoke(initiateState);

const response = {
audio: audioBase64,
dsaQuestion: textGen.dsaQuestion,
codeHelp: textGen.codeHelp,
reply: textGen.reply,
audio: result.audioResponse,
dsaQuestion: result.dsaQuestion,
codeHelp: result.codeHelp,
reply: result.reply,
};

return new Response(JSON.stringify(response), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch {

} catch (error) {
console.error("Error in interview conversation:", error);

const audioBuffer = fs.readFileSync("/sound/error.wav");
const audioBase64 = audioBuffer.toString("base64");
const response = {
audio: audioBase64,
dsaQuestion: null,
codeHelp: null,
reply: null,
reply: "Sorry, I encountered an error. Please try again.",
};

return new Response(JSON.stringify(response), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});

}
}
}
59 changes: 10 additions & 49 deletions src/app/api/resume/route.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { generateObject } from "ai";
import { z } from "zod";
import { GEMINI_1_5_FLASH } from "@/lib/utils/ai";
import { getPrompts } from "@/db/dbFunctions";
import { graph } from "@/lib/ai/resume/graph";
import { initialState } from "@/lib/ai/resume/state";

const schema = z.object({
rating: z.number().int().min(0).max(100),
detailedReview: z.string(),
});

export async function POST(req: Request) {
try {
Expand All @@ -19,48 +13,15 @@ export async function POST(req: Request) {
{ status: 400 },
);
}
const fileBase64 = await file.arrayBuffer();
const fileBuffer = Buffer.from(fileBase64);
const systemInstruction = await getPrompts("resume-review");
if (!systemInstruction) {
return Response.json(
{ error: "System instruction not found" },
{ status: 500 },
);
const initiateState = {
...initialState,
resume: file,
country: country,
}

const result = await generateObject({
model: GEMINI_1_5_FLASH,
messages: [
{
role: "user",
content: file
? [
{
type: "text",
text: `Country: ${country}`,
},
{
type: "file",
mimeType: "application/pdf",
data: fileBuffer,
},
]
: [
{
type: "text",
text: `Country: ${country}`,
},
],
},
],
system: systemInstruction,
schema,
});

return Response.json(result.object);
} catch (error) {
console.error(error);
const response = await graph.invoke(initiateState)
if (response.country === "Not Found") throw new Error("Country not found");
return Response.json(response);
} catch {
return Response.json(
{ error: "Failed to process resume" },
{ status: 500 },
Expand Down
Loading