| import os |
| import io |
| import json |
| import random |
| import logging |
| from PIL import Image, ImageDraw |
| import numpy as np |
| import requests |
| import gradio as gr |
|
|
| |
| os.environ["OMP_NUM_THREADS"] = "1" |
| os.environ["MKL_NUM_THREADS"] = "1" |
|
|
| import torch |
| from torchvision import transforms |
|
|
| |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") |
| logger = logging.getLogger("CompleteProfileAI") |
|
|
| |
| DEVICE = "cpu" |
| BIREFNET_MODEL = None |
|
|
| try: |
| from transformers import AutoModelForImageSegmentation |
| logger.info("Initializing BiRefNet on CPU...") |
| |
| BIREFNET_MODEL = AutoModelForImageSegmentation.from_pretrained( |
| "ZhengPeng7/BiRefNet", |
| trust_remote_code=True |
| ) |
| BIREFNET_MODEL.to(DEVICE) |
| BIREFNET_MODEL.eval() |
| logger.info("BiRefNet successfully loaded and optimized for CPU.") |
| except Exception as e: |
| logger.error(f"Failed to load local BiRefNet model: {e}. Falling back to transparent bypass.") |
|
|
| |
| def create_gradient_backdrop(style="neutral_gray", size=(1024, 1024)): |
| """Generates beautiful, professional linear gradient canvases directly in-memory.""" |
| width, height = size |
| base = Image.new("RGB", size) |
| |
| if style == "office_blue": |
| color1 = (15, 32, 67) |
| color2 = (44, 83, 130) |
| elif style == "soft_teal": |
| color1 = (11, 40, 41) |
| color2 = (41, 108, 104) |
| else: |
| color1 = (25, 25, 25) |
| color2 = (85, 85, 85) |
| |
| for y in range(height): |
| ratio = y / height |
| r = int(color1[0] * (1 - ratio) + color2[0] * ratio) |
| g = int(color1 * (1 - ratio) + color2 * ratio) |
| b = int(color1 * (1 - ratio) + color2 * ratio) |
| |
| for x in range(width): |
| base.putpixel((x, y), (r, g, b)) |
| |
| return base |
|
|
| |
| def generate_fallback_banner(industry, color_palette_name): |
| """Generates a beautiful geometric abstract banner programmatically if the API is offline.""" |
| size = (1584, 396) |
| image = Image.new("RGB", size) |
| draw = ImageDraw.Draw(image) |
| |
| |
| palettes = { |
| "Corporate Blue": ((15, 32, 67), (44, 83, 130), (100, 149, 237)), |
| "Creative Teal": ((11, 40, 41), (41, 108, 104), (127, 255, 212)), |
| "Tech Slate": ((15, 15, 15), (50, 50, 60), (150, 150, 160)), |
| "Creative Amber": ((60, 30, 10), (120, 60, 20), (255, 191, 0)), |
| } |
| |
| colors = palettes.get(color_palette_name, palettes["Corporate Blue"]) |
| c1, c2, c3 = colors |
| |
| |
| for y in range(396): |
| ratio = y / 396 |
| r = int(c1[0] * (1 - ratio) + c2[0] * ratio) |
| g = int(c1 * (1 - ratio) + c2 * ratio) |
| b = int(c1 * (1 - ratio) + c2 * ratio) |
| draw.line([(0, y), (1584, y)], fill=(r, g, b)) |
| |
| |
| random.seed(hash(industry)) |
| |
| for _ in range(12): |
| x1 = random.randint(0, 1584) |
| y1 = random.randint(0, 396) |
| x2 = x1 + random.randint(100, 450) |
| y2 = y1 + random.randint(50, 300) |
| x3 = x1 + random.randint(-200, 200) |
| y3 = y1 + random.randint(-150, 150) |
| |
| |
| shape_img = Image.new("RGBA", size) |
| shape_draw = ImageDraw.Draw(shape_img) |
| fill_color = random.choice([c2, c3]) + (random.randint(25, 75),) |
| shape_draw.polygon([(x1, y1), (x2, y2), (x3, y3)], fill=fill_color) |
| image = Image.alpha_composite(image.convert("RGBA"), shape_img).convert("RGB") |
| |
| return image |
|
|
| |
| def optimize_linkedin_text(target_role, core_skills, achievement, style): |
| api_key = os.environ.get("OPENAI_API_KEY") |
| if not api_key: |
| logger.warning("OPENAI_API_KEY environment variable not configured.") |
| return ( |
| "OpenAI API Key is missing. Please add it to your Space Secrets in settings.", |
| "Please configure your OPENAI_API_KEY setting inside Hugging Face Secrets to activate the copywriter AI.", |
| ["No Key Added"] |
| ) |
|
|
| |
| from openai import OpenAI |
| client = OpenAI(api_key=api_key) |
|
|
| system_prompt = """You are the "AI Career Journalist," an elite Executive Recruiter and world-class LinkedIn Copywriter. Your mission is to interview job seekers and translate their messy, unstructured, conversational raw inputs into compelling, high-converting, and keyword-optimized LinkedIn profiles. |
| |
| Your copywriting philosophy: |
| 1. Cut the Fluff: Avoid generic corporate corporate-speak. Be concrete. |
| 2. Quantify Impact: Turn passive duties into active, measurable achievements. |
| 3. Keep it Human: Write in a natural, professional first-person tone ("I am...", "I lead...") that sounds like a confident professional, not an LLM. |
| |
| You must strictly output your response in valid JSON format matching the schema requested. Do not write conversational preambles or postscripts.""" |
|
|
| task_prompt = f"""Transform the following conversational user inputs into a polished LinkedIn Headline, "About" Summary, and Keyword List. |
| |
| ### Few-Shot Example: |
| - USER INPUTS: |
| * Target Role: Junior Software Engineer |
| * Core Skills: Python, React, PostgreSQL, Git |
| * Major Achievement: Built a campus tutoring app that was used by 300 students to schedule sessions. |
| * Working Style: Collaborative, analytical, eager to solve complex logic. |
| - AI OUTPUT JSON: |
| {{ |
| "headline": "Junior Software Engineer | Python & React | Building Impact-Driven Web Solutions", |
| "summary": "I am a software engineer focused on building highly functional, user-centric web applications. My passion lies in translating complex logic into clean, performant code.\\n\\nRecently, I developed a campus tutoring scheduler using Python and React, which successfully streamlined session booking for over 300 active student users. I thrive in collaborative environments where continuous learning and analytical problem-solving are valued.\\n\\nSpecialties: Python, JavaScript (React), SQL (PostgreSQL), Git, API Integration, and Agile Methodologies.", |
| "extracted_keywords": ["Software Engineering", "Full-Stack Development", "Python", "React.js", "PostgreSQL", "Database Design", "Agile Methodologies"], |
| "status": "success", |
| "error_message": "" |
| }} |
| |
| ### Live Task: |
| - USER INPUTS: |
| * Target Role: {target_role} |
| * Core Skills: {core_skills} |
| * Major Achievement: {achievement} |
| * Working Style: {style} |
| |
| Generate the JSON response following the exact schema shown in the examples. Your output MUST be pure JSON with no markdown wrapping (i.e. no ```json).""" |
|
|
| try: |
| response = client.chat.completions.create( |
| model="gpt-4o-mini", |
| response_format={ "type": "json_object" }, |
| messages=[ |
| {"role": "system", "content": system_prompt}, |
| {"role": "user", "content": task_prompt} |
| ], |
| timeout=30 |
| ) |
| content = response.choices[0].message.content |
| data = json.loads(content) |
| |
| if data.get("status") == "error": |
| return "", data.get("error_message", "Error compiling data."), [] |
| |
| return data["headline"], data["summary"], data["extracted_keywords"] |
| except Exception as e: |
| logger.error(f"Error during OpenAI copy generation: {e}") |
| return ( |
| f"{target_role} | {core_skills.split(',')[0] if core_skills else 'Professional'}", |
| f"An error occurred while calling the OpenAI service: {e}. Please ensure your API secrets are configured correctly.", |
| [s.strip() for s in core_skills.split(",")] if core_skills else [] |
| ) |
|
|
| |
| def process_headshot(image, bg_type, gradient_preset, solid_color): |
| if image is None: |
| return None |
| |
| |
| max_size = 1024 |
| w, h = image.size |
| if w > max_size or h > max_size: |
| ratio = min(max_size / w, max_size / h) |
| new_size = (int(w * ratio), int(h * ratio)) |
| image = image.resize(new_size, Image.Resampling.LANCZOS) |
| logger.info(f"Resized input image to safe processing boundaries: {new_size}") |
| |
| orig_w, orig_h = image.size |
| |
| |
| if BIREFNET_MODEL is None: |
| |
| logger.warning("BiRefNet uninitialized. Bypassing background extraction.") |
| cut_subject = image.convert("RGBA") |
| else: |
| try: |
| logger.info("Starting BiRefNet background segmentation loop...") |
| |
| transform_image = transforms.Compose([ |
| transforms.Resize((1024, 1024)), |
| transforms.ToTensor(), |
| transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) |
| ]) |
| img_rgb = image.convert("RGB") |
| input_tensor = transform_image(img_rgb).unsqueeze(0).to(DEVICE) |
| |
| with torch.no_grad(): |
| outputs = BIREFNET_MODEL(input_tensor) |
| |
| if hasattr(outputs, "logits"): |
| pred = outputs.logits[-1] if isinstance(outputs.logits, list) else outputs.logits |
| elif isinstance(outputs, (list, tuple)): |
| pred = outputs[-1] |
| else: |
| pred = outputs |
| |
| if len(pred.shape) == 4: |
| pred = pred.squeeze(0).squeeze(0) |
| elif len(pred.shape) == 3: |
| pred = pred.squeeze(0) |
| |
| |
| pred = torch.sigmoid(pred).cpu().numpy() |
| |
| |
| mask = Image.fromarray((pred * 255).astype(np.uint8)).resize((orig_w, orig_h), Image.Resampling.BILINEAR) |
| cut_subject = image.convert("RGBA") |
| cut_subject.putalpha(mask) |
| logger.info("Successfully extracted headshot foreground subject.") |
| except Exception as e: |
| logger.error(f"Error during BiRefNet execution: {e}") |
| cut_subject = image.convert("RGBA") |
| |
| |
| if bg_type == "Solid Color": |
| hex_color = solid_color.lstrip('#') |
| rgb_color = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) |
| background = Image.new("RGB" |
|
|