JG1310 commited on
Commit
b88939b
·
verified ·
1 Parent(s): aec36cc

promote hybrid: 8B map + 30B read + split build (VRAM) + FLUX preload + CUDA cache

Browse files
Files changed (1) hide show
  1. arcana/build.py +45 -25
arcana/build.py CHANGED
@@ -101,11 +101,13 @@ def _back_prompt(style_suffix: str) -> str:
101
 
102
 
103
  # ------------------------------------------------------------------ build
104
- def build_deck(theme: str, visual_style: str = "rider-waite-smith",
105
- custom_style: str | None = None,
106
- progress: Progress | None = None) -> dict:
107
- """Design + paint a full deck. Writes image files under decks/<deck_id>/ and
108
- returns the in-memory deck dict (NOT yet saved — call save_deck to persist)."""
 
 
109
  def emit(msg, frac):
110
  if progress:
111
  progress(msg, frac)
@@ -113,43 +115,53 @@ def build_deck(theme: str, visual_style: str = "rider-waite-smith",
113
  theme = (theme or "").strip()
114
  style_id, style_suffix = resolve_style(visual_style, custom_style)
115
  deck_id = f"{slugify(theme)}-{style_id}-{int(time.time())}"
116
- out_dir = deck_dir(deck_id)
117
- os.makedirs(out_dir, exist_ok=True)
118
  seed_base = _seed_base(theme, style_id)
119
 
120
  llm = get_llm()
121
- emit("Consulting the deck designer…", 0.02)
122
  deck = design_deck(theme, llm=llm)
123
- emit("The loremaster reinterprets the cards…", 0.05)
124
- try:
125
- deck = refine_deck(deck, llm=llm)
126
- except Exception:
127
- pass # keep the designer's draft meanings if refinement hiccups
 
128
 
129
  deck.update({
130
- "deck_id": deck_id,
131
- "theme": theme,
132
- "visual_style": style_id,
133
- "style_suffix": style_suffix,
134
- "seed_base": seed_base,
135
  })
 
 
 
 
 
 
 
 
 
136
 
137
- ig = get_imagegen(deck_style=style_suffix)
 
 
 
 
138
 
139
- emit("Pressing the deck-back…", 0.08)
140
  back_path = os.path.join(out_dir, "back.png")
141
  try:
142
- back = ig.generate(_back_prompt(style_suffix), seed=seed_base - 1)
143
  back.save(back_path)
144
  deck["back_path"] = os.path.relpath(back_path, ROOT)
145
- deck["back_disp"] = save_display(back, deck_id, "back") # light WEBP for the flip
146
  except Exception:
147
  deck["back_path"] = deck["back_disp"] = None
148
 
149
  n = len(deck["cards"])
150
  for i, c in enumerate(deck["cards"]):
151
  num = c["arcana_number"]
152
- emit(f"Painting {c['concept']}…", 0.10 + 0.88 * i / n)
153
  seed = seed_base + num
154
  art = ig.generate(c["art_prompt"], seed=seed)
155
  card = compose_card(art, c["concept"], ROMAN_BY_NUMBER[num])
@@ -158,13 +170,21 @@ def build_deck(theme: str, visual_style: str = "rider-waite-smith",
158
  card.save(path)
159
  c["seed"] = seed
160
  c["art_path"] = os.path.relpath(path, ROOT)
161
- c["disp_path"] = save_display(card, deck_id, base) # small WEBP (grid)
162
- c["full_path"] = save_full(card, deck_id, base) # capped WEBP (on click)
163
 
164
  emit("The deck is ready.", 1.0)
165
  return deck
166
 
167
 
 
 
 
 
 
 
 
 
168
  # ------------------------------------------------------------------ persistence
169
  def save_deck(deck: dict) -> str:
170
  """Write deck.json into the deck's dir, registering it as a saved deck (§12)."""
 
101
 
102
 
103
  # ------------------------------------------------------------------ build
104
+ def design_phase(theme: str, visual_style: str = "rider-waite-smith",
105
+ custom_style: str | None = None,
106
+ progress: Progress | None = None) -> dict:
107
+ """LLM phase ONLY (the 30B mapping + loremaster). Returns a deck dict with
108
+ concepts + meanings but NO images yet. Split out so it can run in its own
109
+ @spaces.GPU call — the 30B and the FLUX image model are too big to share the
110
+ 48GB GPU, so we never load them together."""
111
  def emit(msg, frac):
112
  if progress:
113
  progress(msg, frac)
 
115
  theme = (theme or "").strip()
116
  style_id, style_suffix = resolve_style(visual_style, custom_style)
117
  deck_id = f"{slugify(theme)}-{style_id}-{int(time.time())}"
118
+ os.makedirs(deck_dir(deck_id), exist_ok=True)
 
119
  seed_base = _seed_base(theme, style_id)
120
 
121
  llm = get_llm()
122
+ emit("Consulting the deck designer…", 0.05)
123
  deck = design_deck(theme, llm=llm)
124
+ if os.environ.get("SKIP_LOREMASTER", "").lower() not in ("1", "true", "yes"):
125
+ emit("The loremaster reinterprets the cards…", 0.10)
126
+ try:
127
+ deck = refine_deck(deck, llm=llm)
128
+ except Exception:
129
+ pass # keep the designer's draft meanings if refinement hiccups
130
 
131
  deck.update({
132
+ "deck_id": deck_id, "theme": theme, "visual_style": style_id,
133
+ "style_suffix": style_suffix, "seed_base": seed_base,
 
 
 
134
  })
135
+ return deck
136
+
137
+
138
+ def paint_phase(deck: dict, progress: Progress | None = None) -> dict:
139
+ """Image phase ONLY (FLUX). Paints the back + 22 cards into the deck dir and
140
+ fills in the image paths. Runs in its own @spaces.GPU call (see design_phase)."""
141
+ def emit(msg, frac):
142
+ if progress:
143
+ progress(msg, frac)
144
 
145
+ deck_id = deck["deck_id"]
146
+ out_dir = deck_dir(deck_id)
147
+ os.makedirs(out_dir, exist_ok=True)
148
+ seed_base = deck["seed_base"]
149
+ ig = get_imagegen(deck_style=deck.get("style_suffix"))
150
 
151
+ emit("Pressing the deck-back…", 0.15)
152
  back_path = os.path.join(out_dir, "back.png")
153
  try:
154
+ back = ig.generate(_back_prompt(deck.get("style_suffix") or ""), seed=seed_base - 1)
155
  back.save(back_path)
156
  deck["back_path"] = os.path.relpath(back_path, ROOT)
157
+ deck["back_disp"] = save_display(back, deck_id, "back")
158
  except Exception:
159
  deck["back_path"] = deck["back_disp"] = None
160
 
161
  n = len(deck["cards"])
162
  for i, c in enumerate(deck["cards"]):
163
  num = c["arcana_number"]
164
+ emit(f"Painting {c['concept']}…", 0.18 + 0.80 * i / n)
165
  seed = seed_base + num
166
  art = ig.generate(c["art_prompt"], seed=seed)
167
  card = compose_card(art, c["concept"], ROMAN_BY_NUMBER[num])
 
170
  card.save(path)
171
  c["seed"] = seed
172
  c["art_path"] = os.path.relpath(path, ROOT)
173
+ c["disp_path"] = save_display(card, deck_id, base)
174
+ c["full_path"] = save_full(card, deck_id, base)
175
 
176
  emit("The deck is ready.", 1.0)
177
  return deck
178
 
179
 
180
+ def build_deck(theme: str, visual_style: str = "rider-waite-smith",
181
+ custom_style: str | None = None,
182
+ progress: Progress | None = None) -> dict:
183
+ """Convenience: design + paint in one go (used off-Space / non-split callers)."""
184
+ deck = design_phase(theme, visual_style, custom_style, progress=progress)
185
+ return paint_phase(deck, progress=progress)
186
+
187
+
188
  # ------------------------------------------------------------------ persistence
189
  def save_deck(deck: dict) -> str:
190
  """Write deck.json into the deck's dir, registering it as a saved deck (§12)."""