Spaces:
Runtime error
Runtime error
Rename app.py to main.py
Browse files
app.py
DELETED
@@ -1,585 +0,0 @@
|
|
1 |
-
import sys
|
2 |
-
import warnings
|
3 |
-
from datetime import datetime
|
4 |
-
import time
|
5 |
-
import json
|
6 |
-
import gradio as gr
|
7 |
-
from crewai import Agent, Crew, Process, Task, LLM
|
8 |
-
from crewai.project import CrewBase, agent, crew, task
|
9 |
-
from crewai_tools import SerperDevTool, ScrapeWebsiteTool
|
10 |
-
from crewai.knowledge.source.pdf_knowledge_source import PDFKnowledgeSource
|
11 |
-
import openai
|
12 |
-
from crewai import Agent, Task, Crew
|
13 |
-
import os
|
14 |
-
import queue
|
15 |
-
from langchain_openai import ChatOpenAI
|
16 |
-
from typing import List, Dict, Optional
|
17 |
-
from pydantic import BaseModel, Field, confloat
|
18 |
-
|
19 |
-
# llm = LLM(model="deepseek/deepseek-r1-distill-qwen-32b")
|
20 |
-
# from crewai.llms import LiteLLM
|
21 |
-
from fastapi import FastAPI
|
22 |
-
|
23 |
-
app = FastAPI()
|
24 |
-
|
25 |
-
@app.get("/")
|
26 |
-
def greet_json():
|
27 |
-
return {"Hello": "World!"}
|
28 |
-
|
29 |
-
llm = LLM(
|
30 |
-
model="deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
|
31 |
-
api_base="http://localhost:8000" # or DeepSeek API
|
32 |
-
)
|
33 |
-
class SkillScore(BaseModel):
|
34 |
-
skill_name: str = Field(description="Name of the skill being scored")
|
35 |
-
required: bool = Field(description="Whether this skill is required or nice-to-have")
|
36 |
-
match_level: confloat(ge=0, le=1) = Field(description="How well the candidate's experience matches (0-1)")
|
37 |
-
years_experience: Optional[float] = Field(description="Years of experience with this skill", default=None)
|
38 |
-
context_score: confloat(ge=0, le=1) = Field(
|
39 |
-
description="How relevant the skill usage context is to the job requirements",
|
40 |
-
default=0.5
|
41 |
-
)
|
42 |
-
|
43 |
-
class JobMatchScore(BaseModel):
|
44 |
-
overall_match: confloat(ge=0, le=100) = Field(
|
45 |
-
description="Overall match percentage (0-100)"
|
46 |
-
)
|
47 |
-
technical_skills_match: confloat(ge=0, le=100) = Field(
|
48 |
-
description="Technical skills match percentage"
|
49 |
-
)
|
50 |
-
soft_skills_match: confloat(ge=0, le=100) = Field(
|
51 |
-
description="Soft skills match percentage"
|
52 |
-
)
|
53 |
-
experience_match: confloat(ge=0, le=100) = Field(
|
54 |
-
description="Experience level match percentage"
|
55 |
-
)
|
56 |
-
education_match: confloat(ge=0, le=100) = Field(
|
57 |
-
description="Education requirements match percentage"
|
58 |
-
)
|
59 |
-
industry_match: confloat(ge=0, le=100) = Field(
|
60 |
-
description="Industry experience match percentage"
|
61 |
-
)
|
62 |
-
skill_details: List[SkillScore] = Field(
|
63 |
-
description="Detailed scoring for each skill",
|
64 |
-
default_factory=list
|
65 |
-
)
|
66 |
-
strengths: List[str] = Field(
|
67 |
-
description="List of areas where candidate exceeds requirements",
|
68 |
-
default_factory=list
|
69 |
-
)
|
70 |
-
gaps: List[str] = Field(
|
71 |
-
description="List of areas needing improvement",
|
72 |
-
default_factory=list
|
73 |
-
)
|
74 |
-
scoring_factors: Dict[str, float] = Field(
|
75 |
-
description="Weights used for different scoring components",
|
76 |
-
default_factory=lambda: {
|
77 |
-
"technical_skills": 0.35,
|
78 |
-
"soft_skills": 0.20,
|
79 |
-
"experience": 0.25,
|
80 |
-
"education": 0.10,
|
81 |
-
"industry": 0.10
|
82 |
-
}
|
83 |
-
)
|
84 |
-
|
85 |
-
class JobRequirements(BaseModel):
|
86 |
-
technical_skills: List[str] = Field(
|
87 |
-
description="List of required technical skills",
|
88 |
-
default_factory=list
|
89 |
-
)
|
90 |
-
soft_skills: List[str] = Field(
|
91 |
-
description="List of required soft skills",
|
92 |
-
default_factory=list
|
93 |
-
)
|
94 |
-
experience_requirements: List[str] = Field(
|
95 |
-
description="List of experience requirements",
|
96 |
-
default_factory=list
|
97 |
-
)
|
98 |
-
key_responsibilities: List[str] = Field(
|
99 |
-
description="List of key job responsibilities",
|
100 |
-
default_factory=list
|
101 |
-
)
|
102 |
-
education_requirements: List[str] = Field(
|
103 |
-
description="List of education requirements",
|
104 |
-
default_factory=list
|
105 |
-
)
|
106 |
-
nice_to_have: List[str] = Field(
|
107 |
-
description="List of preferred but not required skills",
|
108 |
-
default_factory=list
|
109 |
-
)
|
110 |
-
job_title: str = Field(
|
111 |
-
description="Official job title",
|
112 |
-
default=""
|
113 |
-
)
|
114 |
-
department: Optional[str] = Field(
|
115 |
-
description="Department or team within the company",
|
116 |
-
default=None
|
117 |
-
)
|
118 |
-
reporting_structure: Optional[str] = Field(
|
119 |
-
description="Who this role reports to and any direct reports",
|
120 |
-
default=None
|
121 |
-
)
|
122 |
-
job_level: Optional[str] = Field(
|
123 |
-
description="Level of the position (e.g., Entry, Senior, Lead)",
|
124 |
-
default=None
|
125 |
-
)
|
126 |
-
location_requirements: Dict[str, str] = Field(
|
127 |
-
description="Location details including remote/hybrid options",
|
128 |
-
default_factory=dict
|
129 |
-
)
|
130 |
-
work_schedule: Optional[str] = Field(
|
131 |
-
description="Expected work hours and schedule flexibility",
|
132 |
-
default=None
|
133 |
-
)
|
134 |
-
travel_requirements: Optional[str] = Field(
|
135 |
-
description="Expected travel frequency and scope",
|
136 |
-
default=None
|
137 |
-
)
|
138 |
-
compensation: Dict[str, str] = Field(
|
139 |
-
description="Salary range and compensation details if provided",
|
140 |
-
default_factory=dict
|
141 |
-
)
|
142 |
-
benefits: List[str] = Field(
|
143 |
-
description="List of benefits and perks",
|
144 |
-
default_factory=list
|
145 |
-
)
|
146 |
-
tools_and_technologies: List[str] = Field(
|
147 |
-
description="Specific tools, software, or technologies used",
|
148 |
-
default_factory=list
|
149 |
-
)
|
150 |
-
industry_knowledge: List[str] = Field(
|
151 |
-
description="Required industry-specific knowledge",
|
152 |
-
default_factory=list
|
153 |
-
)
|
154 |
-
certifications_required: List[str] = Field(
|
155 |
-
description="Required certifications or licenses",
|
156 |
-
default_factory=list
|
157 |
-
)
|
158 |
-
security_clearance: Optional[str] = Field(
|
159 |
-
description="Required security clearance level if any",
|
160 |
-
default=None
|
161 |
-
)
|
162 |
-
team_size: Optional[str] = Field(
|
163 |
-
description="Size of the immediate team",
|
164 |
-
default=None
|
165 |
-
)
|
166 |
-
key_projects: List[str] = Field(
|
167 |
-
description="Major projects or initiatives mentioned",
|
168 |
-
default_factory=list
|
169 |
-
)
|
170 |
-
cross_functional_interactions: List[str] = Field(
|
171 |
-
description="Teams or departments this role interacts with",
|
172 |
-
default_factory=list
|
173 |
-
)
|
174 |
-
career_growth: List[str] = Field(
|
175 |
-
description="Career development and growth opportunities",
|
176 |
-
default_factory=list
|
177 |
-
)
|
178 |
-
training_provided: List[str] = Field(
|
179 |
-
description="Training or development programs offered",
|
180 |
-
default_factory=list
|
181 |
-
)
|
182 |
-
diversity_inclusion: Optional[str] = Field(
|
183 |
-
description="D&I statements or requirements",
|
184 |
-
default=None
|
185 |
-
)
|
186 |
-
company_values: List[str] = Field(
|
187 |
-
description="Company values mentioned in the job posting",
|
188 |
-
default_factory=list
|
189 |
-
)
|
190 |
-
job_url: str = Field(
|
191 |
-
description="URL of the job posting",
|
192 |
-
default=""
|
193 |
-
)
|
194 |
-
posting_date: Optional[str] = Field(
|
195 |
-
description="When the job was posted",
|
196 |
-
default=None
|
197 |
-
)
|
198 |
-
application_deadline: Optional[str] = Field(
|
199 |
-
description="Application deadline if specified",
|
200 |
-
default=None
|
201 |
-
)
|
202 |
-
special_instructions: List[str] = Field(
|
203 |
-
description="Any special application instructions or requirements",
|
204 |
-
default_factory=list
|
205 |
-
)
|
206 |
-
match_score: JobMatchScore = Field(
|
207 |
-
description="Detailed scoring of how well the candidate matches the job requirements",
|
208 |
-
default_factory=JobMatchScore
|
209 |
-
)
|
210 |
-
score_explanation: List[str] = Field(
|
211 |
-
description="Detailed explanation of how scores were calculated",
|
212 |
-
default_factory=list
|
213 |
-
)
|
214 |
-
|
215 |
-
class ResumeOptimization(BaseModel):
|
216 |
-
content_suggestions: List[Dict[str, str]] = Field(
|
217 |
-
description="List of content optimization suggestions with 'before' and 'after' examples"
|
218 |
-
)
|
219 |
-
skills_to_highlight: List[str] = Field(
|
220 |
-
description="List of skills that should be emphasized based on job requirements"
|
221 |
-
)
|
222 |
-
achievements_to_add: List[str] = Field(
|
223 |
-
description="List of achievements that should be added or modified"
|
224 |
-
)
|
225 |
-
keywords_for_ats: List[str] = Field(
|
226 |
-
description="List of important keywords for ATS optimization"
|
227 |
-
)
|
228 |
-
formatting_suggestions: List[str] = Field(
|
229 |
-
description="List of formatting improvements"
|
230 |
-
)
|
231 |
-
|
232 |
-
class CompanyResearch(BaseModel):
|
233 |
-
recent_developments: List[str] = Field(
|
234 |
-
description="List of recent company news and developments"
|
235 |
-
)
|
236 |
-
culture_and_values: List[str] = Field(
|
237 |
-
description="Key points about company culture and values"
|
238 |
-
)
|
239 |
-
market_position: Dict[str, List[str]] = Field(
|
240 |
-
description="Information about market position, including competitors and industry standing"
|
241 |
-
)
|
242 |
-
growth_trajectory: List[str] = Field(
|
243 |
-
description="Information about company's growth and future plans"
|
244 |
-
)
|
245 |
-
interview_questions: List[str] = Field(
|
246 |
-
description="Strategic questions to ask during the interview"
|
247 |
-
)
|
248 |
-
|
249 |
-
resume_analyzer = Agent(
|
250 |
-
role= "Resume Optimization Expert",
|
251 |
-
goal= "Analyze resumes and provide structured optimization suggestions",
|
252 |
-
backstory= """
|
253 |
-
You are a resume optimization specialist with deep knowledge of ATS systems
|
254 |
-
and modern resume best practices. You excel at analyzing PDF resumes and
|
255 |
-
providing actionable suggestions for improvement. Your recommendations always
|
256 |
-
focus on both human readability and ATS compatibility.""",
|
257 |
-
verbose=True,
|
258 |
-
llm = llm,
|
259 |
-
# knowledge_sources=[pdf_source],
|
260 |
-
)
|
261 |
-
job_analyzer = Agent(
|
262 |
-
role= "Job Requirements Analyst",
|
263 |
-
goal= "Analyze job descriptions and score candidate fit",
|
264 |
-
backstory= """
|
265 |
-
You are an expert in job market analysis and candidate evaluation. Your strength
|
266 |
-
lies in breaking down job requirements into clear categories and providing
|
267 |
-
detailed scoring based on candidate qualifications. You understand both technical
|
268 |
-
and soft skills requirements, and can evaluate experience levels accurately.""",
|
269 |
-
verbose=True,
|
270 |
-
tools=[ScrapeWebsiteTool()],
|
271 |
-
llm = llm,
|
272 |
-
|
273 |
-
# knowledge_sources=[pdf_source],
|
274 |
-
)
|
275 |
-
company_researcher = Agent(
|
276 |
-
role= "Company Intelligence Specialist",
|
277 |
-
goal= "Research companies and prepare interview insights",
|
278 |
-
backstory= """
|
279 |
-
You are a corporate research expert who excels at gathering and analyzing
|
280 |
-
the latest company information. You know how to find and synthesize data
|
281 |
-
from various sources to create comprehensive company profiles and prepare
|
282 |
-
candidates for interviews. """,
|
283 |
-
tools=[SerperDevTool()],
|
284 |
-
verbose=True,
|
285 |
-
llm = llm,
|
286 |
-
|
287 |
-
# knowledge_sources=[pdf_source],
|
288 |
-
|
289 |
-
)
|
290 |
-
resume_writer = Agent (
|
291 |
-
role= "Resume Markdown Specialist",
|
292 |
-
goal= "Create beautifully formatted, ATS-optimized resumes in markdown",
|
293 |
-
backstory= """
|
294 |
-
You are a resume writing expert who specializes in creating markdown-formatted
|
295 |
-
resumes. You know how to transform structured optimization suggestions into
|
296 |
-
beautifully formatted, ATS-friendly documents that maintain professionalism
|
297 |
-
while showcasing candidate strengths effectively.""",
|
298 |
-
verbose=True,
|
299 |
-
llm = llm,
|
300 |
-
|
301 |
-
)
|
302 |
-
report_generator = Agent(
|
303 |
-
role= "Career Report Generator and Markdown Specialist",
|
304 |
-
goal= "Create comprehensive, visually appealing, and actionable reports from job application analysis",
|
305 |
-
backstory= """
|
306 |
-
You are an expert in data visualization, technical writing, and Markdown formatting.
|
307 |
-
You excel at combining data from multiple JSON sources to create cohesive,
|
308 |
-
visually appealing reports. Your specialty is transforming structured analysis
|
309 |
-
into clear, actionable insights with proper markdown formatting, emojis, and
|
310 |
-
visual elements that make information both appealing and easily digestible.""",
|
311 |
-
verbose=True,
|
312 |
-
llm = llm,
|
313 |
-
|
314 |
-
)
|
315 |
-
|
316 |
-
analyze_job_task = Task(
|
317 |
-
description="""Analyze the {job_url} description and score the candidate's fit based on their resume.
|
318 |
-
Output will be saved as structured JSON data.
|
319 |
-
1. Extract Requirements:
|
320 |
-
- Technical skills (required vs nice-to-have)
|
321 |
-
- Soft skills
|
322 |
-
- Experience levels
|
323 |
-
- Education requirements
|
324 |
-
- Industry knowledge
|
325 |
-
2. Score Technical Skills (35% of total):
|
326 |
-
- For each required skill:
|
327 |
-
* Match Level (0-1): How well does candidate's experience match?
|
328 |
-
* Years Experience: Compare to required years
|
329 |
-
* Context Score: How relevant is their usage of the skill?
|
330 |
-
- Calculate weighted average based on skill importance
|
331 |
-
3. Score Soft Skills (20% of total):
|
332 |
-
- Identify soft skills from resume
|
333 |
-
- Compare against job requirements
|
334 |
-
- Consider context and demonstration of skills
|
335 |
-
4. Score Experience (25% of total):
|
336 |
-
- Years of relevant experience
|
337 |
-
- Role similarity
|
338 |
-
- Industry relevance
|
339 |
-
- Project scope and complexity
|
340 |
-
5. Score Education (10% of total):
|
341 |
-
- Degree level match
|
342 |
-
- Field of study relevance
|
343 |
-
- Additional certifications
|
344 |
-
6. Score Industry Knowledge (10% of total):
|
345 |
-
- Years in similar industry
|
346 |
-
- Domain expertise
|
347 |
-
- Industry-specific achievements
|
348 |
-
7. Calculate Overall Score:
|
349 |
-
- Weighted average of all components
|
350 |
-
- Identify key strengths and gaps
|
351 |
-
- Provide detailed scoring explanation""",
|
352 |
-
expected_output="Structured JSON data containing job analysis and scoring details according to the JobRequirements model schema",
|
353 |
-
output_file='job_analysis.json',
|
354 |
-
output_pydantic=JobRequirements,
|
355 |
-
agent=job_analyzer,
|
356 |
-
# knowledge_sources=[pdf_source],
|
357 |
-
|
358 |
-
)
|
359 |
-
optimize_resume_task = Task(
|
360 |
-
description= """
|
361 |
-
Review the provided resume against the job analysis and create structured optimization suggestions.
|
362 |
-
Output will be saved as structured JSON data.
|
363 |
-
1. Content Analysis:
|
364 |
-
- Compare resume content with job requirements
|
365 |
-
- Identify missing keywords and skills
|
366 |
-
- Analyze achievement descriptions
|
367 |
-
- Check for ATS compatibility
|
368 |
-
2. Structure Review:
|
369 |
-
- Evaluate section organization
|
370 |
-
- Check formatting consistency
|
371 |
-
- Assess information hierarchy
|
372 |
-
- Verify contact details
|
373 |
-
3. Generate Suggestions:
|
374 |
-
- Content improvements with before/after examples
|
375 |
-
- Skills to highlight based on job match
|
376 |
-
- Achievements to add or modify
|
377 |
-
- ATS optimization recommendations
|
378 |
-
4. Make sure not to add skills that are not there in the candidate but you can extract the relavent skills from candidate""",
|
379 |
-
expected_output= """
|
380 |
-
Structured JSON data containing detailed optimization suggestions according to
|
381 |
-
the ResumeOptimization model schema.""",
|
382 |
-
agent=resume_analyzer,
|
383 |
-
context= [analyze_job_task],
|
384 |
-
output_file='resume_optimization.json',
|
385 |
-
output_pydantic=ResumeOptimization
|
386 |
-
)
|
387 |
-
research_company_task = Task(
|
388 |
-
description= """
|
389 |
-
Research the {company_name} and prepare the latest (year 2025) and comprehensive analysis.
|
390 |
-
Output will be saved as structured JSON data.
|
391 |
-
1. Company Overview:
|
392 |
-
- Recent developments and news
|
393 |
-
- Culture and values
|
394 |
-
- Market position
|
395 |
-
- Growth trajectory
|
396 |
-
2. Interview Preparation:
|
397 |
-
- Common interview questions
|
398 |
-
- Company-specific topics
|
399 |
-
- Recent projects or initiatives
|
400 |
-
- Key challenges and opportunities""",
|
401 |
-
expected_output="""
|
402 |
-
Structured JSON data containing company research results according to
|
403 |
-
the CompanyResearch model schema.""",
|
404 |
-
agent= company_researcher,
|
405 |
-
context= [analyze_job_task, optimize_resume_task],
|
406 |
-
output_file='company_research.json',
|
407 |
-
output_pydantic=CompanyResearch
|
408 |
-
)
|
409 |
-
generate_resume_task = Task(
|
410 |
-
description= """
|
411 |
-
Using the optimization suggestions and job analysis from previous steps,
|
412 |
-
create a polished resume in markdown format.
|
413 |
-
Do not add markdown code blocks like '```'.
|
414 |
-
1. Content Integration:
|
415 |
-
- Incorporate optimization suggestions
|
416 |
-
- Add missing keywords and skills
|
417 |
-
- Enhance achievement descriptions
|
418 |
-
- Ensure ATS compatibility
|
419 |
-
2. Formatting:
|
420 |
-
- Use proper markdown headers (#, ##, ###)
|
421 |
-
- Apply consistent styling
|
422 |
-
- Create clear section hierarchy
|
423 |
-
- Use bullet points effectively
|
424 |
-
3. Documentation:
|
425 |
-
- Track changes made
|
426 |
-
- Note preserved elements
|
427 |
-
- Explain optimization choices""",
|
428 |
-
expected_output= """
|
429 |
-
A beautifully formatted markdown resume document that:
|
430 |
-
- Incorporates all optimization suggestions
|
431 |
-
- Uses proper markdown formatting
|
432 |
-
- Is ATS-friendly
|
433 |
-
- Documents all changes made""",
|
434 |
-
agent= resume_writer,
|
435 |
-
context= [optimize_resume_task, analyze_job_task, research_company_task],
|
436 |
-
output_file='output/optimized_resume.md'
|
437 |
-
)
|
438 |
-
generate_report_task = Task(
|
439 |
-
description="""
|
440 |
-
Create an executive summary report using data from previous steps.
|
441 |
-
Format in markdown without code blocks '```'.
|
442 |
-
1. Data Integration:
|
443 |
-
- Job analysis and scores
|
444 |
-
- Resume optimization details
|
445 |
-
- Company research insights
|
446 |
-
- Final resume changes
|
447 |
-
2. Report Sections:
|
448 |
-
## Executive Summary
|
449 |
-
- Overall match score and quick wins
|
450 |
-
- Key strengths and improvement areas
|
451 |
-
- Action items priority list
|
452 |
-
## Job Fit Analysis
|
453 |
-
- Detailed score breakdown
|
454 |
-
- Skills match assessment
|
455 |
-
- Experience alignment
|
456 |
-
## Optimization Overview
|
457 |
-
- Key resume improvements
|
458 |
-
- ATS optimization results
|
459 |
-
- Impact metrics
|
460 |
-
## Company Insights
|
461 |
-
- Culture fit analysis
|
462 |
-
- Interview preparation tips
|
463 |
-
- Key talking points
|
464 |
-
## Next Steps
|
465 |
-
- Prioritized action items
|
466 |
-
- Skill development plan
|
467 |
-
- Application strategy
|
468 |
-
3. Formatting:
|
469 |
-
- Use proper markdown headers
|
470 |
-
- Include relevant emojis
|
471 |
-
- Create tables where appropriate
|
472 |
-
- Use bullet points for scannability""",
|
473 |
-
expected_output=
|
474 |
-
""" A comprehensive markdown report that combines all analyses into an
|
475 |
-
actionable, clear document with concrete next steps.""",
|
476 |
-
agent= report_generator,
|
477 |
-
context= [analyze_job_task, optimize_resume_task, research_company_task, generate_resume_task, ],
|
478 |
-
output_file='output/final_report.md'
|
479 |
-
)
|
480 |
-
import os
|
481 |
-
from crewai import Crew
|
482 |
-
|
483 |
-
# Function to run CrewAI with dynamic inputs
|
484 |
-
def run_crew(api_key: str, job_url: str, company_name: str, resume_pdf_path: str):
|
485 |
-
if not api_key:
|
486 |
-
return "⚠️ Please provide a valid OpenAI API Key."
|
487 |
-
|
488 |
-
os.environ["OPENAI_API_KEY"] = f"{api_key}" # Set API key securely
|
489 |
-
# Load PDF as knowledge source
|
490 |
-
# pdf_source = resume_pdf_path
|
491 |
-
pdf_source = PDFKnowledgeSource(
|
492 |
-
file_paths=[resume_pdf_path])
|
493 |
-
# Initialize CrewAI with resume knowledge
|
494 |
-
print(resume_pdf_path)
|
495 |
-
crew = Crew(
|
496 |
-
agents=[resume_analyzer, job_analyzer, company_researcher, resume_writer, report_generator],
|
497 |
-
tasks=[analyze_job_task, optimize_resume_task, research_company_task, generate_resume_task, generate_report_task ],
|
498 |
-
verbose=True,
|
499 |
-
knowledge_sources=[pdf_source],
|
500 |
-
|
501 |
-
)
|
502 |
-
inputs = {
|
503 |
-
'job_url': job_url,
|
504 |
-
'company_name': company_name
|
505 |
-
}
|
506 |
-
|
507 |
-
# Run AI pipeline
|
508 |
-
result_extractor = crew.kickoff(inputs=inputs)
|
509 |
-
# Ensure output exists before writing files
|
510 |
-
# optimized_resume_text = result_extractor.get('optimized_resume') if isinstance(result_extractor, dict) and 'optimized_resume' in result_extractor else "No resume data generated."
|
511 |
-
# final_report_text = result_extractor.get('final_report') if isinstance(result_extractor, dict) and 'final_report' in result_extractor else "No report generated."
|
512 |
-
|
513 |
-
# # Save outputs as markdown files
|
514 |
-
# optimized_resume_path = os.path.join(OUTPUT_DIR, "optimized_resume.md")
|
515 |
-
# final_report_path = os.path.join(OUTPUT_DIR, "final_report.md")
|
516 |
-
|
517 |
-
# with open(optimized_resume_path, "w", encoding="utf-8") as resume_file:
|
518 |
-
# resume_file.write(f"# Optimized Resume\n\n{optimized_resume_text}\n")
|
519 |
-
|
520 |
-
# with open(final_report_path, "w", encoding="utf-8") as report_file:
|
521 |
-
# report_file.write(f"# AI Resume Optimization Report\n\n{final_report_text}\n")
|
522 |
-
|
523 |
-
# return "✅ Resume processed successfully! Download the files below.", optimized_resume_path, final_report_path
|
524 |
-
|
525 |
-
return result_extractor
|
526 |
-
|
527 |
-
import gradio as gr
|
528 |
-
import os
|
529 |
-
import shutil
|
530 |
-
import traceback
|
531 |
-
|
532 |
-
UPLOAD_DIR = "knowledge"
|
533 |
-
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
534 |
-
OUTPUT_DIR = "output"
|
535 |
-
|
536 |
-
def process_resume(api_key, job_url, company_name, resume_pdf,output_files):
|
537 |
-
"""Handles user input, saves the uploaded resume, and runs CrewAI."""
|
538 |
-
try:
|
539 |
-
if not api_key:
|
540 |
-
return "⚠️ Please enter your OpenAI API Key.", []
|
541 |
-
|
542 |
-
if resume_pdf is None:
|
543 |
-
return "⚠️ Please upload a PDF resume.", []
|
544 |
-
|
545 |
-
# Get the actual file path from the uploaded file
|
546 |
-
resume_path_tmp = resume_pdf.name # Extract temp file path
|
547 |
-
resume_filename = os.path.basename(resume_path_tmp)
|
548 |
-
resume_path = os.path.join(UPLOAD_DIR, resume_filename)
|
549 |
-
|
550 |
-
# Move the uploaded file to the desired location
|
551 |
-
shutil.move(resume_path_tmp, resume_path)
|
552 |
-
print(resume_filename)
|
553 |
-
# Run CrewAI (assuming `run_crew()` is implemented)
|
554 |
-
result = run_crew(api_key, job_url, company_name, resume_filename)
|
555 |
-
|
556 |
-
# Get the list of files in the output folder for download
|
557 |
-
output_files = [os.path.join(OUTPUT_DIR, f) for f in os.listdir(OUTPUT_DIR) if os.path.isfile(os.path.join(OUTPUT_DIR, f))]
|
558 |
-
|
559 |
-
return result, output_files
|
560 |
-
|
561 |
-
except Exception as e:
|
562 |
-
error_msg = f"❌ An error occurred:\n\n{str(e)}\n\n{traceback.format_exc()}"
|
563 |
-
return error_msg, []
|
564 |
-
|
565 |
-
# Build Gradio UI
|
566 |
-
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
567 |
-
gr.Markdown("# 🤖 AI Resume Optimizer 🚀")
|
568 |
-
gr.Markdown("Get Complete analysis of your resume")
|
569 |
-
|
570 |
-
api_key = gr.Textbox(label="🔑 Open AI API Key", placeholder="Enter your OpenAI API Key securely")
|
571 |
-
job_url = gr.Textbox(label="🔗 Job URL", placeholder="Paste job listing URL here")
|
572 |
-
company_name = gr.Textbox(label="🏢 Company Name", placeholder="Enter company name")
|
573 |
-
resume_pdf = gr.File(label="📄 Upload Resume (PDF Only)")
|
574 |
-
btn = gr.Button("Optimize Resume")
|
575 |
-
|
576 |
-
output = gr.Textbox(label="📝 Agent Output", interactive=True)
|
577 |
-
gr.Markdown("Get your optimized resume below")
|
578 |
-
|
579 |
-
output_files = gr.File(label="📂 Download Processed Files", interactive=True)
|
580 |
-
|
581 |
-
btn.click(process_resume, inputs=[api_key, job_url, company_name, resume_pdf], outputs=[output, output_files])
|
582 |
-
|
583 |
-
# Launch the UI
|
584 |
-
if __name__ == "__main__":
|
585 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
main.py
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
import requests
|
3 |
+
|
4 |
+
app = FastAPI()
|
5 |
+
|
6 |
+
OLLAMA_BASE_URL = "http://localhost:11434"
|
7 |
+
|
8 |
+
@app.get("/")
|
9 |
+
def read_root():
|
10 |
+
return {"message": "FastAPI with Ollama is running!"}
|
11 |
+
|
12 |
+
@app.get("/ask_ollama/")
|
13 |
+
def ask_ollama(question: str):
|
14 |
+
payload = {
|
15 |
+
"model": "mistral",
|
16 |
+
"messages": [
|
17 |
+
{"role": "system", "content": "You are an AI assistant."},
|
18 |
+
{"role": "user", "content": question}
|
19 |
+
]
|
20 |
+
}
|
21 |
+
response = requests.post(f"{OLLAMA_BASE_URL}/api/generate", json=payload)
|
22 |
+
return response.json()
|