import gradio as gr
import torch
import random
import time
from datetime import datetime
from diffusers import StableDiffusionPipeline, EulerAncestralDiscreteScheduler
import json
# --- Configuration & Model Loading ---
# Replicating the logic of your ComfyUI JSON:
# - CheckpointLoaderSimple -> runwayml/stable-diffusion-v1-5
# - KSampler -> EulerAncestralDiscreteScheduler
# - EmptyLatentImage -> 512x512
MODEL_ID = "runwayml/stable-diffusion-v1-5"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
# Load the pipeline (simulating the backend compute)
print(f"Loading model {MODEL_ID} on {DEVICE}...")
try:
pipe = StableDiffusionPipeline.from_pretrained(
MODEL_ID,
torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32,
safety_checker=None # Disable for speed/demo purposes
)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
if DEVICE == "cuda":
pipe.to(DEVICE)
print("Model loaded successfully.")
except Exception as e:
print(f"Error loading model: {e}")
pipe = None
# --- Core Logic Functions ---
def generate_image_with_memory(
prompt,
negative_prompt,
steps,
cfg,
seed,
memory_db
):
"""
Generates an image and updates the 'Long Term Memory' database.
"""
if pipe is None:
return None, memory_db, "Error: Model not loaded. Check GPU availability."
start_time = time.time()
# Handle Seed (0 = random)
if seed == 0:
seed = random.randint(0, 2147483647)
generator = torch.Generator(device=DEVICE).manual_seed(seed)
# Generate Image (Replicating KSampler + VAE Decode)
try:
result = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
num_inference_steps=int(steps),
guidance_scale=float(cfg),
height=512,
width=512,
generator=generator
)
image = result.images[0]
except Exception as e:
return None, memory_db, f"Generation Failed: {str(e)}"
# Update Memory Database
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
new_entry = {
"timestamp": timestamp,
"prompt": prompt,
"negative": negative_prompt,
"seed": seed,
"steps": steps,
"cfg": cfg
}
# Prepend new entry to memory
updated_memory = [new_entry] + memory_db
processing_time = f"{time.time() - start_time:.2f}s"
return image, updated_memory, f"Generated in {processing_time} | Seed: {seed}"
def recall_prompt(memory_db, evt: gr.SelectData):
"""
Recalls a specific prompt from history based on user selection.
"""
if memory_db is None or len(memory_db) == 0:
return "", "", 0, 20, 7.0
index = evt.index[0]
selected = memory_db[index]
return (
selected["prompt"],
selected["negative"],
int(selected["seed"]),
int(selected["steps"]),
float(selected["cfg"])
)
def export_memory(memory_db):
"""
Exports the memory database to a downloadable JSON file.
"""
if not memory_db:
return None
json_str = json.dumps(memory_db, indent=4)
return json_str
# --- Gradio 6 UI Construction ---
with gr.Blocks() as demo:
# Header
gr.HTML("""
🧠 AI-Native Memory Core
Automated Long Term Super Intelligent Human Recall | Persistent Database
Built with anycoder
""")
with gr.Row():
# --- Sidebar: Controls & Memory Database ---
with gr.Sidebar(width=350):
gr.Markdown("### ⚙️ Generation Parameters")
with gr.Accordion("Advanced Settings", open=True):
steps = gr.Slider(minimum=1, maximum=150, value=20, step=1, label="Sampling Steps", info="Higher = more detail")
cfg = gr.Slider(minimum=1, maximum=30, value=7.0, step=0.5, label="CFG Scale", info="Prompt adherence")
seed = gr.Number(value=0, label="Seed (0 = Random)", precision=0)
gr.Markdown("---")
gr.Markdown("### 💾 Persistent Memory Database")
gr.Markdown("Click a row to recall prompt settings.")
memory_display = gr.Dataframe(
label="Prompt History",
headers=["Time", "Prompt", "Seed"],
datatype=["str", "str", "number"],
interactive=False,
wrap=True,
height=300
)
with gr.Row():
export_btn = gr.Button("💾 Export Memory (JSON)", size="sm")
download_file = gr.File(visible=False, label="Download Database")
gr.Markdown("---")
gr.Markdown("### 📱 App Access")
# Simulating the APK request via PWA/Install instructions
gr.Markdown("""
To install on Android:
1. Open this in Chrome
2. Tap Menu (⋮)
3. Tap "Add to Home Screen"
""")
app_link_btn = gr.Button("📲 Open App View", link="/?view=standalone", variant="primary")
# --- Main Area: Chat & Generation ---
with gr.Column(scale=2):
with gr.Group():
prompt_input = gr.Textbox(
label="Positive Prompt",
placeholder="Describe your vision...",
lines=3,
show_copy_button=True
)
negative_input = gr.Textbox(
label="Negative Prompt",
value="low quality, blurry, distorted, bad anatomy",
lines=2,
visible=False # Collapsed for clean UI initially
)
with gr.Row():
toggle_neg = gr.Button("Show/Hide Negative", size="sm")
generate_btn = gr.Button("✨ Generate & Remember", variant="primary", scale=2)
# Output Area
with gr.Row():
with gr.Column():
result_image = gr.Image(label="Generated Artifact", type="pil", height=512)
status_text = gr.Textbox(label="System Status", interactive=False)
# --- State Management ---
# We use Gradio State to hold the "Memory Database" in the session
memory_state = gr.State(value=[])
# --- Event Listeners ---
# 1. Toggle Negative Prompt
toggle_neg.click(lambda: gr.Textbox(visible=True), None, negative_input)
# 2. Generate Image & Update Memory
generate_btn.click(
fn=generate_image_with_memory,
inputs=[prompt_input, negative_input, steps, cfg, seed, memory_state],
outputs=[result_image, memory_state, status_text],
api_visibility="public"
)
# 3. Update Memory Table Display whenever state changes
# We format the list of dicts into a dataframe-friendly format
def format_memory_for_display(db):
if not db:
return []
return [[row["timestamp"], row["prompt"][:50] + "...", row["seed"]] for row in db]
# We use a dummy trigger or just chain it.
# In Gradio 6, we can use the `change` event on the state itself or chain outputs.
# Here we chain the output of generate to update the view directly.
# Note: To keep it simple, we modify the generate function to return the formatted dataframe if needed,
# or we can use a separate update trigger. Let's do it inline for efficiency.
# Redefining generate to return formatted table for immediate display
def generate_with_ui_update(prompt, neg, steps, cfg, seed, db):
img, new_db, status = generate_image_with_memory(prompt, neg, steps, cfg, seed, db)
formatted_table = format_memory_for_display(new_db)
return img, new_db, status, formatted_table
generate_btn.click(
fn=generate_with_ui_update,
inputs=[prompt_input, negative_input, steps, cfg, seed, memory_state],
outputs=[result_image, memory_state, status_text, memory_display],
api_visibility="public"
)
# 4. Recall from Memory (Click on Table)
memory_display.select(
fn=recall_prompt,
inputs=[memory_state],
outputs=[prompt_input, negative_input, seed, steps, cfg],
api_visibility="private"
)
# 5. Export Memory
export_btn.click(
fn=export_memory,
inputs=[memory_state],
outputs=[download_file],
api_visibility="private"
)
# --- Launch Configuration ---
# Gradio 6 Syntax: Theme and parameters go in launch(), not Blocks()
demo.launch(
theme=gr.themes.Soft(
primary_hue="indigo",
secondary_hue="blue",
neutral_hue="slate",
font=gr.themes.GoogleFont("Inter"),
text_size="lg",
spacing_size="lg",
radius_size="md"
).set(
button_primary_background_fill="*primary_600",
button_primary_background_fill_hover="*primary_700",
block_title_text_weight="600",
block_border_width="1px",
block_border_color="*neutral_200"
),
footer_links=[
{"label": "Built with anycoder", "url": "https://huggingface.co/spaces/akhaliq/anycoder"},
{"label": "Hugging Face", "url": "https://huggingface.co"}
],
share=False # Set to True if you want a public link immediately
)