topguy commited on
Commit
42bdcb2
·
1 Parent(s): 617d464

feat: refine AI backend options and UI layout

Browse files

- Curated Hugging Face model lists (4 text, 4 image models).
- Added manual Inference Provider support for HF backend (e.g., fal-ai).
- Set default HF provider to 'auto' for robust model discovery.
- Relocated 'Generate Image' button to the right column for better UX.
- Improved error handling for prompt refinement to prevent UI प्रदूषण.
- Added MIT License and updated README with new features and usage info.

Files changed (5) hide show
  1. LICENSE +21 -0
  2. README.md +20 -15
  3. modules/config.py +13 -0
  4. modules/integrations.py +38 -22
  5. modules/ui_layout.py +59 -18
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Topguy (and contributors)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -15,9 +15,13 @@ RPGPortrait is a Gradio-based web application that helps users build highly deta
15
 
16
  ## Features
17
  - **25+ Character Parameters**: Deep customization including Identity, Appearance, Equipment, Environment, VFX, and Technical settings.
18
- - **🧠 AI Refinement**: Intelligent prompt enhancement using **Gemini (Cloud)**, **Hugging Face (Cloud)**, or **Ollama (Local)**.
19
  - **🛠️ Externalized Prompts**: Tweak the core AI system instructions by editing `prompts.yaml`.
20
  - **🖼️ Multi-Backend Image Gen**: Toggle between **Gemini (Cloud)**, **Hugging Face (Cloud)**, and **ComfyUI (Local)**.
 
 
 
 
21
  - **🔍 Dynamic Model Discovery**: Automatically pings local Ollama and ComfyUI servers to fetch available models and hide unavailable backends.
22
  - **Workflow Injection**: Automated prompt, resolution, and seed injection into custom ComfyUI workflows.
23
  - **💾 Save & Load**: Export your character configurations as JSON files and import them back to restore your exact selections.
@@ -25,6 +29,7 @@ RPGPortrait is a Gradio-based web application that helps users build highly deta
25
  - **🎒 Dual Accessories**: Select up to two different accessories for your character.
26
  - **📥 Pro Downloads**: Standard PNG downloads for portraits with friendly filenames.
27
  - **Randomization**: Check individual 🎲 boxes to randomize specific features on regeneration.
 
28
  - **YAML Data Storage**: Easily add or modify races, classes, backgrounds, and templates in `features.yaml`.
29
 
30
  ## Installation
@@ -41,19 +46,17 @@ RPGPortrait is a Gradio-based web application that helps users build highly deta
41
  ```bash
42
  pip install -r requirements.txt
43
  ```
44
- 5. **Set up Gemini API**:
45
  - Create a `.env` file in the root directory.
46
  - Add your keys and connection info:
47
  ```env
48
- GEMINI_API_KEY=your_api_key_here
 
49
  COMFY_HOST=127.0.0.1
50
  COMFY_PORT=8188
51
  OLLAMA_HOST=127.0.0.1
52
  OLLAMA_PORT=11434
53
- OLLAMA_MODEL=llama3
54
  ```
55
- 6. **Configure AI Prompts**:
56
- - Modify `prompts.yaml` to adjust the refinement logic without changing code.
57
 
58
  ## Usage
59
 
@@ -62,13 +65,15 @@ RPGPortrait is a Gradio-based web application that helps users build highly deta
62
  python app.py
63
  ```
64
  2. **Access the UI**: Open your browser and navigate to `http://127.0.0.1:7860`.
65
- 3. **Build your prompt**: Select features and watch the technical prompt update in real-time.
66
- 4. **Refine & Generate**:
67
- - Choose a **Refinement Backend** (Gemini or Ollama).
68
- - If using Ollama, select your model.
69
- - Click **🧠 Refine with Gemini** (or Ollama depending on selection) to polish your prompt.
70
- - Click **🖼️ Generate Image** to create your portrait.
71
- 5. **Save/Load**: Use the 💾 and 📂 buttons to manage your character library.
 
72
 
73
- ## Configuration
74
- All dropdown options and the final prompt template are defined in `features.yaml`. You can customize the behavior of the prompt generator without touching the Python code.
 
 
15
 
