File size: 6,572 Bytes
82a5fd6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import io
import os
from typing import Optional

import docx
import fitz  # PyMuPDF
import modal
import google.generativeai as genai

app = modal.App(
    name="career-advisor-agent-gemini",
    image=modal.Image.debian_slim().pip_install(
        "google-generativeai",
        "pymupdf<1.24.0",
        "python-docx",
        "fastapi[standard]"
    ),
    secrets=[modal.Secret.from_name("my-google-secret")],
)

def parse_resume(file_content: bytes, filename: str) -> str:
    """Parses text from PDF or DOCX files."""
    text = ""
    try:
        if filename.lower().endswith(".pdf"):
            pdf_document = fitz.open(stream=file_content, filetype="pdf")
            for page in pdf_document:
                text += page.get_text()
            pdf_document.close()
        elif filename.lower().endswith(".docx"):
            doc = docx.Document(io.BytesIO(file_content))
            for para in doc.paragraphs:
                text += para.text + "\n"
    except Exception as e:
        print(f"Error parsing file {filename}: {e}")
        return f"Error parsing resume: {e}"
    return text


@app.function(timeout=120)
def get_career_advice(
    bio: str,
    interest: str,
    resume_text: Optional[str] = None,
):
    """The main agent function that queries the Gemini API."""
    try:
        # Configure the Gemini API client
        genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
        
        # List available models to debug
        try:
            models = genai.list_models()
            print("Available models:", [model.name for model in models])
        except Exception as e:
            print(f"Could not list models: {e}")

        # Initialize the Generative Model with explicit model name
        model = genai.GenerativeModel(model_name='gemini-2.0-flash')
        
        prompt = f"""
        You are an expert career advisor and coach. Your goal is to provide clear, actionable, and visually structured career advice.
        Format your entire response in Markdown, making it highly visual and structured.

        Here is the user's profile:
        Primary Interest Area: {interest}
        Bio: {bio}
        Resume Content: {resume_text if resume_text else "No resume provided."}

        Important Instructions:
        1. Focus PRIMARILY on the user's chosen interest area ({interest}). All advice should be specifically tailored to this field.
        2. If a resume is provided, analyze their current skills and experience to provide more personalized recommendations.
        3. Ensure all recommendations, courses, and projects are SPECIFICALLY relevant to {interest}.
        4. Use the resume content to identify transferable skills that would be valuable in {interest}.

        Provide a structured response with the following sections:

        ### πŸ’« Quick Summary
        A brief 2-3 sentence overview focusing specifically on their potential in {interest}, highlighting relevant existing skills and clear next steps.

        ### 🎯 Recommended Roles
        Present 3 recommended roles IN THE {interest} FIELD ONLY in this format:
        1. **Role Name** (Match Score: X/10)
            - Salary Range: $XX,XXX - $XXX,XXX
            - Key Requirements: req1, req2, req3
            - Why It Fits: Brief explanation based on their background

        ### πŸ“Š Skills Assessment
        Analyze their current skills relevant to {interest}. Use this format:
        ```skill-meter
        Current Skills Relevant to {interest}:
        Skill Name     [β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘] 50%
        ```
        Include 4-5 most relevant skills for {interest}, showing both strengths and areas for improvement.

        ### πŸ“š Learning Path
        Present a structured timeline SPECIFIC to {interest}:
        1. **Month 1-2: Foundation**
            - Course: "Course Name" (Platform) - Must be relevant to {interest}
            - Project: "Project idea" - Must be relevant to {interest}
            - Expected Outcome: "What they'll learn"

        ### πŸ’‘ Project Portfolio
        Present 3 project ideas AS CARDS that are SPECIFICALLY for {interest}:
        ```project-card
        Project: Name
        Difficulty: β­β­β­β˜†β˜†
        Duration: 2 weeks
        Skills: skill1, skill2
        Description: Brief description
        ```

        ### πŸŽ“ Certifications
        List 2-3 recommended certifications SPECIFIC to {interest}:
        - Certificate Name (Provider)
        - Difficulty Level: β­β­β­β˜†β˜†
        - Time Commitment: X-Y months
        - Cost Range: $XXX (details)

        Remember to:
        - Keep ALL advice focused on {interest}
        - Use their resume/background to make recommendations more relevant
        - Be specific and actionable in all recommendations
        - Use proper formatting for visual elements (skill bars, project cards, diagram)
        """

        try:
            response = model.generate_content(prompt)
            if response and hasattr(response, 'text'):
                return response.text
            else:
                return "Error: Received empty or invalid response from Gemini API"
        except Exception as e:
            print(f"Generate content error: {str(e)}")
            return f"Error generating content: {str(e)}"
            
    except KeyError:
        return "Error: GOOGLE_API_KEY not found in environment variables"
    except Exception as e:
        print(f"Unexpected error: {str(e)}")
        return f"An unexpected error occurred: {str(e)}"


# --- FIX 1: Use the new decorator name ---
@app.function()
@modal.fastapi_endpoint(method="POST")
def web_endpoint(data: dict):
    """
    This is the web endpoint that our Gradio app will call.
    """
    try:
        print("Received request with data:", data)
        bio = data.get("bio")
        interest = data.get("interest")
        resume_data = data.get("resume") 

        resume_text = None
        if resume_data:
            import base64
            file_content = base64.b64decode(resume_data["data"])
            resume_text = parse_resume(file_content, resume_data["name"])
            print("Parsed resume text length:", len(resume_text) if resume_text else 0)

        # Call the main agent function
        advice = get_career_advice.remote(bio, interest, resume_text)
        print("Generated advice length:", len(advice) if advice else 0)
        return {"advice": advice}
    except Exception as e:
        print(f"Error in web endpoint: {str(e)}")
        return {"advice": f"Error occurred in web endpoint: {str(e)}"}