# storyverse_weaver/core/image_services.py import os import requests # For generic API calls import base64 from io import BytesIO from PIL import Image # from dotenv import load_dotenv # load_dotenv() # --- API Key Configuration (Use specific names for StoryVerse) --- STABILITY_API_KEY = os.getenv("STORYVERSE_STABILITY_API_KEY") OPENAI_API_KEY = os.getenv("STORYVERSE_OPENAI_API_KEY") # For DALL-E # HUGGINGFACE_TOKEN also used by llm_services, can be reused if using HF image models STABILITY_API_CONFIGURED = bool(STABILITY_API_KEY and STABILITY_API_KEY.strip()) OPENAI_DALLE_CONFIGURED = bool(OPENAI_API_KEY and OPENAI_API_KEY.strip()) # HF_IMAGE_CONFIGURED = bool(HF_TOKEN and HF_TOKEN.strip()) # Assuming HF_TOKEN is also for image models class ImageGenResponse: def __init__(self, image: Image.Image = None, image_url: str = None, error: str = None, success: bool = True, provider: str = "unknown"): self.image = image # PIL Image object self.image_url = image_url # If API returns a URL self.error = error self.success = success self.provider = provider def initialize_image_llms(): # Simple function to print status print("INFO: image_services.py - Initializing Image Generation services...") if STABILITY_API_CONFIGURED: print("SUCCESS: image_services.py - Stability AI API Key detected.") else: print("WARNING: image_services.py - STORYVERSE_STABILITY_API_KEY not found. Stability AI disabled.") if OPENAI_DALLE_CONFIGURED: print("SUCCESS: image_services.py - OpenAI API Key detected (for DALL-E).") else: print("WARNING: image_services.py - STORYVERSE_OPENAI_API_KEY not found. DALL-E disabled.") # if HF_IMAGE_CONFIGURED: print("INFO: image_services.py - Hugging Face Token detected (can be used for HF image models).") print("INFO: image_services.py - Image LLM Init complete.") # --- Stability AI (Example) --- def generate_image_stabilityai(prompt: str, style_preset: str = None, negative_prompt: str = None, engine_id: str = "stable-diffusion-xl-1024-v1-0", steps: int = 30, cfg_scale: float = 7.0, width: int = 1024, height: int = 1024) -> ImageGenResponse: if not STABILITY_API_CONFIGURED: return ImageGenResponse(error="Stability AI API key not configured.", success=False, provider="StabilityAI") api_host = os.getenv('API_HOST', 'https://api.stability.ai') request_url = f"{api_host}/v1/generation/{engine_id}/text-to-image" payload = { "text_prompts": [{"text": prompt}], "cfg_scale": cfg_scale, "height": height, "width": width, "steps": steps, "samples": 1, } if style_preset: payload["style_preset"] = style_preset if negative_prompt: payload["text_prompts"].append({"text": negative_prompt, "weight": -1.0}) headers = { "Accept": "application/json", "Content-Type": "application/json", "Authorization": f"Bearer {STABILITY_API_KEY}" } print(f"DEBUG: image_services.py - Calling Stability AI with prompt: {prompt[:50]}...") try: response = requests.post(request_url, headers=headers, json=payload, timeout=60) # Increased timeout response.raise_for_status() # Will raise an HTTPError if the HTTP request returned an unsuccessful status code artifacts = response.json().get("artifacts") if not artifacts: return ImageGenResponse(error="No image artifacts found in Stability AI response.", success=False, provider="StabilityAI", raw_response=response.text) image_data = base64.b64decode(artifacts[0]["base64"]) image = Image.open(BytesIO(image_data)) print("DEBUG: image_services.py - Stability AI image generated successfully.") return ImageGenResponse(image=image, provider="StabilityAI") except requests.exceptions.RequestException as e: error_msg = f"Stability AI API request failed: {type(e).__name__} - {str(e)}" if hasattr(e, 'response') and e.response is not None: error_msg += f" - Response: {e.response.text[:200]}" print(f"ERROR: image_services.py - {error_msg}") return ImageGenResponse(error=error_msg, success=False, provider="StabilityAI", raw_response=e) except Exception as e: # Catch other potential errors like JSON decoding error_msg = f"Error processing Stability AI response: {type(e).__name__} - {str(e)}" print(f"ERROR: image_services.py - {error_msg}") return ImageGenResponse(error=error_msg, success=False, provider="StabilityAI", raw_response=e) # --- DALL-E (Conceptual - you'd need 'openai' library and setup) --- def generate_image_dalle(prompt: str, size="1024x1024", quality="standard", n=1) -> ImageGenResponse: if not OPENAI_DALLE_CONFIGURED: return ImageGenResponse(error="OpenAI DALL-E API key not configured.", success=False, provider="DALL-E") try: # from openai import OpenAI # Would be imported at top level # client = OpenAI(api_key=OPENAI_API_KEY) # response = client.images.generate( # model="dall-e-3", # or "dall-e-2" # prompt=prompt, # size=size, # quality=quality, # n=n, # response_format="url" # or "b64_json" # ) # image_url = response.data[0].url # image_content = requests.get(image_url).content # image = Image.open(BytesIO(image_content)) # return ImageGenResponse(image=image, image_url=image_url, provider="DALL-E") print("DEBUG: image_services.py - DALL-E call placeholder.") # Placeholder # Simulate an image for now dummy_image = Image.new('RGB', (512, 512), color = 'skyblue') return ImageGenResponse(image=dummy_image, provider="DALL-E (Simulated)") except Exception as e: return ImageGenResponse(error=f"DALL-E API Error: {type(e).__name__} - {str(e)}", success=False, provider="DALL-E") # --- Hugging Face Image Model (Conceptual - via Inference API or local Diffusers) --- # def generate_image_hf_model(prompt: str, model_id="stabilityai/stable-diffusion-xl-base-1.0") -> ImageGenResponse: # if not HF_IMAGE_CONFIGURED: # return ImageGenResponse(error="HF Token not configured for image models.", success=False, provider="HF") # try: # You might use a client similar to hf_inference_text_client but for image generation task # Or if it's a diffusers pipeline, you'd load and run it. # This requires the `diffusers` library and often significant compute. # response_bytes = hf_some_image_client.text_to_image(prompt, model=model_id) # Hypothetical client method # image = Image.open(BytesIO(response_bytes)) # return ImageGenResponse(image=image, provider=f"HF ({model_id})") # print("DEBUG: image_services.py - HF Image Model call placeholder.") # dummy_image = Image.new('RGB', (512, 512), color = 'lightgreen') # return ImageGenResponse(image=dummy_image, provider=f"HF ({model_id} - Simulated)") # except Exception as e: # return ImageGenResponse(error=f"HF Image Model Error: {type(e).__name__} - {str(e)}", success=False, provider=f"HF ({model_id})") print("DEBUG: core.image_services (for StoryVerseWeaver) - Module defined.")