Spaces:
Sleeping
Sleeping
| import os | |
| import gradio as gr | |
| import arabic_reshaper | |
| from bidi.algorithm import get_display | |
| from fpdf import FPDF | |
| from llama_index.llms.groq import Groq | |
| import asyncio | |
| import traceback | |
| print("[startup] CV Agent initializing...") | |
| GROQ_KEY = os.environ.get("GROQ_API_KEY") | |
| print(f"[startup] GROQ_API_KEY set: {bool(GROQ_KEY)}") | |
| llm = Groq(model="llama-3.3-70b-versatile", api_key=GROQ_KEY or "") | |
| FONT_DIR = "Fonts" | |
| AR_FONTS = {"Arial": {"regular": f"{FONT_DIR}/ arfonts-arial.ttf", "bold": f"{FONT_DIR}/ arfonts-arial-bold.ttf"}} | |
| EN_FONTS = {"Arial": {"regular": f"{FONT_DIR}/ arial.ttf", "bold": f"{FONT_DIR}/ G_ari_bd.TTF"}} | |
| if not os.path.exists(FONT_DIR): | |
| try: | |
| os.makedirs(FONT_DIR, exist_ok=True) | |
| print(f"[fonts] Created font directory: {FONT_DIR}") | |
| except Exception as e: | |
| print(f"[fonts] Failed to create font dir: {e}") | |
| RESUME_STYLES = [ | |
| "Europass - 🇪🇺 Formal European format", | |
| "Harvard - 🎓 Academic with impact", | |
| "US Resume - 🇺🇸 One-page concise resume", | |
| "UK CV - 🇬🇧 Two-page traditional format", | |
| "Functional Resume - 🧠 Skills-focused layout", | |
| "Academic CV - 📚 For researchers and scholars" | |
| ] | |
| RESUME_STYLE_GUIDELINES = { | |
| "Europass - 🇪🇺 Formal European format": | |
| "الأقسام : الخبرة العملية، التعليم، المهارات، اللغات، معلومات إضافية.\n" | |
| "الخبرة العملية: لكل وظيفة، اذكر الشركة، المسمى الوظيفي، الفترة، المهام الرئيسية والإنجازات.\n" | |
| "التعليم: لكل مؤهل، اذكر المؤسسة، الدرجة، التخصص، سنة التخرج.\n" | |
| "المهارات: قسمين (التقنية والشخصية) مع النقاط التفصيلية.\n" | |
| "اللغات: مع تحديد المستوى حسب الإطار الأوروبي (A1-C2).\n" | |
| "معلومات إضافية: الشهادات، المشاريع، المنشورات، الاهتمامات.", | |
| "Harvard - 🎓 Academic with impact": | |
| "الأقسام : ملخص مهني، الخبرة العملية، التعليم، المهارات، الإنجازات، المنشورات.\n" | |
| "ملخص مهني: 3-4 جمل مركزة تلخص الخبرات والإنجازات الرئيسية.\n" | |
| "الخبرة العملية: ركز على الإنجازات والأثر مع استخدام أفعال الحركة وأرقام قابلة للقياس.\n" | |
| "التعليم: شامل مع ذكر المشاريع البحثية أو الرسائل إذا كانت ذات صلة.\n" | |
| "الإنجازات: قسم منفصل للإنجازات الأكاديمية والمهنية البارزة.\n" | |
| "المنشورات: قائمة كاملة بالمنشورات الأكاديمية أو المقالات.", | |
| "US Resume - 🇺🇸 One-page concise resume": | |
| "الأقسام : ملخص، الخبرة العملية، التعليم، المهارات.\n" | |
| "ملخص: 2-3 جمل قصيرة في الأعلى تلخص المؤهلات الرئيسية.\n" | |
| "الخبرة العملية: فقط الوظائف الأكثر صلة بالوظيفة المستهدفة، مع التركيز على الإنجازات.\n" | |
| "التعليم: مختصر مع ذكر المؤهل والمؤسسة وسنة التخرج فقط.\n" | |
| "المهارات: قائمة نقطية مختصرة بالمهارات الأساسية فقط.\n" | |
| "المبدأ الأساسي: التركيز والاختصار في صفحة واحدة.", | |
| "UK CV - 🇬🇧 Two-page traditional format": | |
| "الأقسام المطلوبة: الملف الشخصي، التاريخ الوظيفي، التعليم، المهارات، الاهتمامات، المراجع.\n" | |
| "الملف الشخصي: فقرة قصيرة تقدم المرشح وأهدافه المهنية.\n" | |
| "التاريخ الوظيفي: مفصل مع ذكر جميع الوظائف والمهام والإنجازات.\n" | |
| "التعليم: شامل مع ذكر المواد أو المشاريع البارزة.\n" | |
| "المراجع: أسماء ووظائف الأشخاص الذين يمكن التواصل معهم للتوصية.\n" | |
| "المبدأ الأساسي: الشمولية والتفصيل في صفحتين كحد أقصى.", | |
| "Functional Resume - 🧠 Skills-focused layout": | |
| "الأقسام المطلوبة: ملخص المهارات، الإنجازات الرئيسية، الخبرة العملية، التعليم.\n" | |
| "ملخص المهارات: قسم مفصل ينقسم إلى مهارات تقنية وشخصية وقيادية.\n" | |
| "الإنجازات الرئيسية: قائمة بالإنجازات البارزة عبر الخبرات المختلفة.\n" | |
| "الخبرة العملية: مختصر مع ذكر الشركات والفترات فقط دون تفاصيل.\n" | |
| "التعليم: مختصر مع ذكر المؤهل الأساسي فقط.\n" | |
| "المبدأ الأساسي: التركيز على المهارات والقدرات بدلاً من التسلسل الزمني.", | |
| "Academic CV - 📚 For researchers and scholars": | |
| "الأقسام المطلوبة: التعليم، الخبرة البحثية، المنشورات، المؤتمرات، الجوائز، المهارات.\n" | |
| "التعليم: شامل مع ذكر المشرفين والرسائل والتقديرات.\n" | |
| "الخبرة البحثية: المشاريع البحثية، المنح، وفرق البحث.\n" | |
| "المنشورات: مفصلة مع ذكر جميع المنشورات الأكاديمية مرتبة زمنياً.\n" | |
| "المؤتمرات: المشاركات في المؤتمرات العلمية والعروض التقديمية.\n" | |
| "الجوائز: جميع الجوائز الأكاديمية والمنح الدراسية.\n" | |
| "المبدأ الأساسي: الشمولية بدون حدود للصفحات مع التركيز على الإنجازات الأكاديمية." | |
| } | |
| RESUME_QUESTIONS_AR = [ | |
| "ما اسمك الكامل؟", | |
| "ما رقم هاتفك للتواصل؟", | |
| "ما عنوان بريدك الإلكتروني؟", | |
| "في أي مدينة ودولة تقيم حاليًا؟", | |
| "ما هو تاريخ ميلادك؟ (اختياري حسب الدولة)", | |
| "ما جنسيتك؟ (اختياري حسب الدولة)", | |
| "ما حالتك الاجتماعية؟ (متزوج، أعزب... إلخ) (اختياري حسب الدولة)", | |
| "هل لديك حسابات مهنية مثل LinkedIn أو GitHub أو موقع شخصي؟ (اكتب الروابط إن وجدت)", | |
| "ما نوع الوظيفة أو المجال الذي تستهدفه حاليًا؟", | |
| "صف نفسك أو هدفك المهني في سطرين إلى ثلاثة أسطر", | |
| "ما هي الوظائف أو الخبرات السابقة التي لديك؟", | |
| "لكل وظيفة سابقة:\n\t\t- ما اسم الشركة؟\n\t\t- ما المسمى الوظيفي؟\n\t\t- ما الفترة الزمنية (من – إلى)؟\n\t\t- ما هي المهام التي كنت تقوم بها؟\n\t\t- ما هي أهم إنجازاتك في هذه الوظيفة؟", | |
| "هل سبق لك العمل بشكل حر (Freelance) أو تطوعي؟ اذكر التفاصيل", | |
| "ما هي مؤهلاتك الدراسية؟", | |
| "لكل مؤهل دراسي:\n\t\t- اسم المؤسسة التعليمية\n\t\t- الدرجة (بكالوريوس، دبلوم...)\n\t\t- التخصص\n\t\t- سنة التخرج\n\t\t- التقدير العام (إن وجد)", | |
| "ما هي المهارات التقنية التي تتقنها؟ (برامج، أدوات، أنظمة...)", | |
| "ما هي المهارات الشخصية التي تميزك؟ (مثل: القيادة، العمل الجماعي، التواصل...)", | |
| "ما هي اللغات التي تتقنها؟", | |
| "ما مستواك في كل لغة؟ (أساسي، متوسط، جيد، ممتاز – أو A1 إلى C2)", | |
| "هل حصلت على أي شهادات أو دورات تدريبية؟", | |
| "لكل دورة أو شهادة:\n\t\t- اسم الدورة\n\t\t- الجهة المانحة\n\t\t- تاريخ الحصول عليها", | |
| "هل لديك مشاريع عملية أو شخصية مهمة شاركت بها؟ اذكر التفاصيل", | |
| "هل لديك جوائز، تكريمات، أو إنجازات خاصة تود ذكرها؟", | |
| "هل لديك عضويات في جمعيات مهنية أو علمية؟", | |
| "هل سبق أن شاركت في مؤتمرات أو أبحاث أو منشورات؟ اذكر التفاصيل", | |
| "هل ترغب بإدراج مراجع (أشخاص يمكن التواصل معهم لتوصية وظيفية)؟ اذكر الاسم، الوظيفة، جهة العمل، ورقم التواصل أو البريد (اختياري)", | |
| "ما هي اهتماماتك أو هواياتك المرتبطة بالمجال المهني أو العام؟", | |
| "هل لديك اي معلومات او ملاحظات اضافية تود اضافتها الى السيرة الذاتية" | |
| ] | |
| RESUME_QUESTIONS_EN = [ | |
| "Full Name?", | |
| "Phone Number?", | |
| "Email Address?", | |
| "Current City and Country?", | |
| "Date of Birth? (Optional depending on country)", | |
| "Nationality? (Optional depending on country)", | |
| "Marital Status? (Married, Single, etc.) (Optional depending on country)", | |
| "Do you have professional accounts (LinkedIn, GitHub, personal website)? (Provide links if available)", | |
| "What type of job or field are you currently targeting?", | |
| "Describe yourself or your career objective in 2-3 lines", | |
| "What previous jobs or work experiences do you have?", | |
| "For each previous job:\n\t\t- Company name\n\t\t- Job title\n\t\t- Time period (from - to)\n\t\t- What were your responsibilities?\n\t\t- Key achievements in this role?", | |
| "Have you done freelance or volunteer work? Provide details", | |
| "What are your educational qualifications?", | |
| "For each qualification:\n\t\t- Institution name\n\t\t- Degree (Bachelor, Diploma, etc.)\n\t\t- Major\n\t\t- Graduation year\n\t\t- Grade (if available)", | |
| "What technical skills do you master? (Software, tools, systems...)", | |
| "What soft skills distinguish you? (e.g., leadership, teamwork, communication...)", | |
| "What languages do you speak?", | |
| "What is your proficiency level in each language? (Basic, Intermediate, Good, Excellent - or A1 to C2)", | |
| "Have you obtained any certificates or training courses?", | |
| "For each course or certificate:\n\t\t- Course name\n\t\t- Issuing authority\n\t\t- Date obtained", | |
| "Do you have any important practical or personal projects you participated in? Provide details", | |
| "Do you have any awards, honors, or special achievements you want to mention?", | |
| "Are you a member of any professional or scientific associations?", | |
| "Have you participated in conferences, research, or publications? Provide details", | |
| "Do you wish to include references? (Name, job title, company, and contact number or email - optional)", | |
| "What are your interests or hobbies related to the professional field or in general?", | |
| "Do you have any additional information or notes for the resume?" | |
| ] | |
| COVER_QUESTIONS_AR = [ | |
| "ما اسم الشركة أو الجهة التي تستهدفها برسالة التغطية؟", | |
| "ما اسم الوظيفة التي تتقدم لها برسالة التغطية؟", | |
| "هل تعرف اسم الشخص المسؤول عن التوظيف؟ (إن وجد)", | |
| "لماذا ترغب في العمل بهذه الشركة تحديدًا؟", | |
| "ما الذي يجعلك مرشحًا مثاليًا لهذه الوظيفة؟", | |
| "ما القيمة التي يمكنك إضافتها إلى الفريق أو الشركة؟", | |
| "هل هناك تجربة أو مشروع سابق ترى أنه مناسب لذكره في رسالة التغطية؟ وضّح السبب.", | |
| "هل سبق لك التقديم لهذه الشركة أو الوظيفة سابقًا؟ هل خضعت لمقابلة؟ (إن وجدت)", | |
| "هل تفضل أن تكون نغمة رسالة التغطية رسمية، مهنية مرنة، أم بأسلوب شخصي قوي؟", | |
| "هل ترغب في تضمين سبب تركك لوظيفتك السابقة (إن وجد) ضمن الرسالة؟ (اختياري)" | |
| ] | |
| COVER_QUESTIONS_EN = [ | |
| "What is the name of the company or organization you are targeting with the cover letter?", | |
| "What is the job title you are applying for?", | |
| "Do you know the name of the hiring manager? (if available)", | |
| "Why do you want to work for this company specifically?", | |
| "What makes you an ideal candidate for this job?", | |
| "What value can you add to the team or company?", | |
| "Is there a previous experience or project you think is relevant to mention in the cover letter? Explain why.", | |
| "Have you applied to this company or job before? Did you have an interview? (if applicable)", | |
| "Do you prefer the tone of the cover letter to be formal, flexible professional, or strong personal style?", | |
| "Do you wish to include the reason for leaving your previous job (if any) in the letter? (optional)" | |
| ] | |
| def reshape_ar(text): | |
| try: | |
| return get_display(arabic_reshaper.reshape(text)) | |
| except Exception as e: | |
| print(f"[reshape_ar] error: {e}") | |
| return text | |
| def parse_resume_sections(text): | |
| sections = {} | |
| if not text: | |
| print("[parse_resume_sections] empty text") | |
| return sections | |
| lines = text.strip().splitlines() | |
| current = None | |
| buffer = [] | |
| i = 0 | |
| while i < len(lines): | |
| line = lines[i].strip() | |
| if line == "[SECTION_TITLE]": | |
| i += 1 | |
| if i < len(lines): | |
| current = lines[i].strip() | |
| buffer = [] | |
| elif line == "[SECTION_BODY]": | |
| i += 1 | |
| while i < len(lines) and lines[i].strip() != "[SECTION_TITLE]": | |
| buffer.append(lines[i]) | |
| i += 1 | |
| if current: | |
| sections[current] = "\n".join(buffer).strip() | |
| continue | |
| else: | |
| i += 1 | |
| print(f"[parse_resume_sections] found sections: {list(sections.keys())}") | |
| return sections | |
| def split_text(text, max_width, pdf, font_name, font_size): | |
| try: | |
| pdf.set_font(font_name, "", font_size) | |
| except Exception: | |
| pass | |
| lines = [] | |
| words = text.split() | |
| current_line = [] | |
| current_width = 0 | |
| for word in words: | |
| word_width = pdf.get_string_width(word + ' ') | |
| if current_width + word_width < max_width: | |
| current_line.append(word) | |
| current_width += word_width | |
| else: | |
| lines.append(' '.join(current_line)) | |
| current_line = [word] | |
| current_width = word_width | |
| if current_line: | |
| lines.append(' '.join(current_line)) | |
| return lines | |
| class PDF(FPDF): | |
| def __init__(self, font_name, font_paths, lang): | |
| super().__init__() | |
| self.lang = lang | |
| self.font_name = font_name | |
| try: | |
| if font_paths.get("regular") and os.path.exists(font_paths["regular"]): | |
| self.add_font(font_name, "", font_paths["regular"], uni=True) | |
| if font_paths.get("bold") and os.path.exists(font_paths["bold"]): | |
| self.add_font(font_name, "B", font_paths["bold"], uni=True) | |
| print(f"[PDF] loaded fonts for {font_name}") | |
| except Exception as e: | |
| print(f"[PDF] font load error: {e}") | |
| self.font_name = "Arial" | |
| self.set_margins(left=5, top=15, right=5) | |
| self.set_auto_page_break(auto=True, margin=15) | |
| def header_custom(self, text): | |
| try: | |
| # استخدم الخط العربي إذا كانت اللغة عربية | |
| if self.lang == 'ar': | |
| self.set_font(self.font_name, 'B', 24) | |
| else: | |
| self.set_font('Helvetica', 'B', 24) | |
| except Exception as e: | |
| print(f"[PDF.header_custom] font error: {e}") | |
| self.set_font('Helvetica', 'B', 24) | |
| self.set_text_color(0, 0, 0) | |
| align = 'R' if self.lang == 'ar' else 'C' | |
| display = reshape_ar(text) if self.lang == 'ar' else (text or "") | |
| try: | |
| self.cell(0, 10, display, ln=1, align=align) | |
| except Exception as e: | |
| print(f"[PDF.header_custom] cell error: {e}") | |
| self.ln(3) | |
| def contact_info(self, lines): | |
| try: | |
| self.set_font(self.font_name, '', 10) | |
| except Exception: | |
| self.set_font('Arial', '', 10) | |
| self.set_text_color(0, 0, 0) | |
| align = 'R' if self.lang == 'ar' else 'C' | |
| for line in lines: | |
| line = (line or "").strip() | |
| if not line: | |
| continue | |
| try: | |
| display = reshape_ar(line) if self.lang == 'ar' else line | |
| self.cell(0, 5, display, ln=1, align=align) | |
| except Exception as e: | |
| print(f"[PDF.contact_info] error: {e}") | |
| self.ln(5) | |
| def section_title(self, title): | |
| try: | |
| if self.lang == 'ar': | |
| self.set_font(self.font_name, 'B', 14) | |
| else: | |
| self.set_font('Helvetica', 'B', 14) | |
| except Exception as e: | |
| print(f"[PDF.section_title] font error: {e}") | |
| self.set_font('Helvetica', 'B', 14) | |
| self.set_text_color(0, 0, 0) | |
| align = 'R' if self.lang == 'ar' else 'L' | |
| display = reshape_ar(title) if self.lang == 'ar' else (title or "") | |
| self.cell(0, 8, display, ln=1, align=align) | |
| y = self.get_y() | |
| self.set_draw_color(0, 0, 0) | |
| self.set_line_width(0.2) | |
| self.line(10, y, self.w - 10, y) | |
| self.ln(5) | |
| def section_body(self, body): | |
| try: | |
| if self.lang == 'ar': | |
| self.set_font(self.font_name, '', 10) | |
| else: | |
| self.set_font('Helvetica', '', 10) | |
| except Exception as e: | |
| print(f"[PDF.section_body] font error: {e}") | |
| self.set_font('Helvetica', '', 10) | |
| self.set_text_color(0, 0, 0) | |
| align = 'R' if self.lang == 'ar' else 'L' | |
| if self.lang == 'ar': | |
| body = reshape_ar(body) | |
| self.multi_cell(0, 5, body, align=align) | |
| self.ln(4) | |
| def _process_arabic(self, text): | |
| try: | |
| reshaped = arabic_reshaper.reshape(text) | |
| return get_display(reshaped) | |
| except Exception as e: | |
| print(f"[PDF._process_arabic] error: {e}") | |
| return text | |
| def generate_pdf_simple(text, filename, font_name, font_paths, lang, user_data): | |
| print(f"[debug] generate_pdf_simple called with filename={filename}, lang={lang}") | |
| pdf = PDF(font_name, font_paths, lang) | |
| pdf.add_page() | |
| sections = parse_resume_sections(text) | |
| print(f"[debug] Total sections parsed: {len(sections)}") | |
| # حذف الأقسام الفارغة | |
| clean_sections = {} | |
| for title, body in sections.items(): | |
| if body and body.strip() not in ["", "(No information provided)", "N/A", "لا توجد معلومات", "Please provide"]: | |
| clean_sections[title] = body.strip() | |
| else: | |
| print(f"[debug] Skipping empty section: {title}") | |
| # العنوان الرئيسي | |
| name = "" | |
| name_keys = ["الاسم", "Name", "Personal Information", "الاسم الكامل"] | |
| for key in name_keys: | |
| if key in clean_sections: | |
| name_lines = clean_sections[key].splitlines() | |
| if name_lines: | |
| name = name_lines[0].strip() | |
| del clean_sections[key] | |
| break | |
| if not name and user_data: | |
| name = user_data[0] if len(user_data) > 0 else "" | |
| # رأس الصفحة | |
| pdf.header_custom(name) | |
| # طباعة الأقسام الباقية فقط | |
| for title, body in clean_sections.items(): | |
| if not body or body.strip() in ["", "(No information provided)"]: | |
| print(f"[debug] Skipping section: {title}") | |
| continue | |
| pdf.section_title(title) | |
| pdf.section_body(body) | |
| try: | |
| pdf.output(filename) | |
| print(f"[debug] PDF successfully saved: {filename}") | |
| except Exception as e: | |
| print(f"[error] PDF generation failed: {e}") | |
| return filename | |
| async def generate_content(prompt, max_tokens=2048, temperature=0.3): | |
| print(f"[generate_content] prompt length={len(prompt)}") | |
| try: | |
| resp = await llm.acomplete(prompt=prompt) | |
| text = getattr(resp, "text", "") | |
| print(f"[generate_content] received text length={len(text)}") | |
| return text.strip() | |
| except Exception as e: | |
| print(f"[generate_content] LLM call error: {e}") | |
| print(traceback.format_exc()) | |
| return "" | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| gr.Markdown(" CV Agent/منشئ السيرة الذاتية الذكي") | |
| lang_dd = gr.Dropdown(["العربية", "English"], label="🌐 اختر اللغة/ choose a language") | |
| start_btn = gr.Button("ابدأ / Start") | |
| resume_boxes = [gr.Textbox(label=f"Q{i+1}", visible=False) for i in range(len(RESUME_QUESTIONS_AR))] | |
| with gr.Row(visible=False) as main_interface: | |
| with gr.Column(scale=2): | |
| for tb in resume_boxes: | |
| tb.visible = True | |
| output_lang_choice_dd = gr.Dropdown(choices=["العربية", "English"], label="لغة السيرة الذاتية/output language") | |
| resume_style_dd = gr.Dropdown(choices=RESUME_STYLES, label="📚 نمط السيرة الذاتية/ CV format ") | |
| submit_btn = gr.Button("توليد السيرة الذاتية✨/✨generate CV") | |
| with gr.Row(visible=False) as output_row: | |
| file_output = gr.File(label="📄 السيرة الذاتية/ your CV") | |
| cv_preview = gr.Textbox(label="📄 المعاينة", lines=10) | |
| cover_preview = gr.Textbox(label="📄 معاينة رسالة التغطية", lines=10, visible=False) | |
| def update_questions(lang): | |
| print(f"[update_questions] lang={lang}") | |
| questions = RESUME_QUESTIONS_AR if lang == "العربية" else RESUME_QUESTIONS_EN | |
| updates = [gr.update(label=q, visible=True) for q in questions] | |
| return updates | |
| lang_dd.change(fn=update_questions, inputs=[lang_dd], outputs=resume_boxes) | |
| def show_questions(lang): | |
| print(f"[show_questions] lang={lang}") | |
| questions = RESUME_QUESTIONS_AR if lang == "العربية" else RESUME_QUESTIONS_EN | |
| updates = [gr.update(visible=True)] | |
| updates += [gr.update(label=q, visible=True) for q in questions] | |
| updates += [gr.update(value=lang), gr.update(value=RESUME_STYLES[0]), gr.update(value="توليد السيرة الذاتية✨/✨generate CV")] | |
| return updates | |
| start_btn.click(fn=show_questions, inputs=[lang_dd], outputs=[main_interface, *resume_boxes, output_lang_choice_dd, resume_style_dd, submit_btn]) | |
| async def process_all(ui_lang, output_lang, resume_style, *answers): | |
| print(f"[process_all] called ui_lang={ui_lang} output_lang={output_lang} resume_style={resume_style}") | |
| try: | |
| questions = RESUME_QUESTIONS_AR if ui_lang == "العربية" else RESUME_QUESTIONS_EN | |
| data = "\n".join(f"- {q}: {a}" for q, a in zip(questions, answers) if a and str(a).strip()) | |
| style_guideline = RESUME_STYLE_GUIDELINES.get(resume_style, "") | |
| prompt = f""" | |
| You are a professional resume writer. | |
| Write a clean, well-structured resume in {output_lang.upper()}. | |
| add [SECTION_TITLE] Before each section title. | |
| Then write the section title (e.g., Education, Work Experience...). | |
| Then on a new line, write [SECTION_BODY] and the content. | |
| Include a section for "Name" with the full name only. | |
| Include a section for "Contact Information" with phone, email, location. | |
| Do not include notes or advices at the end of resume | |
| IMPORTANT: | |
| - ignore any section that has no information provided by the user and add to the body "(No information provided)" | |
| - Do NOT include any extra information. | |
| Return only the resume by the provided informations | |
| - Rewrite any poorly written sentences in a professional manner. | |
| - Do not copy the user's words exactly; improve and rephrase them professionally. | |
| - Follow these style guidelines: {style_guideline} | |
| - the technical skill and personal skill must be in two different sections. | |
| - when the chosen output language is English, translate the information into English. | |
| - write understandable and readable sentences. | |
| Client Information: | |
| {data} | |
| """ | |
| print(f"[process_all] prompt length={len(prompt)}") | |
| cv_text = await generate_content(prompt) | |
| print(f"[process_all] cv_text length={len(cv_text)}") | |
| fonts = AR_FONTS if output_lang == "العربية" else EN_FONTS | |
| pdf_path = generate_pdf_simple(cv_text, "resume.pdf", "Arial", fonts["Arial"], "ar" if output_lang == "العربية" else "en", answers) | |
| print(f"[process_all] returning pdf_path={pdf_path}") | |
| return gr.update(visible=True), pdf_path | |
| except Exception as e: | |
| print(f"[process_all] exception: {e}") | |
| print(traceback.format_exc()) | |
| return gr.update(visible=True), None | |
| submit_btn.click(fn=process_all, inputs=[lang_dd, output_lang_choice_dd, resume_style_dd] + resume_boxes, outputs=[output_row, file_output]) | |
| print("[startup] Launching Gradio demo...") | |
| demo.launch(debug=True, share=True) | |