16
  ## Features
17
  - **25+ Character Parameters**: Deep customization including Identity, Appearance, Equipment, Environment, VFX, and Technical settings.
18
+ - **🧠 AI Refinement**: Intelligent prompt enhancement using **Gemini (Cloud)**, **Curated Hugging Face Models**, or **Ollama (Local)**.
19
  - **🛠️ Externalized Prompts**: Tweak the core AI system instructions by editing `prompts.yaml`.
20
  - **🖼️ Multi-Backend Image Gen**: Toggle between **Gemini (Cloud)**, **Hugging Face (Cloud)**, and **ComfyUI (Local)**.
21
+ - **⚡ Hugging Face Pro Features**:
22
+ - Curated list of 4 text and 4 image models.
23
+ - Manual **Inference Provider** support (e.g., `fal-ai`, `black-forest-labs`) to bypass rate limits or use partner backends.
24
+ - Automated "auto" provider selection by default.
25
  - **🔍 Dynamic Model Discovery**: Automatically pings local Ollama and ComfyUI servers to fetch available models and hide unavailable backends.
26
  - **Workflow Injection**: Automated prompt, resolution, and seed injection into custom ComfyUI workflows.
27
  - **💾 Save & Load**: Export your character configurations as JSON files and import them back to restore your exact selections.
 
29
  - **🎒 Dual Accessories**: Select up to two different accessories for your character.
30
  - **📥 Pro Downloads**: Standard PNG downloads for portraits with friendly filenames.
31
  - **Randomization**: Check individual 🎲 boxes to randomize specific features on regeneration.
32
+ - **🛡️ Robust Error Handling**: AI refinement errors are logged to the console and displayed in the UI status area without polluting your current prompt.
33
  - **YAML Data Storage**: Easily add or modify races, classes, backgrounds, and templates in `features.yaml`.
34
 
35
  ## Installation
 
46
  ```bash
47
  pip install -r requirements.txt
48
  ```
49
+ 5. **Set up API Keys**:
50
  - Create a `.env` file in the root directory.
51
  - Add your keys and connection info:
52
  ```env
53
+ GEMINI_API_KEY=your_gemini_key
54
+ HF_TOKEN=your_huggingface_token # Required for Cloud backends
55
  COMFY_HOST=127.0.0.1
56
  COMFY_PORT=8188
57
  OLLAMA_HOST=127.0.0.1
58
  OLLAMA_PORT=11434
 
59
  ```
 
 
60
 
61
  ## Usage
62
 
 
65
  python app.py
66
  ```
67
  2. **Access the UI**: Open your browser and navigate to `http://127.0.0.1:7860`.
68
+ 3. **Build your prompt**: Select features in the left column; the technical prompt updates in real-time.
69
+ 4. **Refine Prompt**:
70
+ - Choose a **Refinement Backend** in the configuration panel.
71
+ - Click **🧠 Refine Prompt** in the right column to polish your description.
72
+ 5. **Generate Image**:
73
+ - Select an **Image Generation Backend**.
74
+ - Click **🖼️ Generate Image** (located directly under the portrait output) to create your character.
75
+ 6. **Save/Load**: Use the 💾 and 📂 buttons to manage your character library.
76
 
77
+ ## License
78
+
79
+ This project is licensed under the [MIT License](LICENSE).
modules/config.py CHANGED
@@ -19,6 +19,19 @@ HF_BASE_URL = "https://router.huggingface.co/v1"
19
  HF_TEXT_MODEL = "Qwen/Qwen2.5-72B-Instruct"
20
  HF_IMAGE_MODEL = "black-forest-labs/FLUX.1-dev"
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  # Gemini Settings
23
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
24
  GEMINI_TEXT_MODEL = "gemini-3-pro-preview"
 
19
  HF_TEXT_MODEL = "Qwen/Qwen2.5-72B-Instruct"
20
  HF_IMAGE_MODEL = "black-forest-labs/FLUX.1-dev"
21
 
