Spaces:
Sleeping
Sleeping
File size: 7,221 Bytes
235461a | 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 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | import uuid
from typing import Dict, Optional
import replicate
from dotenv import load_dotenv
from langchain_core.runnables import RunnableConfig, RunnableLambda
from pydantic import BaseModel
from llm.prompt import generate_image_tool_description
from llm.utils import create_or_update_ip_generation_count, get_ip_generation_count, store_tool_result, upload_generated_image_to_s3
load_dotenv()
# The generate_image tool's input schema
class GenerateImageToolInput(BaseModel):
prompt: str
user_id: str
image_url: str
title: Optional[str] = "Generated Image"
# The core function that generates an image of the tool
def _generate_image_core(
prompt: str,
user_id: str,
image_url: str,
title: str,
client_ip: str,
) -> str:
"""
Generate an image based on a prompt.
"""
print(f"[TOOL] generate_image called with prompt: {prompt[:50]}..., user_id: {user_id}, image_url: {image_url[:50]}...")
# Check if the user has exceeded the generation limit
if get_ip_generation_count(client_ip) >= 10:
print("[TOOL] User exceeded the generation limit of 10 this week.")
return "Failed as user exceeded the max generation limit of 10 this week."
use_sdxl = False # True for testing purposes
if use_sdxl:
input = {
"width": 768,
"height": 768,
"prompt": prompt,
"refine": "expert_ensemble_refiner",
"apply_watermark": False,
"num_inference_steps": 25,
"prompt_strength": 0.5,
"image": image_url,
"input_image": image_url,
"output_format": "png",
}
version = "stability-ai/sdxl:" "7762fd07cf82c948538e41f63f77d685e02b063e37e496e96eefd46c929f9bdc"
output = replicate.run(
version,
input=input,
)
generated_image_url = output[0] if isinstance(output, list) else output
else: # Flux Kontext Pro
# Generate image using Replicate
input = {
"prompt": prompt,
"input_image": image_url,
"output_format": "png",
}
output = replicate.run(
"black-forest-labs/flux-kontext-pro",
input=input,
)
print(f"[TOOL] Replicate output: {output}")
print(f"[TOOL] Output type: {type(output)}")
print(f"[TOOL] Output length: {len(output) if hasattr(output, '__len__') else 'N/A'}")
# Check if generation was successful
if not output or (hasattr(output, "__len__") and len(output) == 0):
print("[TOOL] Replicate generation failed - no output")
return "Failed to generate image. Please try again."
# Flux Kontext Pro returns a string URL
generated_image_url = str(output)
print(f"[TOOL] Generated image URL: {generated_image_url}")
# Handle Flux Kontext Pro output format
image_data: Optional[bytes] = None
try:
# Download the image from the URL
import requests
response = requests.get(generated_image_url)
response.raise_for_status()
image_data = response.content
print(f"[TOOL] Downloaded image data, size: {len(image_data)} bytes")
except Exception as e:
print(f"[TOOL] Error processing output: {e}")
return f"Failed to process generated image: {str(e)}"
# Check if we successfully got image data
if image_data is None:
return "Failed to get image data from generation output"
# Update or create a new generation count by + 1 for this ip address
create_or_update_ip_generation_count(client_ip)
# Generate unique ID for the image
image_id = str(uuid.uuid4())
# Upload to S3
print(f"[TOOL] Uploading to S3 with image_id: {image_id}")
print(f"[TOOL] Image data size: {len(image_data)} bytes")
try:
s3_result = upload_generated_image_to_s3(
image_data=image_data,
image_id=image_id,
user_id=user_id,
prompt=prompt,
title=title,
)
print(f"[TOOL] S3 upload result: {s3_result}")
print(f"[TOOL] S3 upload success: {s3_result.get('success', False)}")
if s3_result["success"]:
# Store structured result for the agent to retrieve
tool_result = {"image_id": image_id, "title": title, "prompt": prompt, "success": True}
print(f"[TOOL] About to store tool result: {tool_result}")
store_tool_result(user_id, "generate_image", tool_result)
print("[TOOL] Tool result stored successfully")
result_msg = f"Image generated successfully! User can find it his/her gallery. \
Image ID: {image_id}, Title: {title}"
print(f"[TOOL] Returning success: {result_msg}")
return result_msg
else:
error_msg = f"Image generated but failed to save: {s3_result.get('error', 'Unknown error')}"
print(f"[TOOL] Returning error: {error_msg}")
return error_msg
except Exception as e:
error_msg = f"Image generated but failed to save to storage: {str(e)}"
print(f"[TOOL] Exception during S3 upload: {error_msg}")
return error_msg
finally:
if image_data:
# Clear image data from memory
del image_data
def _generate_image_callable(inputs: Dict[str, str], config: RunnableConfig):
# Normalize inputs whether dict or Pydantic
if hasattr(inputs, "model_dump"):
inputs = inputs.model_dump()
elif hasattr(inputs, "dict"):
inputs = inputs.dict()
# Pull the IP from the per-invoke config
cfg = config.get("configurable") or {}
ip = cfg.get("client_ip")
if not isinstance(ip, str) or not ip:
# Fail fast if it's absent or not a string
raise ValueError("client_ip is required in config.configurable and must be a non-empty string")
client_ip: str = ip
# Call your core with the IP
return _generate_image_core(
prompt=inputs["prompt"],
user_id=inputs["user_id"],
image_url=inputs["image_url"],
title=inputs.get("title", "Generated Image"),
client_ip=client_ip,
)
def initialize_tools():
"""Initialize the tools for the agent."""
print("[TOOLS] building generate_image tool")
# A Runnable that receives (inputs, config) every invoke
generate_image_runnable = RunnableLambda(_generate_image_callable)
# As agent creation API expects "tools", convert the runnable to a Tool:
generate_image_tool = generate_image_runnable.as_tool(
name="generate_image",
description=generate_image_tool_description,
args_schema=GenerateImageToolInput,
)
return [generate_image_tool]
if __name__ == "__main__":
# Test the tool
generate_image = initialize_tools()[0]
output = generate_image.invoke(
{
"prompt": "A woman in a beautiful sunset over a calm ocean",
"user_id": "123",
"image_url": "https://example.com/image.jpg",
"title": "Test Image",
},
config={"configurable": {"client_ip": "127.0.0.1"}},
)
print(output)
|