DreamCanvas setup : to be reviewed
This commit is contained in:
0
On host/DreamCanvas/backend/__init__.py
Normal file
0
On host/DreamCanvas/backend/__init__.py
Normal file
15
On host/DreamCanvas/backend/main.py
Normal file
15
On host/DreamCanvas/backend/main.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import uvicorn
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from .routes import router as api_router
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# Serve static files (CSS, JS)
|
||||
app.mount("/static", StaticFiles(directory="ui"), name="static")
|
||||
|
||||
# Include all API routes
|
||||
app.include_router(api_router)
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
13
On host/DreamCanvas/backend/models.py
Normal file
13
On host/DreamCanvas/backend/models.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Request model for LLM
|
||||
class LLMRequest(BaseModel):
|
||||
positive_prompt: str
|
||||
|
||||
# Request model for image generation
|
||||
class PromptRequest(BaseModel):
|
||||
positive_prompt: str
|
||||
negative_prompt: str
|
||||
steps: int = 25
|
||||
width: int = 512
|
||||
height: int = 512
|
||||
58
On host/DreamCanvas/backend/routes.py
Normal file
58
On host/DreamCanvas/backend/routes.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import FileResponse, JSONResponse, StreamingResponse
|
||||
from .models import LLMRequest, PromptRequest
|
||||
from .services import generate_images, get_quick_prompts_data, ask_llm_service
|
||||
from PIL import Image
|
||||
import io
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Serve HTML file (index.html)
|
||||
@router.get("/")
|
||||
async def get_index():
|
||||
return FileResponse("ui/index.html")
|
||||
|
||||
# Endpoint to serve quick prompts from configuration file
|
||||
@router.get("/quick_prompts/")
|
||||
async def get_quick_prompts():
|
||||
try:
|
||||
prompts = get_quick_prompts_data()
|
||||
return JSONResponse(content=prompts)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail="Error reading quick prompts file.")
|
||||
|
||||
# Endpoint to ask the LLM for creative ideas based on the positive prompt
|
||||
@router.post("/ask_llm/")
|
||||
async def ask_llm(request: LLMRequest):
|
||||
try:
|
||||
response = ask_llm_service(request.positive_prompt)
|
||||
return JSONResponse(content={"assistant_reply": response})
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail="Error interacting with LLM: " + str(e))
|
||||
|
||||
# Endpoint to generate images
|
||||
@router.post("/generate_images/")
|
||||
async def generate_images_api(request: PromptRequest):
|
||||
try:
|
||||
images, seed = await generate_images(
|
||||
request.positive_prompt,
|
||||
request.negative_prompt,
|
||||
request.steps,
|
||||
(request.width, request.height)
|
||||
)
|
||||
|
||||
# Convert images to a format FastAPI can return
|
||||
image_responses = []
|
||||
for node_id in images:
|
||||
for image_data in images[node_id]:
|
||||
img = Image.open(io.BytesIO(image_data))
|
||||
img_byte_arr = io.BytesIO()
|
||||
img.save(img_byte_arr, format='PNG')
|
||||
img_byte_arr.seek(0)
|
||||
|
||||
# Append the image response to a list
|
||||
image_responses.append(StreamingResponse(img_byte_arr, media_type="image/png"))
|
||||
|
||||
return image_responses[0] # Return the first image for simplicity
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Image generation failed: {str(e)}")
|
||||
141
On host/DreamCanvas/backend/services.py
Normal file
141
On host/DreamCanvas/backend/services.py
Normal file
@@ -0,0 +1,141 @@
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
import random
|
||||
import uuid
|
||||
import websocket as ws_client
|
||||
from dotenv import load_dotenv
|
||||
from urllib.request import urlopen, Request
|
||||
from urllib.parse import urlencode
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
server_address = os.getenv('COMFYUI_SERVER_ADDRESS', 'localhost:8188')
|
||||
client_id = str(uuid.uuid4())
|
||||
ollama_server_address = os.getenv('OLLAMA_SERVER_ADDRESS', 'localhost:11434')
|
||||
ollama_server_url = f"http://{ollama_server_address}/v1/chat/completions"
|
||||
ollama_model = os.getenv('OLLAMA_MODEL', 'llama3.1:latest') # Load model from .env
|
||||
system_prompt = """
|
||||
You are an AI model that transforms user input into concise, creative, and visually descriptive prompts for AI image generation. If the user input is empty, generate a creative, detailed description automatically. Always respond with a single, visually descriptive line, incorporating elements such as hyperrealistic, 4K, and detailed imagery when applicable.
|
||||
|
||||
Guidelines:
|
||||
1. **Be Specific and Clear**: Ensure the response describes the subject, setting, and style in a visually engaging manner.
|
||||
2. **Keep It Brief**: Limit responses to one or two sentences that provide a vivid image while remaining concise.
|
||||
3. **Encourage Creativity**: Incorporate artistic styles like hyperrealism, 4K resolution, cinematic lighting, or surrealism where appropriate.
|
||||
4. **Handle Empty Input**: If the input is empty, create an imaginative and detailed prompt using the aforementioned elements.
|
||||
5. **No Extra Communication**: Respond only with the transformed prompt without additional commentary.
|
||||
|
||||
### Example Inputs and Outputs:
|
||||
|
||||
**Input**: "A futuristic city at night with neon lights and flying cars."
|
||||
**Output**: "A neon-lit futuristic cityscape at night, with flying cars darting between towering skyscrapers, glowing in vibrant 4K detail."
|
||||
|
||||
**Input**: "A serene beach with palm trees and a sunset."
|
||||
**Output**: "A serene beach at sunset, palm trees swaying gently as the golden light reflects off the 4K ocean waves."
|
||||
|
||||
**Input**: "A magical forest with glowing trees and animals."
|
||||
**Output**: "A hyperrealistic forest where glowing trees light the way for mystical creatures under a twilight sky, captured in stunning 4K."
|
||||
|
||||
**Input**: ""
|
||||
**Output**: "An intricately detailed 4K portrait of a warrior in golden armor, standing proudly under the dramatic glow of cinematic lighting."
|
||||
|
||||
**Input**: "A watercolor painting of a rainy street."
|
||||
**Output**: "A dreamy watercolor painting of a rainy street, soft reflections glistening in the puddles under warm streetlights."
|
||||
"""
|
||||
# Service to get quick prompts data
|
||||
def get_quick_prompts_data():
|
||||
with open("quick_prompts.json", "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
# Service to ask LLM for response
|
||||
def ask_llm_service(positive_prompt: str):
|
||||
ollama_request = {
|
||||
"model": ollama_model, # Use model from .env
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": system_prompt
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": positive_prompt
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
response = requests.post(ollama_server_url, json=ollama_request, headers={"Content-Type": "application/json"})
|
||||
response_data = response.json()
|
||||
return response_data["choices"][0]["message"]["content"]
|
||||
|
||||
# Service to queue a prompt
|
||||
def queue_prompt(prompt):
|
||||
p = {"prompt": prompt, "client_id": client_id}
|
||||
data = json.dumps(p).encode('utf-8')
|
||||
req = Request(f"http://{server_address}/prompt", data=data)
|
||||
return json.loads(urlopen(req).read())
|
||||
|
||||
# Service to get image
|
||||
def get_image(filename, subfolder, folder_type):
|
||||
data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
|
||||
url_values = urlencode(data)
|
||||
with urlopen(f"http://{server_address}/view?{url_values}") as response:
|
||||
return response.read()
|
||||
|
||||
# Service to get history
|
||||
def get_history(prompt_id):
|
||||
with urlopen(f"http://{server_address}/history/{prompt_id}") as response:
|
||||
return json.loads(response.read())
|
||||
|
||||
# WebSocket image generation service
|
||||
async def get_images(ws, prompt):
|
||||
prompt_id = queue_prompt(prompt)['prompt_id']
|
||||
output_images = {}
|
||||
|
||||
while True:
|
||||
out = ws.recv()
|
||||
if isinstance(out, str):
|
||||
message = json.loads(out)
|
||||
if message['type'] == 'executing':
|
||||
data = message['data']
|
||||
if data['node'] is None and data['prompt_id'] == prompt_id:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
history = get_history(prompt_id)[prompt_id]
|
||||
for o in history['outputs']:
|
||||
for node_id in history['outputs']:
|
||||
node_output = history['outputs'][node_id]
|
||||
if 'images' in node_output:
|
||||
images_output = []
|
||||
for image in node_output['images']:
|
||||
image_data = get_image(image['filename'], image['subfolder'], image['type'])
|
||||
images_output.append(image_data)
|
||||
output_images[node_id] = images_output
|
||||
|
||||
return output_images
|
||||
|
||||
# Main image generation function
|
||||
async def generate_images(positive_prompt, negative_prompt, steps=25, resolution=(512, 512)):
|
||||
ws = ws_client.WebSocket()
|
||||
ws.connect(f"ws://{server_address}/ws?clientId={client_id}")
|
||||
|
||||
with open("workflow.json", "r", encoding="utf-8") as f:
|
||||
workflow_data = f.read()
|
||||
|
||||
workflow = json.loads(workflow_data)
|
||||
|
||||
workflow["6"]["inputs"]["text"] = positive_prompt
|
||||
workflow["7"]["inputs"]["text"] = negative_prompt
|
||||
workflow["3"]["inputs"]["steps"] = steps
|
||||
workflow["5"]["inputs"]["width"] = resolution[0]
|
||||
workflow["5"]["inputs"]["height"] = resolution[1]
|
||||
seed = random.randint(1, 1000000000)
|
||||
workflow["3"]["inputs"]["seed"] = seed
|
||||
|
||||
images = await get_images(ws, workflow)
|
||||
|
||||
ws.close()
|
||||
|
||||
return images, seed
|
||||
Reference in New Issue
Block a user