Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1782521
Add 5 Resume Tempaltes From Microsoft
taljacob2 Apr 13, 2025
77d9d91
Move Frontend `assets` Directory To Parent Directory, And Remove Redu…
taljacob2 Apr 13, 2025
147bb5b
Add A Route To Get The Resume Templates As A Base64 Array
taljacob2 Apr 13, 2025
3af4ef3
Add A Stepper Form To Showcase The Templates In Carousel In The Frontend
taljacob2 Apr 13, 2025
81c99f1
Remove Mistakenly Added `package.json`, And Move Carousel Package To …
taljacob2 Apr 24, 2025
02cab00
Add Microsoft Word Documents Preview
taljacob2 Apr 25, 2025
e01bf65
Enlarge The Resume Template Previews
taljacob2 Apr 25, 2025
0b63e26
Add Lazy Loading For Previews
taljacob2 Apr 25, 2025
e173dfe
Fix Preview Of First Resume
taljacob2 Apr 25, 2025
4c5c2c7
Increase The Width Of Previews
taljacob2 Apr 25, 2025
3821ad7
Increase Zoom Of PDF Preview To 90%
taljacob2 Apr 25, 2025
2cacb05
Add Generate Resume Route
taljacob2 Apr 26, 2025
26bc23b
Get Resume Templates With Their Extensions
taljacob2 Apr 26, 2025
a30b414
Add ClassNames To Layout
taljacob2 Apr 26, 2025
c31151f
Update All Layouts To Have ClassNames In `App.tsx`
taljacob2 Apr 26, 2025
bd4e2f3
Update Top Steps Bar For Resume Page
taljacob2 Apr 26, 2025
afb8235
Update Resume Template Preview For Resume Page With Buttons On Top
taljacob2 Apr 26, 2025
9e43050
Add Generate Resume Button
taljacob2 Apr 26, 2025
bd97d76
Show The Filled Fields For Generate Button
taljacob2 Apr 26, 2025
e1c1e28
Show Feedback Status Before Generating Resume
taljacob2 Apr 26, 2025
6c05c55
Send Base64 Template To ChatAI
taljacob2 Apr 26, 2025
9023816
Working On Splitting Resume Into Multiple Prompts
taljacob2 Apr 26, 2025
b45f668
Fix Multiple Prompts Flat In ChatAI Service
taljacob2 Apr 26, 2025
ab2e3d5
Edit `generateImprovedResume` To Unzip A Docx And Rezip It
taljacob2 May 1, 2025
4e6d857
Edit Document XML
taljacob2 May 2, 2025
c1a9da6
Working - AI Response Stucks
taljacob2 May 2, 2025
c6b8ea7
Reduce AI Response Content
taljacob2 May 2, 2025
5753410
Remove Unused Code From `Resume.tsx`
taljacob2 May 3, 2025
61fb339
Add A Try Another Template Button To Reset The Resume Generation
taljacob2 May 3, 2025
4feb599
Align Better The Buttons With Space Between
taljacob2 May 3, 2025
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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 5 additions & 1 deletion nextstep-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
},
"homepage": "https://github.com/NextStepFinalProject/NextStep#readme",
"dependencies": {
"@types/adm-zip": "^0.5.7",
"@types/pdf-parse": "^1.1.5",
"@types/xmldom": "^0.1.34",
"adm-zip": "^0.5.16",
"axios": "^1.8.3",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.3",
Expand All @@ -44,7 +47,8 @@
"socket.io": "^4.8.1",
"supertest": "^7.0.0",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1"
"swagger-ui-express": "^5.0.1",
"xmldom": "^0.6.0"
},
"devDependencies": {
"@babel/preset-env": "^7.26.0",
Expand Down
3 changes: 3 additions & 0 deletions nextstep-backend/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export const config = {
resumesDirectoryPath: () => 'resources/resumes',
resumeMaxSize: () => 5 * 1024 * 1024 // Max file size: 5MB
},
assets: {
resumeTemplatesDirectoryPath: () => 'assets/resume-templates',
},
chatAi: {
api_url: () => process.env.CHAT_AI_API_URL || 'https://openrouter.ai/api/v1/chat/completions',
api_key: () => process.env.OPENROUTER_API_KEY || undefined,
Expand Down
31 changes: 26 additions & 5 deletions nextstep-backend/src/controllers/resume_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Request, Response } from 'express';
import { config } from '../config/config';
import fs from 'fs';
import path from 'path';
import { scoreResume, streamScoreResume } from '../services/resume_service';
import { scoreResume, streamScoreResume, getResumeTemplates, generateImprovedResume } from '../services/resume_service';
import multer from 'multer';
import { CustomRequest } from "types/customRequest";
import { handleError } from "../utils/handle_error";
Expand Down Expand Up @@ -69,7 +69,28 @@ const getStreamResumeScore = async (req: Request, res: Response) => {
}
};

export default {
getResumeScore,
getStreamResumeScore
};
const getTemplates = async (req: Request, res: Response) => {
try {
const templates = await getResumeTemplates();
return res.status(200).json(templates);
} catch (error) {
handleError(error, res);
}
};

const generateResume = async (req: Request, res: Response) => {
try {
const { feedback, jobDescription, templateName } = req.body;

if (!feedback || !jobDescription || !templateName) {
return res.status(400).json({ error: 'Missing required fields' });
}

const result = await generateImprovedResume(feedback, jobDescription, templateName);
return res.status(200).json(result);
} catch (error) {
handleError(error, res);
}
};

