File size: 7,250 Bytes
f7f6fe3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# 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.")