Some checks failed
Profile Linker Docker Build / Build and push Docker image (push) Failing after 3s
- Integrated Cloudflare R2 for template storage and converted file management - Added Google Gemini AI for resume parsing and HTML generation - Created backend API endpoints for templates, conversion, and history - Refactored frontend to use real API instead of mock data - Fixed Docker networking issues (IPv6/IPv4) for R2 connectivity - Added resumeService.ts for frontend API integration - Updated Vite configuration for proper asset serving in Docker - Successfully tested with 13 templates from R2 bucket
136 lines
4.9 KiB
Python
136 lines
4.9 KiB
Python
"""
|
|
Gemini AI Service
|
|
Handles resume text extraction and HTML generation
|
|
"""
|
|
import google.generativeai as genai
|
|
from typing import Optional
|
|
import base64
|
|
from app.core.config import settings
|
|
|
|
|
|
class AIService:
|
|
"""Service for interacting with Google Gemini AI"""
|
|
|
|
def __init__(self):
|
|
"""Initialize Gemini AI with API key"""
|
|
if not settings.GEMINI_API_KEY:
|
|
raise ValueError("GEMINI_API_KEY not configured")
|
|
genai.configure(api_key=settings.GEMINI_API_KEY)
|
|
self.model = genai.GenerativeModel('gemini-2.0-flash-exp')
|
|
|
|
async def extract_text_from_resume(
|
|
self,
|
|
file_content: bytes,
|
|
mime_type: str
|
|
) -> Optional[str]:
|
|
"""
|
|
Extract text from resume file using Gemini Vision
|
|
Args:
|
|
file_content: File content as bytes
|
|
mime_type: MIME type of the file (application/pdf or application/vnd.openxmlformats-officedocument.wordprocessingml.document)
|
|
Returns: Extracted text or None if failed
|
|
"""
|
|
try:
|
|
# Convert bytes to base64
|
|
base64_data = base64.b64encode(file_content).decode('utf-8')
|
|
|
|
prompt = """Extract all text from this resume document.
|
|
Preserve the original structure, including sections, headings, bullet points, and line breaks, as plain text.
|
|
Focus on maintaining the hierarchical structure of the content."""
|
|
|
|
response = self.model.generate_content([
|
|
{
|
|
'mime_type': mime_type,
|
|
'data': base64_data
|
|
},
|
|
prompt
|
|
])
|
|
|
|
return response.text
|
|
except Exception as e:
|
|
print(f"Error extracting text from resume: {e}")
|
|
return None
|
|
|
|
async def generate_html_from_template(
|
|
self,
|
|
resume_text: str,
|
|
template_html: str
|
|
) -> Optional[str]:
|
|
"""
|
|
Generate formatted HTML by merging resume content with template
|
|
Args:
|
|
resume_text: Extracted resume text
|
|
template_html: HTML template content
|
|
Returns: Generated HTML or None if failed
|
|
"""
|
|
try:
|
|
prompt = self._build_generation_prompt(resume_text, template_html)
|
|
|
|
response = self.model.generate_content(prompt)
|
|
|
|
# Clean up the response (remove code blocks if present)
|
|
html_content = response.text.strip()
|
|
if html_content.startswith('```html'):
|
|
html_content = html_content[7:] # Remove ```html
|
|
if html_content.endswith('```'):
|
|
html_content = html_content[:-3] # Remove ```
|
|
|
|
return html_content.strip()
|
|
except Exception as e:
|
|
print(f"Error generating HTML: {e}")
|
|
return None
|
|
|
|
def _build_generation_prompt(self, resume_text: str, template_html: str) -> str:
|
|
"""Build the prompt for HTML generation"""
|
|
instructions = """### 🎯 EXACT TEMPLATE PRESERVATION INSTRUCTIONS:
|
|
|
|
**🚨 RULE #1: COPY TEMPLATE EXACTLY - NO STRUCTURAL CHANGES! 🚨**
|
|
**🚨 RULE #2: ONLY REPLACE PLACEHOLDER TEXT - NOTHING ELSE! 🚨**
|
|
|
|
**YOU ARE A FIND-AND-REPLACE TOOL - NOT A DESIGNER!**
|
|
|
|
**SIMPLE 3-STEP PROCESS:**
|
|
1. **COPY**: Take the entire HTML template (every character from <!DOCTYPE to </html>).
|
|
2. **FIND**: Locate placeholder text in the template (like "{{name}}", "John Doe", "Software Engineer", "2020-2023", etc.).
|
|
3. **REPLACE**: Replace ONLY that placeholder text with the user's corresponding information.
|
|
|
|
**WHAT TO REPLACE:**
|
|
- Names, contact info
|
|
- Job titles, companies, dates, descriptions
|
|
- Education details
|
|
- Skills lists
|
|
|
|
**WHAT TO NEVER CHANGE:**
|
|
- HTML tags (div, p, h1, etc.), CSS classes, IDs, or any inline styles.
|
|
- The overall HTML structure, layout, nesting, alignment, spacing, colors, and fonts.
|
|
|
|
**FOR EXTRA USER CONTENT:**
|
|
If the user's resume has sections not present in the template (e.g., 'Projects', 'Certifications'):
|
|
- Find a similar section in the template (e.g., 'Experience').
|
|
- Copy that section's HTML structure.
|
|
- Add it at a logical place (usually at the end) with the user's content.
|
|
- Reuse the same CSS classes and styling patterns to maintain consistency.
|
|
|
|
**CRITICAL:** Ensure ALL information from the user's resume is included in the final HTML. Do not omit any details.
|
|
"""
|
|
|
|
return f"""You are an expert HTML resume generator. Your task is to take the user's resume content and perfectly merge it into the provided company HTML template by acting as a precise find-and-replace tool.
|
|
|
|
**User's Resume Content:**
|
|
---
|
|
{resume_text}
|
|
---
|
|
|
|
**Company HTML Template:**
|
|
---
|
|
{template_html}
|
|
---
|
|
|
|
{instructions}
|
|
|
|
Now, generate the final, complete HTML file. Your entire output must be only the HTML code, starting with `<!DOCTYPE html>` and ending with `</html>`. Do not include any explanations or surrounding text."""
|
|
|
|
|
|
# Singleton instance
|
|
ai_service = AIService()
|