export default { getResumeScore, getStreamResumeScore, getTemplates, generateResume };
104 changes: 104 additions & 0 deletions nextstep-backend/src/openapi/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,110 @@ paths:
'500':
description: Internal server error

/resume/templates:
get:
tags:
- Resume
summary: Get available resume templates
security:
- BearerAuth: []
responses:
'200':
description: List of available resume templates retrieved successfully
content:
application/json:
schema:
type: array
items:
type: object
properties:
name:
type: string
description: The name of the template (with extension)
content:
type: string
description: Base64 encoded content of the template file
type:
type: string
description: MIME type of the template file
enum:
- application/pdf
- application/msword
- application/vnd.openxmlformats-officedocument.wordprocessingml.document
'401':
description: Unauthorized
'500':
description: Internal server error

/resume/generate:
post:
tags:
- Resume
summary: Generate an improved resume based on a resume template, feedback and job description
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- feedback
- jobDescription
- templateName
properties:
feedback:
type: string
description: The feedback received from the resume scoring process
jobDescription:
type: string
description: The job description used for scoring the resume
templateName:
type: string
description: The name of the template to use for generating the improved resume
responses:
'200':
description: The generated improved resume
content:
application/json:
schema:
type: object
properties:
content:
type: string
description: Base64 encoded content of the generated resume file
type:
type: string
description: MIME type of the generated resume file
enum:
- application/msword
- application/vnd.openxmlformats-officedocument.wordprocessingml.document
'400':
description: Bad request - Missing required fields
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Error message indicating missing fields
'401':
description: Unauthorized - Missing or invalid authentication token
'404':
description: Template not found
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Error message indicating template not found
'500':
description: Internal server error

/room/user/{receiverUserId}:
get:
tags:
Expand Down
4 changes: 4 additions & 0 deletions nextstep-backend/src/routes/resume_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ router.get('/score/:filename', Resume.getResumeScore);

router.get('/streamScore/:filename', Resume.getStreamResumeScore);

router.get('/templates', Resume.getTemplates);

router.post('/generate', Resume.generateResume);

export default router;
26 changes: 11 additions & 15 deletions nextstep-backend/src/services/chat_api_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const cleanResponse = (response: string): string => {
/**
* @example
* await streamChatWithAI(
* "How would you build the tallest building ever?",
* ["How would you build the tallest building ever?"],
* "You are a helpful assistant.",
* (chunk) => {
* // Handle each chunk of the response
Expand All @@ -18,8 +18,8 @@ const cleanResponse = (response: string): string => {
* );
*/
export const streamChatWithAI = async (
userMessageContent: string,
systemMessageContent: string,
userMessageContents: string[],
onChunk: (chunk: string) => void
): Promise<void> => {
try {
Expand All @@ -32,11 +32,6 @@ export const streamChatWithAI = async (
content: systemMessageContent
};

const userMessage = {
role: 'user',
content: userMessageContent
};

const response = await fetch(API_URL, {
method: 'POST',
headers: {
Expand All @@ -45,7 +40,10 @@ export const streamChatWithAI = async (
},
body: JSON.stringify({
model: MODEL_NAME,
messages: [systemMessage, userMessage],
messages: [ systemMessage, ...userMessageContents.map(userMessageContent => ({
role: 'user',
content: userMessageContent
})) ],
stream: true,
}),
});
Expand Down Expand Up @@ -99,7 +97,7 @@ export const streamChatWithAI = async (
}
}

export const chatWithAI = async (userMessageContent: string, systemMessageContent: string): Promise<string> => {
export const chatWithAI = async (systemMessageContent: string, userMessageContents: string[]): Promise<string> => {
try {
const API_URL = config.chatAi.api_url();
const API_KEY = config.chatAi.api_key();
Expand All @@ -110,16 +108,14 @@ export const chatWithAI = async (userMessageContent: string, systemMessageConten
content: systemMessageContent
};

const userMessage = {
role: 'user',
content: userMessageContent
};

const response = await axios.post(
API_URL,
{
model: MODEL_NAME,
messages: [ systemMessage, userMessage ],
messages: [ systemMessage, ...userMessageContents.map(userMessageContent => ({
role: 'user',
content: userMessageContent
})) ],
},
{
headers: {
Expand Down
2 changes: 1 addition & 1 deletion nextstep-backend/src/services/posts_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const addMisterAIComment = async (postId: string, postContent: string) => {
misterAI = await usersService.addUser('misterai', 'securepassword', 'misterai@example.com', 'local');
}

const comment = await chatService.chatWithAI(postContent, 'You are an AI assistant tasked with providing the first comment on forum posts. Your responses should be relevant, engaging, and encourage further discussion, also must be short, and you must answer if you know the answer. Ensure your comments are appropriate for the content and tone of the post. Also must answer in the language of the user post. answer short answers. dont ask questions to follow up');
const comment = await chatService.chatWithAI('You are an AI assistant tasked with providing the first comment on forum posts. Your responses should be relevant, engaging, and encourage further discussion, also must be short, and you must answer if you know the answer. Ensure your comments are appropriate for the content and tone of the post. Also must answer in the language of the user post. answer short answers. dont ask questions to follow up', [postContent]);
const commentData: CommentData = { postId, owner: misterAI.id, content: comment };
const savedComment = await commentsService.addComment(commentData);
return savedComment;
Expand Down
Loading
Loading