22
+ HF_TEXT_MODELS = [
23
+ "Qwen/Qwen2.5-72B-Instruct",
24
+ "meta-llama/Llama-3.1-70B-Instruct",
25
+ "mistralai/Mistral-7B-Instruct-v0.3",
26
+ "microsoft/Phi-3-mini-4k-instruct"
27
+ ]
28
+
29
+ HF_IMAGE_MODELS = [
30
+ "black-forest-labs/FLUX.1-dev",
31
+ "Tongyi-MAI/Z-Image-Turbo",
32
+ "Qwen/Qwen-Image-2512"
33
+ ]
34
+
35
  # Gemini Settings
36
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
37
  GEMINI_TEXT_MODEL = "gemini-3-pro-preview"
modules/integrations.py CHANGED
@@ -96,7 +96,9 @@ def refine_with_gemini(prompt, mode="refinement"):
96
  )
97
  return response.text.strip()
98
  except Exception as e:
99
- return f"Error refining prompt with Gemini: {e}"
 
 
100
 
101
  def refine_with_ollama(prompt, model, mode="refinement"):
102
  """Refines the prompt using a local Ollama instance."""
@@ -121,9 +123,10 @@ def refine_with_ollama(prompt, model, mode="refinement"):
121
  text = "\n".join(lines).strip()
122
  return text
123
  except Exception as e:
124
- return f"Error refining prompt with Ollama: {e}"
 
125
 
126
- def refine_with_hf(prompt, token=None, mode="refinement"):
127
  """Refines the prompt using Hugging Face Router (OpenAI compatible)."""
128
  active_client = hf_client
129
 
@@ -141,7 +144,8 @@ def refine_with_hf(prompt, token=None, mode="refinement"):
141
  return "Error: Hugging Face token not found. Please log in or provide a token."
142
 
143
  system_prompt = load_system_prompt(mode)
144
- model_id = HF_TEXT_MODEL
 
145
 
146
  try:
147
  messages = [
@@ -149,17 +153,21 @@ def refine_with_hf(prompt, token=None, mode="refinement"):
149
  {"role": "user", "content": f"Original Prompt: {prompt}"}
150
  ]
151
 
 
 
152
  response = active_client.chat.completions.create(
153
- model=model_id,
154
  messages=messages,
155
  max_tokens=500,
156
- temperature=0.7
 
157
  )
158
  return response.choices[0].message.content.strip()
159
  except Exception as e:
160
- return f"Hugging Face Router Error: {e}"
 
161
 
162
- def refine_master(prompt, backend, ollama_model, manual_token=None, character_name=None):
163
  """Routes prompt refinement to the selected backend."""
164
  if not prompt.strip():
165
  return ""
