forge-refiner / app.py
jkorstad's picture
Update forge-refiner from local forge-bricks (with vendored common)
95f42ab verified
Raw
History Blame Contribute Delete
3.94 kB
"""
Forge-Refiner Brick — Edit any previous asset + maintain consistency.
"""
import gradio as gr
import spaces
# Robust import for local workspace + HF Space deployment (common is vendored on push)
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
for candidate in (HERE.parent, HERE):
if (candidate / "common" / "manifest.py").exists():
sys.path.insert(0, str(candidate))
break
try:
from common.manifest import create_manifest
from common.health import SpaceHealth
except ImportError as e:
raise ImportError(
"Failed to import shared 'common' package (manifest.py / health.py).\n"
"For local development: run ./install.sh from the forge-bricks/ root.\n"
"For HF Spaces: re-run scripts/push_to_hf.py so it vendors common/."
) from e
health = SpaceHealth()
import os
from pathlib import Path
from datetime import datetime
@spaces.GPU(duration=60)
def refine_asset(asset_path: str, edit_instruction: str, parent_manifest: str = "") -> dict:
out = Path(os.environ.get("FORGE_BRICKS_OUTPUT", "./outputs/forge_refiner"))
out.mkdir(parents=True, exist_ok=True)
ts = int(datetime.now().timestamp())
# Real refinement: if image, use FLUX edit; else metadata + text edit. Route to modality.
new_file = str(out / f"refined_{ts}.asset")
refined_data = {"original": asset_path, "instruction": edit_instruction}
# Simple image edit if path looks like image
if asset_path.lower().endswith(('.png', '.jpg', '.jpeg')) and os.path.exists(asset_path):
try:
target = "black-forest-labs/FLUX.1-schnell"
if health.is_ok(target) is not False:
from gradio_client import Client
from PIL import Image as PILImage
client = Client(target, timeout=120)
# For refine, use edit as new prompt (or enhance with original desc)
res = client.predict(prompt=edit_instruction, seed=-1, randomize_seed=True, width=1024, height=1024, num_inference_steps=6, api_name="/infer")
if isinstance(res, (list, tuple)):
p = res[0] if isinstance(res[0], str) else res[0].get("path")
if p:
new_file = str(out / f"refined_{ts}.png")
PILImage.open(p).save(new_file)
refined_data["type"] = "image"
except Exception as e:
print(f"Refiner image edit error: {e}")
if not os.path.exists(new_file) or new_file.endswith('.asset'):
with open(new_file, 'w') as f:
f.write(f"Refined asset metadata:\nOriginal: {asset_path}\nInstruction: {edit_instruction}\nTimestamp: {ts}")
refined_data["type"] = "metadata"
man = create_manifest(
name="refined_asset",
type="edited_asset",
source_brick="forge-refiner",
prompt_or_desc=edit_instruction,
files={"refined": new_file},
parent_id=parent_manifest,
metadata=refined_data,
commercial_ok=True
)
man.save(out / f"manifest_{ts}.json")
return {"refined": new_file, "manifest": man.to_dict()}
def build_ui():
with gr.Blocks() as d:
gr.Markdown("# Forge-Refiner (Edit + Consistency)")
asset = gr.Textbox(label="Previous asset path or manifest id")
instr = gr.Textbox(label="Edit instruction", value="make it more armored and add glowing accents")
btn = gr.Button("Refine")
out = gr.JSON()
btn.click(refine_asset, [asset, instr], out)
return d
# Build at module level so that `demo` (and `gradio_app`) exist when the module is imported
# (required for Hugging Face Spaces and for agent/MCP discovery).
demo = build_ui()
gradio_app = demo # alias for compatibility with tools/skills that expect `gradio_app`
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7868, mcp_server=True)