Spaces:
Sleeping
Sleeping
| from PIL import Image | |
| from io import BytesIO | |
| import base64 | |
| import os | |
| import re | |
| from groq import Groq | |
| import gradio as gr | |
| # Read key from Hugging Face secrets or default fallback | |
| api_key = os.getenv("GROQ_API_KEY", "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") | |
| client = Groq(api_key=api_key) | |
| def encode_image(image_input): | |
| """Encode image (path, dict, or base64 data URI) to base64 string.""" | |
| try: | |
| # If Gradio passes a FileData dictionary | |
| if isinstance(image_input, dict): | |
| image_input = image_input.get("path", "") | |
| # If already a base64 Data URI string | |
| if isinstance(image_input, str) and image_input.startswith("data:image"): | |
| return image_input.split(",")[1] | |
| # Otherwise treat as a local file path and open via PIL | |
| image = Image.open(image_input).convert("RGB") | |
| base_height = 512 | |
| h_percent = (base_height / float(image.size[1])) | |
| w_size = int((float(image.size[0]) * float(h_percent))) | |
| image = image.resize((w_size, base_height), Image.LANCZOS) | |
| buffered = BytesIO() | |
| image.save(buffered, format="JPEG") | |
| return base64.b64encode(buffered.getvalue()).decode("utf-8") | |
| except Exception as e: | |
| print(f"Error encoding image: {e}") | |
| return None | |
| def feifeichat(image): | |
| try: | |
| if image is None: | |
| return "Please upload a photo" | |
| base64_image = encode_image(image) | |
| if not base64_image: | |
| return "Error processing image" | |
| messages = [ | |
| { | |
| "role": "user", | |
| "content": [ | |
| { | |
| "type": "text", | |
| "text": "Analyze the image and return strictly valid JSON format with keys 'title', 'description', 'meta_title' (max 60 chars), 'meta_description' (max 155 chars), 'tags' (array of 5-8 strings), 'style_breakdown' (object with keys: aesthetic, top, bottom, legwear, accessories), and 'faqs' (array of two objects with 'q' and 'a'). Follow these strict copywriting guidelines: 1. Description must be an elegant, descriptive paragraph (3-4 sentences) written like an alternative fashion lookbook. 2. Focus on physical aesthetics: mention terms like 'slender body', 'petite frame', 'youthful appeal', 'cute vibe', and 'sexy style' naturally. 3. Avoid literal camera actions: DO NOT say 'taking a selfie', 'mirror picture', or 'holding a phone'. Describe the pose as casual, relaxed, or playful. 4. Tags must be individual aesthetic search terms (e.g., ['egirl-aesthetic', 'thigh-highs', 'white-tights', 'alternative-fashion'])." | |
| }, | |
| { | |
| "type": "image_url", | |
| "image_url": { | |
| "url": f"data:image/jpeg;base64,{base64_image}" | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| response = client.chat.completions.create( | |
| model="qwen/qwen3.6-27b", | |
| messages=messages, | |
| response_format={"type": "json_object"}, | |
| temperature=0.7, | |
| stream=False | |
| ) | |
| raw_content = response.choices[0].message.content or "" | |
| # Strip Qwen reasoning/thinking blocks (<think>...</think>) | |
| cleaned = re.sub(r'<think>.*?</think>', '', raw_content, flags=re.DOTALL).strip() | |
| # Strip markdown fences if present | |
| cleaned = re.sub(r'^```(?:json)?\s*', '', cleaned) | |
| cleaned = re.sub(r'\s*```$', '', cleaned) | |
| return cleaned.strip() | |
| except Exception as e: | |
| print(f"Error: {e}") | |
| return f"An error occurred while processing your request: {e}" | |
| with gr.Blocks() as demo: | |
| gr.Markdown("Image To Flux Prompt") | |
| with gr.Tab(label="Image To Flux Prompt"): | |
| input_img = gr.Image(label="Input Picture", height=320, type="filepath") | |
| output_text = gr.Textbox(label="Flux Prompt") | |
| submit_btn = gr.Button(value="Submit") | |
| submit_btn.click( | |
| fn=feifeichat, | |
| inputs=input_img, | |
| outputs=output_text, | |
| api_name="feifeichat" | |
| ) | |
| # Disabling SSR mode resolves SvelteKit 405 Method Not Allowed errors on Gradio 5 | |
| demo.launch(ssr_mode=False) |