@@ -168,13 +176,18 @@ def refine_master(prompt, backend, ollama_model, manual_token=None, character_na
168
  hf_token = manual_token.strip() if manual_token and manual_token.strip() else None
169
 
170
  if backend == "Ollama (Local)":
171
- return refine_with_ollama(prompt, ollama_model, mode="refinement")
172
  elif backend == "Hugging Face (Cloud)":
173
- return refine_with_hf(prompt, hf_token, mode="refinement")
174
  else:
175
- return refine_with_gemini(prompt, mode="refinement")
 
 
 
 
 
176
 
177
- def generate_name_master(prompt, backend, ollama_model, manual_token=None):
178
  """Generates a thematic name based on the current prompt context."""
179
  if not prompt.strip():
180
  return "Unnamed Hero"
@@ -182,11 +195,13 @@ def generate_name_master(prompt, backend, ollama_model, manual_token=None):
182
  hf_token = manual_token.strip() if manual_token and manual_token.strip() else None
183
 
184
  if backend == "Ollama (Local)":
185
- return refine_with_ollama(prompt, ollama_model, mode="naming")
186
  elif backend == "Hugging Face (Cloud)":
187
- return refine_with_hf(prompt, hf_token, mode="naming")
188
  else:
189
- return refine_with_gemini(prompt, mode="naming")
 
 
190
 
191
  def generate_image_with_gemini(refined_prompt, technical_prompt, aspect_ratio, character_name="Unnamed Hero"):
192
  if not gemini_active:
@@ -292,13 +307,14 @@ def generate_image_with_comfy(prompt, aspect_ratio, character_name="Unnamed Hero
292
  traceback.print_exc()
293
  return None, None, f"ComfyUI Error: {e}"
294
 
295
- def generate_image_with_hf(prompt, aspect_ratio, token=None, character_name="Unnamed Hero"):
296
  """Generates an image using Hugging Face Inference API."""
297
  active_token = token if token else HF_TOKEN
298
  if not active_token:
299
  return None, None, "Error: Hugging Face token not found. Please log in or provide a token."
300
 
301
- model_id = HF_IMAGE_MODEL
 
302
 
303
  # Resolution mapping
304
  res_map = {
@@ -311,8 +327,8 @@ def generate_image_with_hf(prompt, aspect_ratio, token=None, character_name="Unn
311
  width, height = res_map.get(aspect_ratio, (1024, 1024))
312
 
313
  try:
314
- client = InferenceClient(api_key=active_token)
315
- img = client.text_to_image(prompt, model=model_id, width=width, height=height)
316
 
317
  # Embed metadata
318
  metadata = PngInfo()
@@ -325,12 +341,12 @@ def generate_image_with_hf(prompt, aspect_ratio, token=None, character_name="Unn
325
  temp_dir = tempfile.mkdtemp()
326
  img_path = os.path.join(temp_dir, filename)
327
  img.save(img_path, "PNG", pnginfo=metadata)
328
- return img, img_path, f"Image generated via Hugging Face ({model_id})!"
329
  except Exception as e:
330
  traceback.print_exc()
331
  return None, None, f"Hugging Face Image Error: {e}"
332
 
333
- def generate_image_master(refined_prompt, technical_prompt, aspect_ratio, backend, manual_token=None, character_name="Unnamed Hero"):
334
  """Routes image generation to the selected backend."""
335
  final_prompt = refined_prompt.strip() if refined_prompt.strip() else technical_prompt
336
 
@@ -340,6 +356,6 @@ def generate_image_master(refined_prompt, technical_prompt, aspect_ratio, backen
340
  if backend == "ComfyUI (Local)":
341
  return generate_image_with_comfy(final_prompt, aspect_ratio, character_name)
342
  elif backend == "Hugging Face (Cloud)":
343
- return generate_image_with_hf(final_prompt, aspect_ratio, hf_token, character_name)
344
  else:
345
  return generate_image_with_gemini(refined_prompt, technical_prompt, aspect_ratio, character_name)
 
96
  )
97
  return response.text.strip()
98
  except Exception as e:
99
+ print(f"Gemini Refinement Error: {e}")
100
+ traceback.print_exc()
101
+ return None
102
 
103
  def refine_with_ollama(prompt, model, mode="refinement"):
104
  """Refines the prompt using a local Ollama instance."""
 
123
  text = "\n".join(lines).strip()
124
  return text
125
  except Exception as e:
126
+ print(f"Ollama Refinement Error: {e}")
127
+ return None
128
 
129
+ def refine_with_hf(prompt, model_id=None, provider=None, token=None, mode="refinement"):
130
  """Refines the prompt using Hugging Face Router (OpenAI compatible)."""
131
  active_client = hf_client
132
 
 
144
  return "Error: Hugging Face token not found. Please log in or provide a token."
145
 
146
  system_prompt = load_system_prompt(mode)
147
+ active_model = model_id if model_id else HF_TEXT_MODEL
148
+ active_provider = provider if provider and provider.strip() else "auto"
149
 
150
  try:
151
  messages = [
 
153
  {"role": "user", "content": f"Original Prompt: {prompt}"}
154
  ]
155
 
156
+ # Note: Provider for Chat Completions is currently handled by the route or specific model naming conventions.
157
+ # But we pass it if the client supports it or for future use.
158
  response = active_client.chat.completions.create(
159
+ model=active_model,
160
  messages=messages,
161
  max_tokens=500,
162
+ temperature=0.7,
163
+ extra_body={"provider": active_provider}
164
  )
165
  return response.choices[0].message.content.strip()
166
  except Exception as e:
167
+ print(f"HF Refinement Error: {e}")
168
+ return None
169
 
170
+ def refine_master(prompt, backend, ollama_model, hf_text_model, hf_text_provider, manual_token=None, character_name=None):
171
  """Routes prompt refinement to the selected backend."""
172
  if not prompt.strip():
173
  return ""
 
176
  hf_token = manual_token.strip() if manual_token and manual_token.strip() else None
177
 
178
  if backend == "Ollama (Local)":
179
+ result = refine_with_ollama(prompt, ollama_model, mode="refinement")
180
  elif backend == "Hugging Face (Cloud)":
181
+ result = refine_with_hf(prompt, hf_text_model, hf_text_provider, hf_token, mode="refinement")
182
  else:
183
+ result = refine_with_gemini(prompt, mode="refinement")
184
+
185
+ if result is None:
186
+ return gr.update(), f"⚠️ Refinement failed. Check console for details. Original prompt preserved."
187
+
188
+ return result, ""
189
 
190
+ def generate_name_master(prompt, backend, ollama_model, hf_text_model, hf_text_provider, manual_token=None):
191
  """Generates a thematic name based on the current prompt context."""
192
  if not prompt.strip():
193
  return "Unnamed Hero"
 
195
  hf_token = manual_token.strip() if manual_token and manual_token.strip() else None
196
 
197
  if backend == "Ollama (Local)":
198
+ result = refine_with_ollama(prompt, ollama_model, mode="naming")
199
  elif backend == "Hugging Face (Cloud)":
200
+ result = refine_with_hf(prompt, hf_text_model, hf_text_provider, hf_token, mode="naming")
201
  else:
202
+ result = refine_with_gemini(prompt, mode="naming")
203
+
204
+ return result if result else "Unnamed Hero"
205
 
206
  def generate_image_with_gemini(refined_prompt, technical_prompt, aspect_ratio, character_name="Unnamed Hero"):
207
  if not gemini_active:
 
307
  traceback.print_exc()
308
  return None, None, f"ComfyUI Error: {e}"
309
 
310
+ def generate_image_with_hf(prompt, aspect_ratio, model_id=None, provider=None, token=None, character_name="Unnamed Hero"):
311
  """Generates an image using Hugging Face Inference API."""
312
  active_token = token if token else HF_TOKEN
313
  if not active_token:
314
  return None, None, "Error: Hugging Face token not found. Please log in or provide a token."
315
 
316
+ active_model = model_id if model_id else HF_IMAGE_MODEL
317
+ active_provider = provider if provider and provider.strip() else "auto"
318
 
319
  # Resolution mapping
320
  res_map = {
 
327
  width, height = res_map.get(aspect_ratio, (1024, 1024))
328
 
329
  try:
330
+ client = InferenceClient(api_key=active_token, provider=active_provider)
331
+ img = client.text_to_image(prompt, model=active_model, width=width, height=height)
332
 
333
  # Embed metadata
334
  metadata = PngInfo()
 
341
  temp_dir = tempfile.mkdtemp()
342
  img_path = os.path.join(temp_dir, filename)
343
  img.save(img_path, "PNG", pnginfo=metadata)
344
+ return img, img_path, f"Image generated via Hugging Face ({active_model})!"
345
  except Exception as e:
346
  traceback.print_exc()
347
  return None, None, f"Hugging Face Image Error: {e}"
348
 
349
+ def generate_image_master(refined_prompt, technical_prompt, aspect_ratio, backend, hf_image_model, hf_image_provider, manual_token=None, character_name="Unnamed Hero"):
350
  """Routes image generation to the selected backend."""
351
  final_prompt = refined_prompt.strip() if refined_prompt.strip() else technical_prompt
352
 
 
356
  if backend == "ComfyUI (Local)":
357
  return generate_image_with_comfy(final_prompt, aspect_ratio, character_name)
358
  elif backend == "Hugging Face (Cloud)":
359
+ return generate_image_with_hf(final_prompt, aspect_ratio, hf_image_model, hf_image_provider, hf_token, character_name)
360
  else:
361
  return generate_image_with_gemini(refined_prompt, technical_prompt, aspect_ratio, character_name)
modules/ui_layout.py CHANGED
@@ -1,5 +1,5 @@
1
  import gradio as gr
2
- from .config import FEATURE_SEQUENCE, SECTIONS
3
  from .core_logic import (
4
  features_data, generate_prompt, handle_regeneration,
5
  save_character, load_character, get_example_list, load_example_character
@@ -137,38 +137,65 @@ def build_ui():
137
  ollama_active = len(ollama_models) > 0
138
  comfy_active = check_comfy_availability()
139
 
140
- refinement_choices = ["Gemini (Cloud)", "Hugging Face (Cloud)"]
 
 
141
  if ollama_active:
142
  refinement_choices.append("Ollama (Local)")
143
 
144
  refinement_backend = gr.Radio(
145
  choices=refinement_choices,
146
- value="Gemini (Cloud)",
147
  label="Prompt Refinement Backend",
148
  scale=2
149
  )
150
 
151
- ollama_model_dropdown = gr.Dropdown(
152
- choices=ollama_models,
153
- value=ollama_models[0] if ollama_active else None,
154
- label="Ollama Model",
155
- visible=False,
156
- scale=1
157
- )
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
  with gr.Row():
160
- img_choices = ["Gemini (Cloud)", "Hugging Face (Cloud)"]
 
 
161
  if comfy_active:
162
  img_choices.append("ComfyUI (Local)")
163
 
164
  backend_selector = gr.Radio(
165
  choices=img_choices,
166
- value="Gemini (Cloud)",
167
  label="Image Generation Backend",
168
  scale=2
169
  )
170
  with gr.Column(scale=1):
171
- gen_img_btn = gr.Button("🖼️ Generate Image", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  with gr.Row():
174
  with gr.Column(scale=1):
@@ -190,6 +217,7 @@ def build_ui():
190
 
191
  gr.Markdown("---")
192
  image_output = gr.Image(label="Portrait", show_label=False)
 
193
  download_img_btn = gr.DownloadButton("📥 Download Portrait (PNG)", variant="secondary", visible=False)
194
  status_msg = gr.Markdown("")
195
  download_file = gr.File(label="Saved Character JSON", visible=False)
@@ -220,13 +248,13 @@ def build_ui():
220
 
221
  refine_btn.click(
222
  fn=refine_master,
223
- inputs=[prompt_output, refinement_backend, ollama_model_dropdown, hf_token_input, character_name],
224
- outputs=refined_output
225
  )
226
 
227
  gen_img_btn.click(
228
  fn=generate_image_master,
229
- inputs=[refined_output, prompt_output, dropdowns[-1], backend_selector, hf_token_input, character_name],
230
  outputs=[image_output, download_img_btn, status_msg]
231
  ).then(
232
  fn=lambda x: gr.update(value=x, visible=True) if x else gr.update(visible=False),
@@ -235,9 +263,22 @@ def build_ui():
235
  )
236
 
237
  refinement_backend.change(
238
- fn=lambda b: gr.update(visible=(b == "Ollama (Local)")),
 
 
 
 
239
  inputs=refinement_backend,
240
- outputs=ollama_model_dropdown
 
 
 
 
 
 
 
 
 
241
  )
242
 
243
  save_btn.click(
 
1
  import gradio as gr
2
+ from .config import FEATURE_SEQUENCE, SECTIONS, HF_TEXT_MODELS, HF_IMAGE_MODELS, GEMINI_API_KEY
3
  from .core_logic import (
4
  features_data, generate_prompt, handle_regeneration,
5
  save_character, load_character, get_example_list, load_example_character
 
137
  ollama_active = len(ollama_models) > 0
138
  comfy_active = check_comfy_availability()
139
 
140
+ refinement_choices = ["Hugging Face (Cloud)"]
141
+ if GEMINI_API_KEY:
142
+ refinement_choices.insert(0, "Gemini (Cloud)")
143
  if ollama_active:
144
  refinement_choices.append("Ollama (Local)")
145
 
146
  refinement_backend = gr.Radio(
147
  choices=refinement_choices,
148
+ value=refinement_choices[0],
149
  label="Prompt Refinement Backend",
150
  scale=2
151
  )
152
 
153
+ with gr.Column(scale=1):
154
+ ollama_model_dropdown = gr.Dropdown(
155
+ choices=ollama_models,
156
+ value=ollama_models[0] if ollama_active else None,
157
+ label="Olama Model",
158
+ visible=False
159
+ )
160
+ hf_text_model_dropdown = gr.Dropdown(
161
+ choices=HF_TEXT_MODELS,
162
+ value=HF_TEXT_MODELS[0],
163
+ label="HF Text Model",
164
+ visible=("Hugging Face (Cloud)" in refinement_choices and refinement_choices[0] == "Hugging Face (Cloud)")
165
+ )
166
+ hf_text_provider_input = gr.Textbox(
167
+ value="auto",
168
+ placeholder="Optional: e.g. fal-ai",
169
+ label="HF Text Provider",
170
+ visible=("Hugging Face (Cloud)" in refinement_choices and refinement_choices[0] == "Hugging Face (Cloud)")
171
+ )
172
 
173
  with gr.Row():
174
+ img_choices = ["Hugging Face (Cloud)"]
175
+ if GEMINI_API_KEY:
176
+ img_choices.insert(0, "Gemini (Cloud)")
177
  if comfy_active:
178
  img_choices.append("ComfyUI (Local)")
179
 
180
  backend_selector = gr.Radio(
181
  choices=img_choices,
182
+ value=img_choices[0],
183
  label="Image Generation Backend",
184
  scale=2
185
  )
186
  with gr.Column(scale=1):
187
+ hf_image_model_dropdown = gr.Dropdown(
188
+ choices=HF_IMAGE_MODELS,
189
+ value=HF_IMAGE_MODELS[0],
190
+ label="HF Image Model",
191
+ visible=("Hugging Face (Cloud)" in img_choices and img_choices[0] == "Hugging Face (Cloud)")
192
+ )
193
+ hf_image_provider_input = gr.Textbox(
194
+ value="auto",
195
+ placeholder="Optional: e.g. fal-ai",
196
+ label="HF Image Provider",
197
+ visible=("Hugging Face (Cloud)" in img_choices and img_choices[0] == "Hugging Face (Cloud)")
198
+ )
199
 
200
  with gr.Row():
201
  with gr.Column(scale=1):
 
217
 
218
  gr.Markdown("---")
219
  image_output = gr.Image(label="Portrait", show_label=False)
220
+ gen_img_btn = gr.Button("🖼️ Generate Image", variant="primary", scale=1)
221
  download_img_btn = gr.DownloadButton("📥 Download Portrait (PNG)", variant="secondary", visible=False)
222
  status_msg = gr.Markdown("")
223
  download_file = gr.File(label="Saved Character JSON", visible=False)
 
248
 
249
  refine_btn.click(
250
  fn=refine_master,
251
+ inputs=[prompt_output, refinement_backend, ollama_model_dropdown, hf_text_model_dropdown, hf_text_provider_input, hf_token_input, character_name],
252
+ outputs=[refined_output, status_msg]
253
  )
254
 
255
  gen_img_btn.click(
256
  fn=generate_image_master,
257
+ inputs=[refined_output, prompt_output, dropdowns[-1], backend_selector, hf_image_model_dropdown, hf_image_provider_input, hf_token_input, character_name],
258
  outputs=[image_output, download_img_btn, status_msg]
259
  ).then(
260
  fn=lambda x: gr.update(value=x, visible=True) if x else gr.update(visible=False),
 
263
  )
264
 
265
  refinement_backend.change(
266
+ fn=lambda b: (
267
+ gr.update(visible=(b == "Ollama (Local)")),
268
+ gr.update(visible=(b == "Hugging Face (Cloud)")),
269
+ gr.update(visible=(b == "Hugging Face (Cloud)"))
270
+ ),
271
  inputs=refinement_backend,
272
+ outputs=[ollama_model_dropdown, hf_text_model_dropdown, hf_text_provider_input]
273
+ )
274
+
275
+ backend_selector.change(
276
+ fn=lambda b: (
277
+ gr.update(visible=(b == "Hugging Face (Cloud)")),
278
+ gr.update(visible=(b == "Hugging Face (Cloud)"))
279
+ ),
280
+ inputs=backend_selector,
281
+ outputs=[hf_image_model_dropdown, hf_image_provider_input]
282
  )
283
 
284
  save_btn.click(