Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import base64 | |
| import io | |
| from PIL import Image | |
| from huggingface_hub import InferenceClient | |
| # ========================================== | |
| # RECIPE DIGITALIZER PIPELINE (Image -> JSON) | |
| # ========================================== | |
| class RecipeDigitalizerPipeline: | |
| def __init__(self): | |
| print("Connecting to Hugging Face API (Qwen Mode)...") | |
| self.token = os.getenv("HF_TOKEN") | |
| # This targets the larger model which uses a DIFFERENT server | |
| self.model_id = "Qwen/Qwen2.5-VL-72B-Instruct" | |
| self.client = InferenceClient(token=self.token) | |
| def compress_image(self, image_path): | |
| """ | |
| Resizes the image so it doesn't crash the Free API. | |
| """ | |
| with Image.open(image_path) as img: | |
| if img.mode != 'RGB': | |
| img = img.convert('RGB') | |
| # Resize: Free API often rejects images larger than 1024x1024 | |
| max_size = 1024 | |
| if max(img.size) > max_size: | |
| img.thumbnail((max_size, max_size)) | |
| # Save to memory as JPEG | |
| buffer = io.BytesIO() | |
| img.save(buffer, format="JPEG", quality=70) # Quality 70 is enough for text | |
| # Convert to Base64 | |
| encoded_string = base64.b64encode(buffer.getvalue()).decode('utf-8') | |
| return f"data:image/jpeg;base64,{encoded_string}" | |
| def run_pipeline(self, image_path): | |
| prompt = """Extract the recipe from this image. | |
| Output strictly valid JSON with keys: title, ingredients (list), instructions (list), cuisine_type, difficulty. | |
| Do not include markdown formatting like ```json, just the raw JSON.""" | |
| try: | |
| # 1. Compress Image (Solves 400 Bad Request) | |
| image_url = self.compress_image(image_path) | |
| # 2. Call Qwen API | |
| response = self.client.chat.completions.create( | |
| model=self.model_id, | |
| messages=[ | |
| { | |
| "role": "user", | |
| "content": [ | |
| { | |
| "type": "image_url", | |
| "image_url": {"url": image_url} | |
| }, | |
| {"type": "text", "text": prompt} | |
| ] | |
| } | |
| ], | |
| max_tokens=1024 | |
| ) | |
| # 3. Clean Output | |
| raw_text = response.choices[0].message.content | |
| # Remove potential markdown fences | |
| clean_json = raw_text.replace("```json", "").replace("```", "").strip() | |
| # Extra safety: Find the first { and last } | |
| start = clean_json.find('{') | |
| end = clean_json.rfind('}') + 1 | |
| if start != -1 and end != -1: | |
| clean_json = clean_json[start:end] | |
| return json.loads(clean_json) | |
| except Exception as e: | |
| return {"error": f"Qwen API Error: {str(e)}"} | |
| # --- PART 4: EXECUTION EXAMPLE --- | |
| if __name__ == "__main__": | |
| # 1. AUTHENTICATION FIX (For Colab / Local Env) | |
| try: | |
| # If running in Colab, try to fetch from secrets | |
| try: | |
| from google.colab import userdata | |
| hf1_secret = userdata.get('HF_TOKEN') | |
| os.environ["HF_TOKEN"] = hf1_secret | |
| print(f"✅ Successfully loaded token from secret HF_TOKEN") | |
| except ImportError: | |
| # If running locally, assume env var is already set or manually set here | |
| pass | |
| except Exception as e: | |
| print(f"⚠️ Warning: Could not load secret 'HF_TOKEN'. Make sure the name in the Key icon is exactly 'HF_TOKEN'.") | |
| print(f"Error details: {e}") | |
| # 2. INITIALIZE PIPELINE | |
| try: | |
| app = RecipeDigitalizerPipeline() | |
| # 3. USER INPUT (Update this path to your actual image) | |
| user_image = "Recipe.jfif" | |
| # 4. RUN PIPELINE | |
| if os.path.exists(user_image): | |
| print(f"Processing {user_image}...") | |
| ai_output = app.run_pipeline(user_image) | |
| # 5. AI OUTPUT | |
| print("\n--- FINAL DIGITAL OUTPUT ---") | |
| print(json.dumps(ai_output, indent=4)) | |
| else: | |
| print(f"❌ Error: Image not found at {user_image}") | |
| except Exception as e: | |
| print(f"❌ Application Error: {e}") | |