Hosdroid commited on
Commit
e46ed21
·
verified ·
1 Parent(s): ef8e6a7

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -722
app.py DELETED
@@ -1,722 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- open_image_ai_hivemind.py
5
- Single-file, open-source suite:
6
- - Image AI (offline-capable) with bilingual GUI (FA/EN)
7
- - Code Learner Worm (self-improving patcher with tests/linters)
8
- - Open-Source Harvester (local+URL indexing and modular plug-in loader)
9
- No paid APIs; no accounts; uses local and open tools only.
10
-
11
- Run:
12
- python open_image_ai_hivemind.py
13
-
14
- Build onefile (optional):
15
- python -m pip install pyinstaller
16
- pyinstaller --onefile open_image_ai_hivemind.py
17
- """
18
-
19
- import os
20
- import sys
21
- import json
22
- import time
23
- import math
24
- import hashlib
25
- import random
26
- import threading
27
- import subprocess
28
- import shutil
29
- import re
30
- from datetime import datetime
31
- from pathlib import Path
32
- import importlib.util
33
-
34
- # Lazy installer (open-source libs only)
35
- def _lazy_install(pkg):
36
- try:
37
- __import__(pkg)
38
- except ImportError:
39
- os.system(f"{sys.executable} -m pip install --quiet {pkg}")
40
-
41
- for pkg in ("torch", "diffusers", "accelerate", "safetensors", "Pillow"):
42
- _lazy_install(pkg)
43
-
44
- import tkinter as tk
45
- from tkinter import ttk, filedialog, messagebox
46
- import torch
47
- from PIL import Image, ImageOps, ImageFilter, ImageDraw
48
- from diffusers import StableDiffusionPipeline, DiffusionPipeline
49
-
50
- # ---------------------------
51
- # Paths & Directories
52
- # ---------------------------
53
- APP_NAME = "Open Image AI Hivemind"
54
- ROOT_DIR = Path(__file__).parent.resolve()
55
- OUTPUT_DIR = Path(os.environ.get("OIA_OUT", ROOT_DIR / "outputs")).resolve()
56
- META_DIR = OUTPUT_DIR / "meta"
57
- CACHE_DIR = OUTPUT_DIR / "cache"
58
- TMP_DIR = OUTPUT_DIR / "tmp"
59
- PLUGINS_DIR = ROOT_DIR / "plugins"
60
- HARVEST_DIR = ROOT_DIR / "harvest"
61
- for d in (OUTPUT_DIR, META_DIR, CACHE_DIR, TMP_DIR, PLUGINS_DIR, HARVEST_DIR):
62
- d.mkdir(parents=True, exist_ok=True)
63
-
64
- # ---------------------------
65
- # Image AI Config
66
- # ---------------------------
67
- DEFAULT_STEPS = int(os.environ.get("OIA_STEPS", "30"))
68
- DEFAULT_GUIDANCE = float(os.environ.get("OIA_GUIDANCE", "7.5"))
69
- MAX_QUEUE = int(os.environ.get("OIA_MAX_QUEUE", "64"))
70
- SEED_AUTOMATIC = -1
71
-
72
- OPEN_MODELS = {
73
- "SDXL (base)": "stabilityai/stable-diffusion-xl-base-1.0",
74
- "SD 1.5 (classic)": "runwayml/stable-diffusion-v1-5",
75
- # Add more open models if diffusers supports them
76
- # "Stable Cascade": "stabilityai/stable-cascade",
77
- # "OpenJourney": "prompthero/openjourney-v4",
78
- }
79
-
80
- # ---------------------------
81
- # Prompt utilities
82
- # ---------------------------
83
- OPTICAL_ILLUSION_PRESETS = [
84
- "high-contrast black and white optical illusion, impossible geometry, Penrose stairs, concentric lines, parallax shifts",
85
- "moiré patterns, nested grids, Escher-inspired recursion, tilt-shift depth cues, perspective ambiguity",
86
- "rotational symmetry, non-Euclidean corridor, variable line thickness, alternating diagonals, dizzying parallax",
87
- "spiral tunnels, zigzag pathways, impossible cube, nested frames, sharp edges, stark contrast",
88
- ]
89
-
90
- WEIGHTS = [
91
- "hyper-detailed", "vector-sharp edges", "ultra high resolution",
92
- "minimal noise", "geometric precision", "dynamic parallax",
93
- "photorealistic lighting"
94
- ]
95
-
96
- MODS = [
97
- "isometric view", "45-degree inclination", "multi-angle perception",
98
- "viewer-dependent illusion", "top-down perspective"
99
- ]
100
-
101
- def vary_prompt(base: str) -> str:
102
- extra = ", ".join(random.sample(OPTICAL_ILLUSION_PRESETS, k=random.randint(1, 3)))
103
- hint = ", ".join(random.sample(WEIGHTS, k=random.randint(2, 4)))
104
- mod = random.choice(MODS)
105
- return f"{base}, {extra}, {hint}, {mod}"
106
-
107
- def prompt_hash(p: str) -> str:
108
- return hashlib.sha256(p.encode("utf-8")).hexdigest()[:16]
109
-
110
- # ---------------------------
111
- # Cache & Meta
112
- # ---------------------------
113
- def cache_path_for(prompt: str) -> Path:
114
- return CACHE_DIR / f"{prompt_hash(prompt)}.png"
115
-
116
- def cached(prompt: str) -> Path | None:
117
- p = cache_path_for(prompt)
118
- return p if p.exists() else None
119
-
120
- def write_meta(meta: dict, fname: Path):
121
- meta["timestamp"] = datetime.utcnow().isoformat()
122
- with open(META_DIR / (fname.stem + ".json"), "w", encoding="utf-8") as f:
123
- json.dump(meta, f, ensure_ascii=False, indent=2)
124
-
125
- # ---------------------------
126
- # Anti-error (Image AI)
127
- # ---------------------------
128
- class AntiError:
129
- def __init__(self):
130
- self.fail_count = 0
131
- self.circuit_open = False
132
- self.warned_no_model = False
133
-
134
- def reset(self):
135
- self.fail_count = 0
136
- self.circuit_open = False
137
-
138
- def bump(self):
139
- self.fail_count += 1
140
- if self.fail_count >= 5:
141
- self.circuit_open = True
142
-
143
- def warn_model_missing(self):
144
- if not self.warned_no_model:
145
- print("[!] No model loaded. Using local illusion fallback.")
146
- self.warned_no_model = True
147
-
148
- anti = AntiError()
149
-
150
- # ---------------------------
151
- # Local illusion generator
152
- # ---------------------------
153
- def local_illusion(size=1024) -> Image.Image:
154
- img = Image.new("RGB", (size, size), "white")
155
- draw = ImageDraw.Draw(img)
156
- step = 6
157
- for r in range(24, size//2, step):
158
- val = 0 if (r//step) % 2 == 0 else 255
159
- for x in range(size):
160
- if abs(x - size//2) == r:
161
- draw.line([(x, 0), (x, size)], fill=(val, val, val))
162
- for y in range(size):
163
- if abs(y - size//2) == r:
164
- draw.line([(0, y), (size, y)], fill=(val, val, val))
165
- hatch = Image.new("L", (size, size), color=255)
166
- hpx = hatch.load()
167
- for x in range(size):
168
- for y in range(size):
169
- if (x + y) % 18 == 0:
170
- hpx[x, y] = 0
171
- merged = Image.merge("RGB", (ImageOps.autocontrast(hatch), ImageOps.autocontrast(hatch), ImageOps.autocontrast(hatch)))
172
- merged = Image.blend(img, merged, 0.35).filter(ImageFilter.SHARPEN)
173
- merged = merged.rotate(random.choice([4, -6, 8, -10]), resample=Image.BICUBIC, expand=False)
174
- return merged
175
-
176
- # ---------------------------
177
- # Post-process filters
178
- # ---------------------------
179
- POST_MODES = [
180
- "illusion_boost", "bw_high", "moire_mix", "invert", "edge_halo",
181
- "soft_glow", "posterize4", "contrast_max", "rotate_slight", "grain_fine"
182
- ]
183
-
184
- def post_process(img: Image.Image, mode: str) -> Image.Image:
185
- if mode == "illusion_boost":
186
- out = ImageOps.autocontrast(img).filter(ImageFilter.UnsharpMask(radius=2, percent=180, threshold=3))
187
- elif mode == "bw_high":
188
- out = ImageOps.autocontrast(ImageOps.grayscale(img)).convert("RGB")
189
- elif mode == "moire_mix":
190
- out = ImageOps.posterize(img.filter(ImageFilter.DETAIL).filter(ImageFilter.SHARPEN), bits=4)
191
- elif mode == "invert":
192
- out = ImageOps.invert(img)
193
- elif mode == "edge_halo":
194
- out = img.filter(ImageFilter.UnsharpMask(radius=3, percent=240, threshold=2))
195
- elif mode == "soft_glow":
196
- blur = img.filter(ImageFilter.GaussianBlur(radius=2))
197
- out = Image.blend(img, blur, alpha=0.3)
198
- elif mode == "posterize4":
199
- out = ImageOps.posterize(img, bits=4)
200
- elif mode == "contrast_max":
201
- out = ImageOps.autocontrast(img)
202
- elif mode == "rotate_slight":
203
- out = img.rotate(random.choice([-3, 3, -2, 2]), resample=Image.BICUBIC, expand=False)
204
- elif mode == "grain_fine":
205
- out = img.filter(ImageFilter.SMOOTH_MORE)
206
- else:
207
- out = img
208
- return out
209
-
210
- # ---------------------------
211
- # Model Hub
212
- # ---------------------------
213
- class ModelHub:
214
- def __init__(self):
215
- self.device = "cuda" if torch.cuda.is_available() else "cpu"
216
- self.pipe = None
217
- self.model_id = None
218
-
219
- def load(self, model_id: str, fp16=True):
220
- self.model_id = model_id
221
- dtype = torch.float16 if (fp16 and self.device == "cuda") else torch.float32
222
- try:
223
- if "stable-diffusion-xl" in model_id:
224
- self.pipe = DiffusionPipeline.from_pretrained(model_id, torch_dtype=dtype, variant="fp16" if dtype==torch.float16 else None)
225
- else:
226
- self.pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=dtype)
227
- self.pipe = self.pipe.to(self.device)
228
- if self.device == "cuda":
229
- self.pipe.enable_attention_slicing()
230
- anti.reset()
231
- return True
232
- except Exception as e:
233
- print(f"[!] Model load error: {e}")
234
- self.pipe = None
235
- return False
236
-
237
- def generate(self, prompt: str, steps: int, guidance: float, seed: int | None, height=768, width=768) -> Image.Image:
238
- if self.pipe is None:
239
- anti.warn_model_missing()
240
- return local_illusion(size=min(height, width))
241
- generator = torch.Generator(device=self.device)
242
- if seed is not None and seed >= 0:
243
- generator = generator.manual_seed(seed)
244
- try:
245
- out = self.pipe(prompt, height=height, width=width, num_inference_steps=steps, guidance_scale=guidance, generator=generator)
246
- return out.images[0]
247
- except Exception as e:
248
- print(f"[!] Generate error: {e}")
249
- anti.bump()
250
- return local_illusion(size=min(height, width))
251
-
252
- hub = ModelHub()
253
-
254
- # ---------------------------
255
- # Orchestrator (Image Queue)
256
- # ---------------------------
257
- class Orchestrator:
258
- def __init__(self):
259
- self.queue = []
260
- self.running = False
261
-
262
- def enqueue(self, prompt, count, vary, steps, guidance, seed, size, post_mode):
263
- for _ in range(count):
264
- if len(self.queue) >= MAX_QUEUE:
265
- break
266
- self.queue.append({
267
- "prompt": prompt,
268
- "vary": vary,
269
- "steps": steps,
270
- "guidance": guidance,
271
- "seed": seed,
272
- "size": size,
273
- "post_mode": post_mode
274
- })
275
-
276
- def _do_item(self, item):
277
- p = item["prompt"]
278
- if item["vary"]:
279
- p = vary_prompt(p)
280
- cp = cached(p)
281
- if cp:
282
- img = Image.open(cp).convert("RGB")
283
- else:
284
- img = hub.generate(p, item["steps"], item["guidance"], item["seed"], item["size"], item["size"])
285
- try:
286
- cache_path = cache_path_for(p)
287
- img.save(cache_path, "PNG", optimize=True)
288
- except Exception as e:
289
- print(f"[!] Cache save error: {e}")
290
- if item["post_mode"]:
291
- img = post_process(img, item["post_mode"])
292
- fname = OUTPUT_DIR / f"{datetime.utcnow().strftime('%Y%m%d_%H%M%S_%f')}_{prompt_hash(p)}.png"
293
- img.save(fname, "PNG", optimize=True)
294
- write_meta({"prompt": p, "model": hub.model_id or "local_illusion", "post_mode": item["post_mode"]}, fname)
295
- return fname
296
-
297
- def run(self, on_log):
298
- if self.running:
299
- return
300
- self.running = True
301
- try:
302
- while self.queue:
303
- item = self.queue.pop(0)
304
- try:
305
- path = self._do_item(item)
306
- on_log(f" -> Saved: {path}")
307
- except Exception as e:
308
- on_log(f" x Failed: {e}")
309
- finally:
310
- self.running = False
311
-
312
- orch = Orchestrator()
313
-
314
- # ---------------------------
315
- # Code Learner Worm
316
- # ---------------------------
317
- class CodeLearner:
318
- """
319
- A self-improving patcher:
320
- - Scans this file and plugins for improvement opportunities.
321
- - Runs tests/health checks (syntax, import, basic run).
322
- - Generates small patches (safe transformations) and applies if all checks pass.
323
- - Keeps history in harvest/patches.json.
324
- """
325
- def __init__(self, root: Path):
326
- self.root = root
327
- self.target_file = root / Path(__file__).name
328
- self.patches_file = HARVEST_DIR / "patches.json"
329
- if not self.patches_file.exists():
330
- self._write_json(self.patches_file, [])
331
-
332
- def _read(self, p: Path) -> str:
333
- return p.read_text(encoding="utf-8")
334
-
335
- def _write(self, p: Path, s: str):
336
- p.write_text(s, encoding="utf-8")
337
-
338
- def _write_json(self, p: Path, obj):
339
- with open(p, "w", encoding="utf-8") as f:
340
- json.dump(obj, f, ensure_ascii=False, indent=2)
341
-
342
- def _record_patch(self, summary, diff_preview):
343
- data = json.loads(self.patches_file.read_text(encoding="utf-8"))
344
- data.append({
345
- "time": datetime.utcnow().isoformat(),
346
- "summary": summary,
347
- "preview": diff_preview[:1000]
348
- })
349
- self._write_json(self.patches_file, data)
350
-
351
- def _basic_health(self) -> bool:
352
- # Syntax check by importing this file as a module
353
- try:
354
- spec = importlib.util.spec_from_file_location("oiah", self.target_file)
355
- module = importlib.util.module_from_spec(spec)
356
- spec.loader.exec_module(module) # type: ignore
357
- return True
358
- except Exception as e:
359
- print(f"[WORM] Health check failed: {e}")
360
- return False
361
-
362
- def _safe_transforms(self, code: str) -> list[tuple[str, str]]:
363
- transforms = []
364
- # Example 1: ensure UI theme fallback exists (idempotent)
365
- if "style.theme_use(\"clam\")" not in code:
366
- transforms.append(("Add clam theme fallback",
367
- "style = ttk.Style()\n try:\n style.theme_use(\"clam\")\n except Exception:\n pass\n"))
368
- # Example 2: add plugin loader hook if missing
369
- if "load_plugins()" not in code:
370
- transforms.append(("Add plugin loader hook", "load_plugins()\n"))
371
- # Example 3: add Open Models expansion comment marker
372
- if "# Add more open models" not in code:
373
- transforms.append(("Add models marker", "# Add more open models if diffusers supports them\n"))
374
- return transforms
375
-
376
- def _apply_transform(self, original: str, transform_snippet: str) -> str:
377
- # Simple heuristic: insert before main()
378
- insertion_point = original.rfind("def main():")
379
- if insertion_point == -1:
380
- return original + "\n" + transform_snippet
381
- head = original[:insertion_point]
382
- tail = original[insertion_point:]
383
- return head + "\n" + transform_snippet + "\n" + tail
384
-
385
- def improve(self, on_log):
386
- code = self._read(self.target_file)
387
- transforms = self._safe_transforms(code)
388
- if not transforms:
389
- on_log("[WORM] Nothing to improve (idempotent).")
390
- return
391
- for summary, snippet in transforms:
392
- on_log(f"[WORM] Proposing patch: {summary}")
393
- patched = self._apply_transform(code, snippet)
394
- backup = self.target_file.with_suffix(".bak")
395
- shutil.copyfile(self.target_file, backup)
396
- self._write(self.target_file, patched)
397
- if self._basic_health():
398
- diff_preview = self._diff_preview(code, patched)
399
- self._record_patch(summary, diff_preview)
400
- on_log(f"[WORM] Patch applied: {summary}")
401
- code = patched # continue from patched version
402
- else:
403
- on_log(f"[WORM] Patch reverted: {summary}")
404
- shutil.copyfile(backup, self.target_file)
405
- backup.unlink(missing_ok=True)
406
-
407
- def _diff_preview(self, old: str, new: str) -> str:
408
- import difflib
409
- return "\n".join(difflib.unified_diff(old.splitlines(), new.splitlines(), lineterm=""))
410
-
411
- # ---------------------------
412
- # Open-Source Harvester
413
- # ---------------------------
414
- class Harvester:
415
- """
416
- Finds and connects open resources:
417
- - Index local folders and user-provided URLs (git/http) without any paid APIs.
418
- - Clones git repos and downloads raw files into harvest/.
419
- - Loads plugin modules from plugins/ to extend functionality.
420
- """
421
- def __init__(self, harvest_dir: Path, plugins_dir: Path):
422
- self.harvest_dir = harvest_dir
423
- self.plugins_dir = plugins_dir
424
-
425
- def index_local(self, paths: list[Path]) -> list[Path]:
426
- indexed = []
427
- for p in paths:
428
- if p.exists():
429
- for root, dirs, files in os.walk(p):
430
- for f in files:
431
- indexed.append(Path(root) / f)
432
- (self.harvest_dir / "index.json").write_text(json.dumps([str(x) for x in indexed], indent=2), encoding="utf-8")
433
- return indexed
434
-
435
- def clone_git(self, url: str, name_hint: str | None = None) -> Path | None:
436
- # Requires git installed; no accounts; open repos only
437
- out = self.harvest_dir / (name_hint or f"repo_{prompt_hash(url)}")
438
- try:
439
- subprocess.check_call(["git", "clone", "--depth", "1", url, str(out)])
440
- return out
441
- except Exception as e:
442
- print(f"[HARVEST] Git clone failed: {e}")
443
- return None
444
-
445
- def http_fetch(self, url: str, name: str | None = None) -> Path | None:
446
- # Simple open HTTP fetch (no paid API); may need allowlist
447
- import urllib.request
448
- try:
449
- data = urllib.request.urlopen(url, timeout=30).read()
450
- target = self.harvest_dir / (name or f"file_{prompt_hash(url)}")
451
- target.write_bytes(data)
452
- return target
453
- except Exception as e:
454
- print(f"[HARVEST] HTTP fetch failed: {e}")
455
- return None
456
-
457
- def load_plugins(self) -> list[str]:
458
- loaded = []
459
- for py in self.plugins_dir.glob("*.py"):
460
- try:
461
- spec = importlib.util.spec_from_file_location(py.stem, py)
462
- module = importlib.util.module_from_spec(spec)
463
- spec.loader.exec_module(module) # type: ignore
464
- loaded.append(py.stem)
465
- except Exception as e:
466
- print(f"[PLUGINS] Failed to load {py}: {e}")
467
- (self.harvest_dir / "plugins_loaded.json").write_text(json.dumps(loaded, indent=2), encoding="utf-8")
468
- return loaded
469
-
470
- harvester = Harvester(HARVEST_DIR, PLUGINS_DIR)
471
-
472
- def load_plugins():
473
- harvester.load_plugins()
474
-
475
- # ---------------------------
476
- # GUI (Bilingual)
477
- # ---------------------------
478
- class App:
479
- def __init__(self, root):
480
- self.root = root
481
- root.title(APP_NAME)
482
- root.geometry("1080x780")
483
-
484
- self.lang = tk.StringVar(value="FA")
485
- self.prompt = tk.StringVar()
486
- self.count = tk.IntVar(value=3)
487
- self.vary = tk.BooleanVar(value=True)
488
- self.steps = tk.IntVar(value=DEFAULT_STEPS)
489
- self.guidance = tk.DoubleVar(value=DEFAULT_GUIDANCE)
490
- self.seed = tk.IntVar(value=SEED_AUTOMATIC)
491
- self.size = tk.IntVar(value=768)
492
- self.post_mode = tk.StringVar(value="")
493
- self.model_choice = tk.StringVar(value=list(OPEN_MODELS.keys())[0])
494
-
495
- # Harvester inputs
496
- self.harvest_url = tk.StringVar()
497
- self.harvest_local = tk.StringVar()
498
-
499
- self._build_ui()
500
- self._set_texts()
501
- self._log_header()
502
-
503
- def _build_ui(self):
504
- top = ttk.Frame(self.root)
505
- top.pack(fill="x", padx=10, pady=10)
506
- ttk.Label(top, text="Language / زبان").pack(side="left")
507
- ttk.Combobox(top, textvariable=self.lang, values=["FA", "EN"], width=5, state="readonly").pack(side="left", padx=8)
508
- ttk.Button(top, text="Apply/اعمال", command=self._set_texts).pack(side="left", padx=8)
509
-
510
- model_frame = ttk.LabelFrame(self.root, text="Model / مدل")
511
- model_frame.pack(fill="x", padx=10, pady=6)
512
- ttk.Label(model_frame, text="Select model:").grid(row=0, column=0, sticky="w", padx=6, pady=6)
513
- ttk.Combobox(model_frame, textvariable=self.model_choice, values=list(OPEN_MODELS.keys()), width=28, state="readonly").grid(row=0, column=1, sticky="w", padx=6, pady=6)
514
- ttk.Button(model_frame, text="Load model", command=self.load_model).grid(row=0, column=2, padx=6, pady=6)
515
-
516
- mid = ttk.LabelFrame(self.root, text="Controls / کنترل‌ها")
517
- mid.pack(fill="x", padx=10, pady=6)
518
- self.lbl_prompt = ttk.Label(mid, text="")
519
- self.lbl_prompt.grid(row=0, column=0, sticky="w", padx=6, pady=6)
520
- ttk.Entry(mid, textvariable=self.prompt, width=72).grid(row=0, column=1, sticky="we", padx=6, pady=6)
521
-
522
- self.lbl_count = ttk.Label(mid, text="")
523
- self.lbl_count.grid(row=1, column=0, sticky="w", padx=6, pady=6)
524
- ttk.Spinbox(mid, from_=1, to=50, textvariable=self.count, width=6).grid(row=1, column=1, sticky="w", padx=6, pady=6)
525
-
526
- self.lbl_vary = ttk.Label(mid, text="")
527
- self.lbl_vary.grid(row=2, column=0, sticky="w", padx=6, pady=6)
528
- ttk.Checkbutton(mid, text="", variable=self.vary).grid(row=2, column=1, sticky="w", padx=6, pady=6)
529
-
530
- self.lbl_steps = ttk.Label(mid, text="")
531
- self.lbl_steps.grid(row=3, column=0, sticky="w", padx=6, pady=6)
532
- ttk.Spinbox(mid, from_=5, to=100, textvariable=self.steps, width=6).grid(row=3, column=1, sticky="w", padx=6, pady=6)
533
-
534
- self.lbl_guidance = ttk.Label(mid, text="")
535
- self.lbl_guidance.grid(row=4, column=0, sticky="w", padx=6, pady=6)
536
- ttk.Spinbox(mid, from_=0.0, to=20.0, increment=0.5, textvariable=self.guidance, width=6).grid(row=4, column=1, sticky="w", padx=6, pady=6)
537
-
538
- self.lbl_seed = ttk.Label(mid, text="")
539
- self.lbl_seed.grid(row=5, column=0, sticky="w", padx=6, pady=6)
540
- ttk.Spinbox(mid, from_=-1, to=2**31-1, textvariable=self.seed, width=12).grid(row=5, column=1, sticky="w", padx=6, pady=6)
541
-
542
- self.lbl_size = ttk.Label(mid, text="")
543
- self.lbl_size.grid(row=6, column=0, sticky="w", padx=6, pady=6)
544
- ttk.Spinbox(mid, from_=256, to=1024, increment=64, textvariable=self.size, width=6).grid(row=6, column=1, sticky="w", padx=6, pady=6)
545
-
546
- self.lbl_pp = ttk.Label(mid, text="")
547
- self.lbl_pp.grid(row=7, column=0, sticky="w", padx=6, pady=6)
548
- ttk.Combobox(mid, textvariable=self.post_mode, values=[""] + POST_MODES, width=20, state="readonly").grid(row=7, column=1, sticky="w", padx=6, pady=6)
549
-
550
- btns = ttk.Frame(mid)
551
- btns.grid(row=8, column=0, columnspan=2, sticky="we", padx=6, pady=6)
552
- self.btn_run = ttk.Button(btns, text="", command=self.run_queue)
553
- self.btn_run.pack(side="left", padx=6)
554
- ttk.Button(btns, text="Open outputs", command=self.open_outputs).pack(side="left", padx=6)
555
- ttk.Button(btns, text="Illusion offline test", command=self.make_offline_illusion).pack(side="left", padx=6)
556
-
557
- # Harvester panel
558
- hframe = ttk.LabelFrame(self.root, text="Open-Source Harvester")
559
- hframe.pack(fill="x", padx=10, pady=10)
560
- ttk.Label(hframe, text="Git/HTTP URL:").grid(row=0, column=0, sticky="w", padx=6, pady=6)
561
- ttk.Entry(hframe, textvariable=self.harvest_url, width=60).grid(row=0, column=1, sticky="we", padx=6, pady=6)
562
- ttk.Button(hframe, text="Clone/Fetch", command=self.harvest_fetch).grid(row=0, column=2, padx=6, pady=6)
563
-
564
- ttk.Label(hframe, text="Local path to index:").grid(row=1, column=0, sticky="w", padx=6, pady=6)
565
- ttk.Entry(hframe, textvariable=self.harvest_local, width=60).grid(row=1, column=1, sticky="we", padx=6, pady=6)
566
- ttk.Button(hframe, text="Index", command=self.harvest_index).grid(row=1, column=2, padx=6, pady=6)
567
-
568
- # Learner panel
569
- lframe = ttk.LabelFrame(self.root, text="Code Learner Worm")
570
- lframe.pack(fill="x", padx=10, pady=10)
571
- ttk.Button(lframe, text="Analyze & Improve", command=self.run_worm).grid(row=0, column=0, padx=6, pady=6)
572
- ttk.Button(lframe, text="Load plugins", command=load_plugins).grid(row=0, column=1, padx=6, pady=6)
573
-
574
- self.log = tk.Text(self.root, height=18, wrap="word")
575
- self.log.pack(fill="both", expand=True, padx=10, pady=10)
576
-
577
- status_bar = ttk.Frame(self.root)
578
- status_bar.pack(fill="x", padx=10, pady=5)
579
- self.status = tk.StringVar(value="Ready")
580
- self.lbl_status = ttk.Label(status_bar, textvariable=self.status)
581
- self.lbl_status.pack(side="left")
582
-
583
- def _set_texts(self):
584
- if self.lang.get() == "FA":
585
- self.lbl_prompt.config(text="متن راهنمای تصویر (پرومپت):")
586
- self.lbl_count.config(text="تعداد تولید:")
587
- self.lbl_vary.config(text="تنوع‌دهی خودکار پرومپت:")
588
- self.lbl_steps.config(text="تعداد گام‌ها:")
589
- self.lbl_guidance.config(text="راهنمایی (Guidance):")
590
- self.lbl_seed.config(text="Seed (برای تکرارپذیری، -1 خودکار):")
591
- self.lbl_size.config(text="اندازه (پیکسل مربع):")
592
- self.lbl_pp.config(text="پس‌پردازش:")
593
- self.btn_run.config(text="شروع تولید")
594
- self.root.title(f"{APP_NAME} - رابط فارسی")
595
- else:
596
- self.lbl_prompt.config(text="Image prompt:")
597
- self.lbl_count.config(text="Count:")
598
- self.lbl_vary.config(text="Auto prompt variation:")
599
- self.lbl_steps.config(text="Steps:")
600
- self.lbl_guidance.config(text="Guidance:")
601
- self.lbl_seed.config(text="Seed (-1 automatic):")
602
- self.lbl_size.config(text="Size (square px):")
603
- self.lbl_pp.config(text="Post-process:")
604
- self.btn_run.config(text="Start")
605
- self.root.title(f"{APP_NAME} - English UI")
606
-
607
- def _log_header(self):
608
- self.append_log(f"[{APP_NAME}] Ready. Select a model, enter prompt, and start.")
609
-
610
- def append_log(self, s):
611
- self.log.insert("end", s + "\n")
612
- self.log.see("end")
613
-
614
- def open_outputs(self):
615
- path = str(OUTPUT_DIR)
616
- try:
617
- if os.name == "nt":
618
- os.startfile(path)
619
- elif sys.platform == "darwin":
620
- os.system(f'open "{path}"')
621
- else:
622
- os.system(f'xdg-open "{path}"')
623
- except Exception:
624
- messagebox.showinfo("Info", f"Outputs at: {path}")
625
-
626
- def load_model(self):
627
- name = self.model_choice.get()
628
- model_id = OPEN_MODELS.get(name)
629
- if not model_id:
630
- messagebox.showerror("Error", "Model id not found.")
631
- return
632
- self.status.set("Loading model...")
633
- self.append_log(f"[+] Loading: {name} -> {model_id}")
634
- self.root.update_idletasks()
635
- ok = hub.load(model_id, fp16=True)
636
- if ok:
637
- self.status.set("Model loaded")
638
- self.append_log(" -> Model ready.")
639
- else:
640
- self.status.set("Model load failed (fallback active)")
641
- self.append_log(" x Model load failed; using local illusion fallback.")
642
-
643
- def run_queue(self):
644
- base_prompt = self.prompt.get().strip()
645
- if not base_prompt:
646
- messagebox.showwarning("Warn", "Please enter a prompt / لطفاً پرومپت را وارد کنید")
647
- return
648
- count = self.count.get()
649
- vary = self.vary.get()
650
- steps = self.steps.get()
651
- guidance = self.guidance.get()
652
- seed = self.seed.get()
653
- size = self.size.get()
654
- pp = self.post_mode.get()
655
-
656
- orch.enqueue(base_prompt, count, vary, steps, guidance, seed, size, pp)
657
- self.status.set("Running...")
658
- self.append_log(f"[+] {datetime.now().strftime('%H:%M:%S')} Enqueued: {base_prompt} x {count} vary={vary} steps={steps} guidance={guidance} seed={seed} size={size} pp={pp}")
659
-
660
- def worker():
661
- try:
662
- orch.run(self.append_log)
663
- finally:
664
- self.status.set("Done")
665
- threading.Thread(target=worker, daemon=True).start()
666
-
667
- def make_offline_illusion(self):
668
- img = local_illusion(size=self.size.get())
669
- fname = OUTPUT_DIR / f"{datetime.utcnow().strftime('%Y%m%d_%H%M%S_%f')}_offline_illusion.png"
670
- img.save(fname, "PNG", optimize=True)
671
- write_meta({"prompt": "offline_illusion", "model": "local_illusion"}, fname)
672
- self.append_log(f" -> Saved offline illusion: {fname}")
673
-
674
- def harvest_fetch(self):
675
- url = self.harvest_url.get().strip()
676
- if not url:
677
- messagebox.showwarning("Warn", "Enter Git/HTTP URL / لطفاً URL را وارد کنید")
678
- return
679
- self.append_log(f"[HARVEST] Fetching: {url}")
680
- if url.endswith(".git"):
681
- path = harvester.clone_git(url)
682
- else:
683
- name = Path(url).name
684
- path = harvester.http_fetch(url, name=name)
685
- if path:
686
- self.append_log(f" -> Stored at: {path}")
687
- else:
688
- self.append_log(" x Fetch failed")
689
-
690
- def harvest_index(self):
691
- p = self.harvest_local.get().strip()
692
- if not p:
693
- messagebox.showwarning("Warn", "Enter local path / لطفاً مسیر محلی را وارد کنید")
694
- return
695
- path = Path(p).resolve()
696
- self.append_log(f"[HARVEST] Indexing: {path}")
697
- files = harvester.index_local([path])
698
- self.append_log(f" -> Indexed {len(files)} files")
699
-
700
- def run_worm(self):
701
- self.append_log("[WORM] Starting self-improvement...")
702
- worm = CodeLearner(ROOT_DIR)
703
- worm.improve(self.append_log)
704
- self.append_log("[WORM] Done.")
705
-
706
- # ---------------------------
707
- # Entry
708
- # ---------------------------
709
- def main():
710
- # Optional: load plugins at start if present
711
- load_plugins()
712
- root = tk.Tk()
713
- style = ttk.Style()
714
- try:
715
- style.theme_use("clam")
716
- except Exception:
717
- pass
718
- app = App(root)
719
- root.mainloop()
720
-
721
- if __name__ == "__main__":
722
- main()