mgbam commited on
Commit
ded730b
·
verified ·
1 Parent(s): 0e3fb27

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -307
app.py CHANGED
@@ -1,49 +1,21 @@
 
1
  import gradio as gr
2
- from huggingface_hub import InferenceClient # Still needed for HF fallbacks
3
- import google.generativeai as genai # For Google Gemini API
4
  import os
5
- import random
6
-
7
- # --- ALGOFORGE PRIME™ CONFIGURATION & SECRETS ---
8
-
9
- # Google API Key - ESSENTIAL for Google Gemini Pro/Flash models via their API
10
- GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY")
11
- GEMINI_API_CONFIGURED = False
12
- if GOOGLE_API_KEY:
13
- try:
14
- genai.configure(api_key=GOOGLE_API_KEY)
15
- GEMINI_API_CONFIGURED = True
16
- print("INFO: Google Gemini API configured successfully.")
17
- except Exception as e:
18
- print(f"ERROR: Failed to configure Google Gemini API with provided key: {e}. Gemini models will be unavailable.")
19
- # GOOGLE_API_KEY = None # Effectively disables it if config fails
20
- else:
21
- print("WARNING: GOOGLE_API_KEY not found in Space Secrets. Google Gemini API models will be disabled.")
22
 
23
- # Hugging Face Token - For Hugging Face hosted models (fallbacks or alternatives)
24
- HF_TOKEN = os.getenv("HF_TOKEN")
25
- HF_API_CONFIGURED = False
26
- if not HF_TOKEN:
27
- print("WARNING: HF_TOKEN not found in Space Secrets. Calls to Hugging Face hosted models will be disabled.")
28
- else:
29
- HF_API_CONFIGURED = True
30
- print("INFO: HF_TOKEN detected. Hugging Face hosted models can be used.")
31
 
32
- # Initialize Hugging Face Inference Client (conditionally)
33
- hf_inference_client = None
34
- if HF_API_CONFIGURED:
35
- try:
36
- hf_inference_client = InferenceClient(token=HF_TOKEN)
37
- print("INFO: Hugging Face InferenceClient initialized successfully.")
38
- except Exception as e:
39
- print(f"ERROR: Failed to initialize Hugging Face InferenceClient: {e}. HF models will be unavailable.")
40
- HF_API_CONFIGURED = False # Mark as not configured if client init fails
41
-
42
- # --- MODEL DEFINITIONS ---
43
  AVAILABLE_MODELS = {}
44
  DEFAULT_MODEL_KEY = None
45
 
46
- # Populate with Gemini models if API is configured
47
  if GEMINI_API_CONFIGURED:
48
  AVAILABLE_MODELS.update({
49
  "Google Gemini 1.5 Flash (API - Fast, Recommended)": {"id": "gemini-1.5-flash-latest", "type": "google_gemini"},
@@ -51,364 +23,282 @@ if GEMINI_API_CONFIGURED:
51
  })
52
  DEFAULT_MODEL_KEY = "Google Gemini 1.5 Flash (API - Fast, Recommended)"
53
 
54
- # Populate with Hugging Face models if API is configured (as alternatives/fallbacks)
55
  if HF_API_CONFIGURED:
56
  AVAILABLE_MODELS.update({
57
  "Google Gemma 2B (HF - Quick Test)": {"id": "google/gemma-2b-it", "type": "hf"},
58
  "Mistral 7B Instruct (HF)": {"id": "mistralai/Mistral-7B-Instruct-v0.2", "type": "hf"},
59
- "CodeLlama 7B Instruct (HF)": {"id": "codellama/CodeLlama-7b-Instruct-hf", "type": "hf"},
60
  })
61
- if not DEFAULT_MODEL_KEY: # If Gemini isn't configured, default to an HF model
62
  DEFAULT_MODEL_KEY = "Google Gemma 2B (HF - Quick Test)"
63
 
64
- # Absolute fallback if no models could be configured
65
  if not AVAILABLE_MODELS:
66
- print("CRITICAL ERROR: No models could be configured. Neither Google API Key nor HF Token seem to be working or present.")
67
- # Add a dummy entry to prevent crashes, though the app will be non-functional
68
  AVAILABLE_MODELS["No Models Available"] = {"id": "dummy", "type": "none"}
69
  DEFAULT_MODEL_KEY = "No Models Available"
70
- elif not DEFAULT_MODEL_KEY: # If somehow DEFAULT_MODEL_KEY is still None but AVAILABLE_MODELS is not empty
71
  DEFAULT_MODEL_KEY = list(AVAILABLE_MODELS.keys())[0]
72
 
73
 
74
- # --- CORE AI ENGINEERING: LLM INTERACTION FUNCTIONS ---
75
-
76
- def call_huggingface_llm_api(prompt_text, model_id, temperature=0.7, max_new_tokens=350, system_prompt=None):
77
- if not HF_API_CONFIGURED or not hf_inference_client:
78
- return "ERROR: Hugging Face API is not configured (HF_TOKEN missing or client init failed)."
79
-
80
- if system_prompt:
81
- full_prompt = f"<s>[INST] <<SYS>>\n{system_prompt}\n<</SYS>>\n\n{prompt_text} [/INST]"
82
- else:
83
- full_prompt = prompt_text
84
- try:
85
- use_sample = temperature > 0.0
86
- response_text = hf_inference_client.text_generation(
87
- full_prompt, model=model_id, max_new_tokens=max_new_tokens,
88
- temperature=temperature if use_sample else None,
89
- do_sample=use_sample, stream=False
90
- )
91
- return response_text
92
- except Exception as e:
93
- error_details = f"Error Type: {type(e).__name__}, Message: {str(e)}"
94
- print(f"Hugging Face LLM API Call Error ({model_id}): {error_details}")
95
- return f"LLM API Error (Hugging Face Model: {model_id}). Details: {error_details}. Check Space logs."
96
-
97
- def call_google_gemini_api(prompt_text, model_id, temperature=0.7, max_new_tokens=400, system_prompt=None):
98
- if not GEMINI_API_CONFIGURED:
99
- return "ERROR: Google Gemini API is not configured (GOOGLE_API_KEY missing or config failed)."
100
-
101
- try:
102
- # For gemini-1.5-flash and newer, system_instruction is the preferred way.
103
- # For older gemini-1.0-pro, you might need to structure the 'contents' array.
104
- model_instance = genai.GenerativeModel(model_name=model_id, system_instruction=system_prompt if system_prompt else None)
105
-
106
- generation_config = genai.types.GenerationConfig(
107
- temperature=temperature,
108
- max_output_tokens=max_new_tokens
109
- )
110
- # Simple user prompt if system_instruction is handled by GenerativeModel
111
- response = model_instance.generate_content(
112
- prompt_text, # Just the user prompt
113
- generation_config=generation_config,
114
- stream=False
115
- )
116
-
117
- # Robust check for response content and safety blocks
118
- if response.prompt_feedback and response.prompt_feedback.block_reason:
119
- block_reason_msg = response.prompt_feedback.block_reason_message or response.prompt_feedback.block_reason
120
- print(f"Google Gemini API: Prompt blocked. Reason: {block_reason_msg}")
121
- return f"Google Gemini API Error: Your prompt was blocked. Reason: {block_reason_msg}. Try rephrasing."
122
-
123
- if not response.candidates or not response.candidates[0].content.parts:
124
- # Check if any candidate has content
125
- candidate_had_content = any(cand.content and cand.content.parts for cand in response.candidates)
126
- if not candidate_had_content:
127
- finish_reason = response.candidates[0].finish_reason if response.candidates else "Unknown"
128
- # Specific check for safety if that's the finish reason
129
- if str(finish_reason).upper() == "SAFETY":
130
- print(f"Google Gemini API: Response generation stopped due to safety settings. Finish Reason: {finish_reason}")
131
- return f"Google Gemini API Error: Response generation stopped due to safety settings. Finish Reason: {finish_reason}. Try a different prompt or adjust safety settings in your Google AI Studio if possible."
132
- else:
133
- print(f"Google Gemini API: Empty response or no content parts. Finish Reason: {finish_reason}")
134
- return f"Google Gemini API Error: Empty response or no content generated. Finish Reason: {finish_reason}. The model might not have had anything to say or the request was malformed."
135
-
136
- # Assuming the first candidate has the primary response
137
- return response.candidates[0].content.parts[0].text
138
-
139
- except Exception as e:
140
- error_details = f"Error Type: {type(e).__name__}, Message: {str(e)}"
141
- print(f"Google Gemini API Call Error ({model_id}): {error_details}")
142
- # Provide more specific feedback for common errors if possible
143
- if "API key not valid" in str(e) or "PERMISSION_DENIED" in str(e):
144
- return f"LLM API Error (Google Gemini Model: {model_id}). Details: API key invalid or permission denied. Please check your GOOGLE_API_KEY and ensure the Gemini API is enabled for your project. Original error: {error_details}"
145
- elif "Could not find model" in str(e):
146
- return f"LLM API Error (Google Gemini Model: {model_id}). Details: Model ID '{model_id}' not found or not accessible with your key. Original error: {error_details}"
147
- return f"LLM API Error (Google Gemini Model: {model_id}). Details: {error_details}. Check Space logs."
148
-
149
-
150
- # --- ALGOFORGE PRIME™ - THE GRAND ORCHESTRATOR ---
151
- # (This function remains largely the same as the previous "full rewrite",
152
- # as the dispatch_llm_call logic handles routing to the correct API call function.
153
- # I will include it for completeness but highlight any minor adjustments if needed.)
154
-
155
  def run_algoforge_simulation(
156
- problem_type, problem_description, initial_hints,
157
  num_initial_solutions, selected_model_key,
158
  gen_temp, gen_max_tokens,
159
  eval_temp, eval_max_tokens,
160
- evolve_temp, evolve_max_tokens
 
161
  ):
 
 
 
162
  if not problem_description:
163
- return "ERROR: Problem Description is the lifeblood of innovation! Please provide it.", "", "", ""
164
 
165
- model_info = AVAILABLE_MODELS.get(selected_model_key)
166
- if not model_info or model_info["type"] == "none":
167
- return f"ERROR: No valid model selected or available. Please check API key configurations. Selected: '{selected_model_key}'", "", "", ""
168
 
169
- model_id = model_info["id"]
170
- model_type = model_info["type"]
 
 
 
 
171
 
172
- log_entries = [f"**AlgoForge Prime™ Initializing...**\nSelected Model Core: {model_id} ({selected_model_key} - Type: {model_type})\nProblem Type: {problem_type}"]
173
-
174
- def dispatch_llm_call(prompt, system_p, temp, max_tok, stage_name=""):
175
- log_entries.append(f" Dispatching to {model_type.upper()} API for {stage_name} (Model: {model_id}):\n Prompt (snippet): {prompt[:100]}...")
176
- if system_p: log_entries[-1] += f"\n System Prompt (snippet): {system_p[:100]}..."
177
-
178
- if model_type == "hf":
179
- if not HF_API_CONFIGURED: return "ERROR: HF_TOKEN not configured or InferenceClient failed."
180
- result = call_huggingface_llm_api(prompt, model_id, temp, max_tok, system_p)
181
- elif model_type == "google_gemini":
182
- if not GEMINI_API_CONFIGURED: return "ERROR: GOOGLE_API_KEY not configured or Gemini API setup failed."
183
- result = call_google_gemini_api(prompt, model_id, temp, max_tok, system_p)
184
- else:
185
- result = f"ERROR: Unknown model type '{model_type}' for selected model."
186
-
187
- log_entries.append(f" {model_type.upper()} API Response ({stage_name} - Snippet): {str(result)[:150]}...")
188
- return result
189
-
190
- # STAGE 1: GENESIS
191
- log_entries.append("\n**Stage 1: Genesis Engine - Generating Initial Solution Candidates...**")
192
- generated_solutions_raw = []
193
- system_prompt_generate = f"You are an expert {problem_type.lower().replace(' ', '_')} algorithm designer. Your goal is to brainstorm multiple diverse solutions to the user's problem."
194
- for i in range(num_initial_solutions):
195
- user_prompt_generate = (
196
- f"Problem Description: \"{problem_description}\"\n"
197
- f"Consider these initial thoughts/constraints: \"{initial_hints if initial_hints else 'None'}\"\n"
198
- f"Please provide one distinct and complete solution/algorithm for this problem. "
199
- f"This is solution attempt #{i+1} of {num_initial_solutions}. Try a different approach if possible."
200
- )
201
- solution_text = dispatch_llm_call(user_prompt_generate, system_prompt_generate, gen_temp, gen_max_tokens, f"Genesis Attempt {i+1}")
202
- generated_solutions_raw.append(solution_text)
203
-
204
- if not any(sol and not str(sol).startswith("ERROR:") and not str(sol).startswith("LLM API Error") for sol in generated_solutions_raw):
205
- log_entries.append(" Genesis Engine failed to produce viable candidates or all calls resulted in errors.")
206
- initial_sol_output = "No valid solutions generated by the Genesis Engine. All attempts failed or returned errors."
207
- if generated_solutions_raw:
208
- initial_sol_output += "\n\nErrors Encountered:\n" + "\n".join([f"- {str(s)}" for s in generated_solutions_raw if str(s).startswith("ERROR") or str(s).startswith("LLM API Error")])
209
- return initial_sol_output, "", "", "\n".join(log_entries)
210
 
211
- # STAGE 2: CRITIQUE
212
- log_entries.append("\n**Stage 2: Critique Crucible - Evaluating Candidates...**")
213
- evaluated_solutions_display = []
214
- evaluated_sols_data = []
215
- system_prompt_evaluate = "You are a highly critical and insightful AI algorithm evaluator. Assess the provided solution based on clarity, potential correctness, and perceived efficiency. Provide a concise critique and a numerical score from 1 (poor) to 10 (excellent). CRITICALLY: You MUST include the score in the format 'Score: X/10' where X is an integer."
216
-
217
- for i, sol_text_candidate in enumerate(generated_solutions_raw):
218
- sol_text = str(sol_text_candidate)
219
- critique_text = f"Critique for Candidate {i+1}" # Placeholder
220
- score = 0
221
-
222
- if sol_text.startswith("ERROR:") or sol_text.startswith("LLM API Error"):
223
- critique_text = f"Candidate {i+1} could not be properly generated due to an earlier API error: {sol_text}"
224
- score = 0
225
  else:
226
- user_prompt_evaluate = (
227
- f"Problem Reference (for context only, do not repeat in output): \"{problem_description[:150]}...\"\n\n"
228
- f"Now, evaluate the following proposed solution:\n```\n{sol_text}\n```\n"
229
- f"Provide your critique and ensure you output a score in the format 'Score: X/10'."
230
  )
231
- evaluation_text = str(dispatch_llm_call(user_prompt_evaluate, system_prompt_evaluate, eval_temp, eval_max_tokens, f"Critique Candidate {i+1}"))
232
-
233
- critique_text = evaluation_text # Default to full response
234
- if evaluation_text.startswith("ERROR:") or evaluation_text.startswith("LLM API Error"):
235
- critique_text = f"Error during evaluation of Candidate {i+1}: {evaluation_text}"
236
- score = 0
237
- else:
238
- # Try to parse score
239
- score_match_found = False
240
- if "Score:" in evaluation_text:
241
- try:
242
- # More robust parsing for "Score: X/10" or "Score: X"
243
- score_part_full = evaluation_text.split("Score:")[1].strip()
244
- score_num_str = score_part_full.split("/")[0].split()[0].strip() # Get number before / or space
245
- parsed_score_val = int(score_num_str)
246
- score = max(1, min(parsed_score_val, 10)) # Clamp score
247
- score_match_found = True
248
- except (ValueError, IndexError, TypeError):
249
- log_entries.append(f" Warning: Could not parse score accurately from: '{evaluation_text}' despite 'Score:' marker.")
250
-
251
- if not score_match_found: # Fallback if parsing fails or marker missing
252
- log_entries.append(f" Warning: 'Score:' marker missing or unparsable in evaluation: '{evaluation_text}'. Assigning random score.")
253
- score = random.randint(3, 7)
254
 
255
- evaluated_solutions_display.append(f"**Candidate {i+1}:**\n```text\n{sol_text}\n```\n**Crucible Verdict (Score: {score}/10):**\n{critique_text}\n---")
256
- evaluated_sols_data.append({"id": i+1, "solution": sol_text, "score": score, "critique": critique_text})
257
-
258
- if not evaluated_sols_data or all(s['score'] == 0 for s in evaluated_sols_data):
259
- log_entries.append(" Critique Crucible yielded no valid evaluations or all solutions had errors.")
260
- current_output = "\n\n".join(evaluated_solutions_display) if evaluated_solutions_display else "Generation might be OK, but evaluation failed for all candidates."
261
- return current_output, "", "", "\n".join(log_entries)
262
-
263
- # STAGE 3: SELECTION
264
- evaluated_sols_data.sort(key=lambda x: x["score"], reverse=True)
265
- best_initial_solution_data = evaluated_sols_data[0]
266
- log_entries.append(f"\n**Stage 3: Champion Selected - Candidate {best_initial_solution_data['id']} (Score: {best_initial_solution_data['score']}) chosen for evolution.**")
267
- if best_initial_solution_data['solution'].startswith("ERROR:") or best_initial_solution_data['solution'].startswith("LLM API Error"):
268
- log_entries.append(" ERROR: Selected champion solution itself is an error message. Cannot evolve.")
269
- return "\n\n".join(evaluated_solutions_display), f"Selected champion was an error: {best_initial_solution_data['solution']}", "Cannot evolve an error.", "\n".join(log_entries)
270
-
271
- # STAGE 4: EVOLUTION
272
- log_entries.append("\n**Stage 4: Evolutionary Forge - Refining the Champion...**")
273
- system_prompt_evolve = f"You are an elite AI algorithm optimizer and refiner. Your task is to take the provided solution and make it significantly better. Focus on {problem_type.lower()} best practices, improve efficiency or clarity, fix any potential errors, and expand on it if appropriate. Explain the key improvements you've made clearly."
274
- user_prompt_evolve = (
275
- f"Original Problem (for context): \"{problem_description}\"\n\n"
276
- f"The current leading solution (which had a score of {best_initial_solution_data['score']}/10) is:\n```\n{best_initial_solution_data['solution']}\n```\n"
277
- f"The original critique for this solution was: \"{best_initial_solution_data['critique']}\"\n\n"
278
- f"Your mission: Evolve this solution. Make it demonstrably superior. If the original solution was just a sketch, flesh it out. If it had flaws, fix them. If it was good, make it great. Explain the key improvements you've made as part of your response."
279
- )
280
- evolved_solution_text = str(dispatch_llm_call(user_prompt_evolve, system_prompt_evolve, evolve_temp, evolve_max_tokens, "Evolution"))
281
 
282
- if evolved_solution_text.startswith("ERROR:") or evolved_solution_text.startswith("LLM API Error"):
283
- log_entries.append(" ERROR: Evolution step resulted in an API error.")
284
- evolved_solution_output_md = f"**Evolution Failed:**\n{evolved_solution_text}"
285
- else:
286
- evolved_solution_output_md = f"**✨ AlgoForge Prime™ Evolved Artifact ✨:**\n```text\n{evolved_solution_text}\n```"
287
-
288
- # FINAL OUTPUT ASSEMBLY
289
- initial_solutions_output_md = "\n\n".join(evaluated_solutions_display)
290
- best_solution_output_md = (
291
- f"**Champion Candidate {best_initial_solution_data['id']} (Original Score: {best_initial_solution_data['score']}/10):**\n"
292
- f"```text\n{best_initial_solution_data['solution']}\n```\n"
293
- f"**Original Crucible Verdict:**\n{best_initial_solution_data['critique']}"
 
 
 
294
  )
 
 
 
 
 
295
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  log_entries.append("\n**AlgoForge Prime™ Cycle Complete.**")
297
- final_log_output = "\n".join(log_entries)
 
298
 
299
- return initial_solutions_output_md, best_solution_output_md, evolved_solution_output_md, final_log_output
300
 
301
- # --- GRADIO UI ---
302
  intro_markdown = """
303
- # ✨ AlgoForge Prime™ ✨: Conceptual Algorithmic Evolution (Gemini Focused)
304
- Welcome! This system demonstrates AI-assisted algorithm discovery and refinement, with a primary focus on **Google Gemini API models**.
305
- Hugging Face hosted models are available as alternatives if configured.
306
- **This is a conceptual demo, not AlphaEvolve itself.**
307
 
308
  **API Keys Required in Space Secrets:**
309
- - `GOOGLE_API_KEY` (Primary): For Google Gemini API models (e.g., Gemini 1.5 Flash, Gemini 1.0 Pro).
310
- - `HF_TOKEN` (Secondary): For Hugging Face hosted models (e.g., Gemma on HF, Mistral).
311
- If a key is missing, corresponding models will be unusable or limited.
312
  """
313
-
314
  token_status_md = ""
315
  if not GEMINI_API_CONFIGURED and not HF_API_CONFIGURED:
316
- token_status_md = "<p style='color:red;'>⚠️ CRITICAL: NEITHER GOOGLE_API_KEY NOR HF_TOKEN are configured or working. The application will not function.</p>"
317
  else:
318
- if GEMINI_API_CONFIGURED:
319
- token_status_md += "<p style='color:green;'>✅ Google Gemini API Key detected and configured.</p>"
320
- else:
321
- token_status_md += "<p style='color:orange;'>⚠️ GOOGLE_API_KEY missing or failed to configure. Gemini API models disabled.</p>"
322
-
323
- if HF_API_CONFIGURED:
324
- token_status_md += "<p style='color:green;'>✅ Hugging Face API Token detected and client initialized.</p>"
325
- else:
326
- token_status_md += "<p style='color:orange;'>⚠️ HF_TOKEN missing or client failed to initialize. Hugging Face models disabled.</p>"
327
 
328
 
329
- with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"), title="AlgoForge Prime™ (Gemini)") as demo: # Changed theme
330
  gr.Markdown(intro_markdown)
331
- gr.HTML(token_status_md)
332
 
333
  if not AVAILABLE_MODELS or DEFAULT_MODEL_KEY == "No Models Available":
334
- gr.Markdown("<h2 style='color:red;'>No models are available. Please check your API key configurations in Space Secrets and restart the Space.</h2>")
335
  else:
336
  with gr.Row():
337
- with gr.Column(scale=1):
338
  gr.Markdown("## 💡 1. Define the Challenge")
339
  problem_type_dd = gr.Dropdown(
340
- ["Python Algorithm", "Data Structure Logic", "Mathematical Optimization", "Conceptual System Design", "Pseudocode Refinement", "Verilog Snippet Idea", "General Brainstorming"],
341
- label="Type of Problem/Algorithm", value="Python Algorithm"
342
  )
343
  problem_desc_tb = gr.Textbox(
344
- lines=5, label="Problem Description / Desired Outcome",
345
- placeholder="e.g., 'Efficient Python function for Fibonacci sequence using memoization.'"
346
  )
347
  initial_hints_tb = gr.Textbox(
348
- lines=3, label="Initial Thoughts / Constraints / Seed Ideas (Optional)",
349
- placeholder="e.g., 'Focus on clarity and correctness.' OR 'Target O(n) complexity.'"
 
 
 
 
 
 
350
  )
351
 
352
  gr.Markdown("## ⚙️ 2. Configure The Forge")
353
  model_select_dd = gr.Dropdown(
354
  choices=list(AVAILABLE_MODELS.keys()),
355
- value=DEFAULT_MODEL_KEY if DEFAULT_MODEL_KEY in AVAILABLE_MODELS else (list(AVAILABLE_MODELS.keys())[0] if AVAILABLE_MODELS else None), # Ensure default is valid
356
  label="Select LLM Core Model"
357
  )
358
- num_solutions_slider = gr.Slider(1, 4, value=2, step=1, label="Number of Initial Solutions (Genesis Engine)")
359
 
360
  with gr.Accordion("Advanced LLM Parameters", open=False):
 
361
  with gr.Row():
362
- gen_temp_slider = gr.Slider(0.0, 1.0, value=0.7, step=0.05, label="Genesis Temp") # Gemini often uses 0-1 range
363
- gen_max_tokens_slider = gr.Slider(100, 2048, value=512, step=64, label="Genesis Max Tokens")
364
  with gr.Row():
365
  eval_temp_slider = gr.Slider(0.0, 1.0, value=0.4, step=0.05, label="Crucible Temp")
366
- eval_max_tokens_slider = gr.Slider(100, 1024, value=300, step=64, label="Crucible Max Tokens")
367
  with gr.Row():
368
  evolve_temp_slider = gr.Slider(0.0, 1.0, value=0.75, step=0.05, label="Evolution Temp")
369
- evolve_max_tokens_slider = gr.Slider(100, 2048, value=768, step=64, label="Evolution Max Tokens")
 
370
 
371
  submit_btn = gr.Button("🚀 ENGAGE ALGOFORGE PRIME™ 🚀", variant="primary", size="lg")
372
 
373
- with gr.Column(scale=2):
374
  gr.Markdown("## 🔥 3. The Forge's Output")
375
  with gr.Tabs():
376
- with gr.TabItem("📜 Genesis Candidates & Crucible Verdicts"):
377
- output_initial_solutions_md = gr.Markdown(label="LLM-Generated Initial Solutions & Evaluations")
378
  with gr.TabItem("🏆 Champion Candidate (Pre-Evolution)"):
379
- output_best_solution_md = gr.Markdown(label="Evaluator's Top Pick")
380
- with gr.TabItem("🌟 Evolved Artifact"):
381
- output_evolved_solution_md = gr.Markdown(label="Refined Solution from the Evolutionary Forge")
 
382
  with gr.TabItem("🛠️ Interaction Log (Dev View)"):
383
  output_interaction_log_md = gr.Markdown(label="Detailed Log of LLM Prompts & Responses")
 
 
 
 
 
 
384
 
385
  submit_btn.click(
386
  fn=run_algoforge_simulation,
387
  inputs=[
388
- problem_type_dd, problem_desc_tb, initial_hints_tb,
389
  num_solutions_slider, model_select_dd,
390
  gen_temp_slider, gen_max_tokens_slider,
391
  eval_temp_slider, eval_max_tokens_slider,
392
  evolve_temp_slider, evolve_max_tokens_slider
393
  ],
394
- outputs=[
395
- output_initial_solutions_md, output_best_solution_md,
396
- output_evolved_solution_md, output_interaction_log_md
397
- ]
398
  )
399
  gr.Markdown("---")
400
  gr.Markdown(
401
- "**Disclaimer:** This is a conceptual demo. LLM outputs require rigorous human oversight. Use for inspiration and exploration."
402
- "\n*Powered by Gradio, Google Gemini API, Hugging Face Inference API, and innovation.*"
403
  )
404
 
 
405
  if __name__ == "__main__":
406
  print("="*80)
407
- print("AlgoForge Prime™ (Gemini Focused) Starting...")
408
- if not GEMINI_API_CONFIGURED: print("REMINDER: GOOGLE_API_KEY missing or config failed. Gemini API models disabled.")
409
- if not HF_API_CONFIGURED: print("REMINDER: HF_TOKEN missing or client init failed. Hugging Face models disabled.")
410
- if not GEMINI_API_CONFIGURED and not HF_API_CONFIGURED: print("CRITICAL: NEITHER API IS CONFIGURED. APP WILL NOT FUNCTION.")
411
- print(f"UI will attempt to default to model key: {DEFAULT_MODEL_KEY}")
412
  print(f"Available models for UI: {list(AVAILABLE_MODELS.keys())}")
413
  print("="*80)
414
  demo.launch(debug=True, server_name="0.0.0.0")
 
1
+ # algoforge_prime/app.py
2
  import gradio as gr
 
 
3
  import os
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
+ # Initialize core components first (important for loading API keys etc.)
6
+ # This needs to happen before other core modules try to use the status
7
+ from core.llm_clients import initialize_clients, GEMINI_API_CONFIGURED, HF_API_CONFIGURED
8
+ initialize_clients() # Explicitly initialize
 
 
 
 
9
 
10
+ from core.generation_engine import generate_initial_solutions
11
+ from core.evaluation_engine import evaluate_solution_candidate, EvaluationResult
12
+ from core.evolution_engine import evolve_solution
13
+ # from prompts.system_prompts import get_system_prompt # Might not be needed directly here if core modules handle it
14
+
15
+ # --- MODEL DEFINITIONS (can also be moved to a config file/module later) ---
 
 
 
 
 
16
  AVAILABLE_MODELS = {}
17
  DEFAULT_MODEL_KEY = None
18
 
 
19
  if GEMINI_API_CONFIGURED:
20
  AVAILABLE_MODELS.update({
21
  "Google Gemini 1.5 Flash (API - Fast, Recommended)": {"id": "gemini-1.5-flash-latest", "type": "google_gemini"},
 
23
  })
24
  DEFAULT_MODEL_KEY = "Google Gemini 1.5 Flash (API - Fast, Recommended)"
25
 
 
26
  if HF_API_CONFIGURED:
27
  AVAILABLE_MODELS.update({
28
  "Google Gemma 2B (HF - Quick Test)": {"id": "google/gemma-2b-it", "type": "hf"},
29
  "Mistral 7B Instruct (HF)": {"id": "mistralai/Mistral-7B-Instruct-v0.2", "type": "hf"},
 
30
  })
31
+ if not DEFAULT_MODEL_KEY:
32
  DEFAULT_MODEL_KEY = "Google Gemma 2B (HF - Quick Test)"
33
 
 
34
  if not AVAILABLE_MODELS:
 
 
35
  AVAILABLE_MODELS["No Models Available"] = {"id": "dummy", "type": "none"}
36
  DEFAULT_MODEL_KEY = "No Models Available"
37
+ elif not DEFAULT_MODEL_KEY:
38
  DEFAULT_MODEL_KEY = list(AVAILABLE_MODELS.keys())[0]
39
 
40
 
41
+ # --- Main Orchestration Logic ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def run_algoforge_simulation(
43
+ problem_type, problem_description, initial_hints, user_tests_string, # New input: user_tests_string
44
  num_initial_solutions, selected_model_key,
45
  gen_temp, gen_max_tokens,
46
  eval_temp, eval_max_tokens,
47
+ evolve_temp, evolve_max_tokens,
48
+ progress=gr.Progress(track_tqdm=True) # Gradio progress bar
49
  ):
50
+ progress(0, desc="Initializing AlgoForge Prime™...")
51
+ log_entries = [f"**AlgoForge Prime™ Cycle Starting...**"]
52
+
53
  if not problem_description:
54
+ return "ERROR: Problem Description is mandatory.", "", "", "", ""
55
 
56
+ model_config = AVAILABLE_MODELS.get(selected_model_key)
57
+ if not model_config or model_config["type"] == "none":
58
+ return f"ERROR: No valid model selected ('{selected_model_key}'). Check API key configs.", "", "", "", ""
59
 
60
+ log_entries.append(f"Selected Model: {selected_model_key} (Type: {model_config['type']}, ID: {model_config['id']})")
61
+ log_entries.append(f"Problem Type: {problem_type}, User Tests Provided: {'Yes' if user_tests_string else 'No'}")
62
+
63
+ # --- STAGE 1: GENESIS ---
64
+ progress(0.1, desc="Stage 1: Genesis Engine - Generating Solutions...")
65
+ log_entries.append("\n**Stage 1: Genesis Engine**")
66
 
67
+ llm_gen_config = {"type": model_config["type"], "model_id": model_config["id"], "temp": gen_temp, "max_tokens": gen_max_tokens}
68
+ initial_solution_texts = generate_initial_solutions(
69
+ problem_description, initial_hints, problem_type,
70
+ num_initial_solutions, llm_gen_config
71
+ )
72
+ log_entries.append(f"Generated {len(initial_solution_texts)} raw solution candidates.")
73
+ for i, sol_text in enumerate(initial_solution_texts):
74
+ log_entries.append(f" Candidate {i+1} (Snippet): {str(sol_text)[:100]}...")
75
+
76
+
77
+ valid_initial_solutions = [s for s in initial_solution_texts if s and not s.startswith("ERROR")]
78
+ if not valid_initial_solutions:
79
+ error_summary = "\n".join(set(s for s in initial_solution_texts if s and s.startswith("ERROR")))
80
+ return f"No valid solutions generated by Genesis Engine. Errors:\n{error_summary}", "", "", "\n".join(log_entries), ""
81
+
82
+ # --- STAGE 2: CRITIQUE & EVALUATION ---
83
+ progress(0.3, desc="Stage 2: Critique Crucible - Evaluating Candidates...")
84
+ log_entries.append("\n**Stage 2: Critique Crucible & Automated Evaluation**")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ evaluated_candidates_data = []
87
+ llm_eval_config = {"type": model_config["type"], "model_id": model_config["id"], "temp": eval_temp, "max_tokens": eval_max_tokens}
88
+
89
+ for i, sol_text in enumerate(initial_solution_texts): # Evaluate all, even errors, to show the error
90
+ progress(0.3 + (i / num_initial_solutions) * 0.4, desc=f"Evaluating Candidate {i+1}...")
91
+ log_entries.append(f"\nEvaluating Candidate {i+1}:")
92
+ if sol_text.startswith("ERROR"):
93
+ eval_res = EvaluationResult(score=0, critique=f"Candidate was an error from Genesis: {sol_text}")
94
+ log_entries.append(f" Skipping detailed evaluation for error: {sol_text}")
 
 
 
 
 
95
  else:
96
+ eval_res = evaluate_solution_candidate(
97
+ sol_text, problem_description, problem_type, user_tests_string, llm_eval_config
 
 
98
  )
99
+ log_entries.append(f" LLM Critique & Test Score: {eval_res.score}/10")
100
+ log_entries.append(f" Test Results: {eval_res.passed_tests}/{eval_res.total_tests} passed.")
101
+ if eval_res.execution_error: log_entries.append(f" Execution Error: {eval_res.execution_error}")
102
+ log_entries.append(f" Full Critique (Snippet): {str(eval_res.critique)[:150]}...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
+ evaluated_candidates_data.append({
105
+ "id": i + 1,
106
+ "solution_text": sol_text,
107
+ "evaluation": eval_res
108
+ })
109
+
110
+ # Format display for initial solutions
111
+ initial_solutions_display_md = []
112
+ for data in evaluated_candidates_data:
113
+ initial_solutions_display_md.append(
114
+ f"**Candidate {data['id']}:**\n```python\n{data['solution_text']}\n```\n"
115
+ f"**Evaluation Verdict (Score: {data['evaluation'].score}/10):**\n{data['evaluation'].critique}\n---"
116
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
+ # --- STAGE 3: SELECTION ---
119
+ progress(0.75, desc="Stage 3: Selecting Champion...")
120
+ # Filter out candidates that were errors from genesis before sorting by score
121
+ valid_evaluated_candidates = [cand for cand in evaluated_candidates_data if not cand['solution_text'].startswith("ERROR")]
122
+ if not valid_evaluated_candidates:
123
+ return "\n\n".join(initial_solutions_display_md), "No valid candidates to select from after evaluation.", "", "\n".join(log_entries), ""
124
+
125
+ valid_evaluated_candidates.sort(key=lambda x: x["evaluation"].score, reverse=True)
126
+ best_candidate_data = valid_evaluated_candidates[0]
127
+ log_entries.append(f"\n**Stage 3: Champion Selected**\nCandidate {best_candidate_data['id']} chosen with score {best_candidate_data['evaluation'].score}/10.")
128
+
129
+ best_solution_display_md = (
130
+ f"**Champion Candidate {best_candidate_data['id']} (Original Score: {best_candidate_data['evaluation'].score}/10):**\n"
131
+ f"```python\n{best_candidate_data['solution_text']}\n```\n"
132
+ f"**Original Comprehensive Evaluation:**\n{best_candidate_data['evaluation'].critique}"
133
  )
134
+
135
+ # --- STAGE 4: EVOLUTION ---
136
+ progress(0.8, desc="Stage 4: Evolutionary Forge - Refining Champion...")
137
+ log_entries.append("\n**Stage 4: Evolutionary Forge**")
138
+ llm_evolve_config = {"type": model_config["type"], "model_id": model_config["id"], "temp": evolve_temp, "max_tokens": evolve_max_tokens}
139
 
140
+ evolved_solution_text = evolve_solution(
141
+ best_candidate_data["solution_text"],
142
+ str(best_candidate_data["evaluation"].critique), # Pass the full critique including test results
143
+ best_candidate_data["evaluation"].score,
144
+ problem_description,
145
+ problem_type,
146
+ llm_evolve_config
147
+ )
148
+ log_entries.append(f"Evolved solution text (Snippet): {str(evolved_solution_text)[:150]}...")
149
+
150
+ evolved_solution_display_md = ""
151
+ final_thoughts_md = "" # For LLM explanation of unit test results if needed
152
+
153
+ if evolved_solution_text.startswith("ERROR"):
154
+ evolved_solution_display_md = f"**Evolution Failed:**\n{evolved_solution_text}"
155
+ else:
156
+ evolved_solution_display_md = f"**✨ AlgoForge Prime™ Evolved Artifact ✨:**\n```python\n{evolved_solution_text}\n```"
157
+ # Optionally, re-evaluate the evolved solution with unit tests if provided
158
+ if "python" in problem_type.lower() and user_tests_string:
159
+ progress(0.9, desc="Re-evaluating Evolved Solution with Tests...")
160
+ log_entries.append("\n**Post-Evolution Sanity Check (Re-running Tests on Evolved Code)**")
161
+ # Using a neutral LLM config for this, or could be separate
162
+ # This evaluation is primarily for the test results, not another LLM critique of the evolved code
163
+ evolved_eval_res = evaluate_solution_candidate(
164
+ evolved_solution_text, problem_description, problem_type, user_tests_string,
165
+ {"type": model_config["type"], "model_id": model_config["id"], "temp": 0.1, "max_tokens": eval_max_tokens} # Low temp for focused test eval
166
+ )
167
+ evolved_solution_display_md += (
168
+ f"\n\n**Post-Evolution Test Results (Simulated):**\n"
169
+ f"Passed: {evolved_eval_res.passed_tests}/{evolved_eval_res.total_tests}\n"
170
+ )
171
+ if evolved_eval_res.execution_error:
172
+ evolved_solution_display_md += f"Execution Output/Error: {evolved_eval_res.execution_error}\n"
173
+ log_entries.append(f" Evolved Code Test Results: {evolved_eval_res.passed_tests}/{evolved_eval_res.total_tests} passed.")
174
+
175
+ # Get LLM to explain the test results of the evolved code
176
+ # progress(0.95, desc="Explaining Evolved Code Test Results...")
177
+ # explain_prompt = f"The following Python code was generated: \n```python\n{evolved_solution_text}\n```\nIt was tested against these assertions:\n```python\n{user_tests_string}\n```\nThe test outcome was: {evolved_eval_res.passed_tests}/{evolved_eval_res.total_tests} passed. \nExecution/Error details: {evolved_eval_res.execution_error}\n\nProvide a brief analysis of these test results for the given code."
178
+ # explain_sys_prompt = get_system_prompt("code_execution_explainer")
179
+ # explanation_response = dispatch_llm_call_simplified(explain_prompt, explain_sys_prompt, llm_evolve_config) # Need a simplified dispatcher or use the full one
180
+ # final_thoughts_md = f"**AI Analysis of Evolved Code's Test Results:**\n{explanation_response}"
181
+
182
+
183
  log_entries.append("\n**AlgoForge Prime™ Cycle Complete.**")
184
+ progress(1.0, desc="Cycle Complete!")
185
+ return "\n\n".join(initial_solutions_display_md), best_solution_display_md, evolved_solution_display_md, "\n".join(log_entries), final_thoughts_md
186
 
 
187
 
188
+ # --- GRADIO UI (largely similar, but with a new input for user tests) ---
189
  intro_markdown = """
190
+ # ✨ AlgoForge Prime™ ✨: Modular Algorithmic Evolution
191
+ This enhanced version demonstrates a more structured approach to AI-assisted algorithm discovery,
192
+ featuring basic (simulated) unit testing for Python code.
 
193
 
194
  **API Keys Required in Space Secrets:**
195
+ - `GOOGLE_API_KEY` (Primary): For Google Gemini API models.
196
+ - `HF_TOKEN` (Secondary): For Hugging Face hosted models.
 
197
  """
 
198
  token_status_md = ""
199
  if not GEMINI_API_CONFIGURED and not HF_API_CONFIGURED:
200
+ token_status_md = "<p style='color:red;'>⚠️ CRITICAL: NEITHER API IS CONFIGURED. APP WILL NOT FUNCTION.</p>"
201
  else:
202
+ if GEMINI_API_CONFIGURED: token_status_md += "<p style='color:green;'>✅ Google Gemini API Key detected.</p>"
203
+ else: token_status_md += "<p style='color:orange;'>⚠️ GOOGLE_API_KEY missing/failed. Gemini models disabled.</p>"
204
+ if HF_API_CONFIGURED: token_status_md += "<p style='color:green;'>✅ Hugging Face API Token detected.</p>"
205
+ else: token_status_md += "<p style='color:orange;'>⚠️ HF_TOKEN missing/failed. Hugging Face models disabled.</p>"
 
 
 
 
 
206
 
207
 
208
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue="teal", secondary_hue="cyan"), title="AlgoForge Prime™ Modular") as demo:
209
  gr.Markdown(intro_markdown)
210
+ gr.HTML(token_status_md)
211
 
212
  if not AVAILABLE_MODELS or DEFAULT_MODEL_KEY == "No Models Available":
213
+ gr.Markdown("<h2 style='color:red;'>No models are available. Check API keys and restart.</h2>")
214
  else:
215
  with gr.Row():
216
+ with gr.Column(scale=2): # Made input column wider
217
  gr.Markdown("## 💡 1. Define the Challenge")
218
  problem_type_dd = gr.Dropdown(
219
+ ["Python Algorithm with Tests", "Python Algorithm (Critique Only)", "General Algorithm Idea", "Conceptual System Design"],
220
+ label="Type of Problem/Algorithm", value="Python Algorithm with Tests"
221
  )
222
  problem_desc_tb = gr.Textbox(
223
+ lines=4, label="Problem Description / Desired Outcome",
224
+ placeholder="e.g., 'Python function `is_palindrome(s: str) -> bool` that checks if a string is a palindrome, ignoring case and non-alphanumeric chars.'"
225
  )
226
  initial_hints_tb = gr.Textbox(
227
+ lines=2, label="Initial Thoughts / Constraints (Optional)",
228
+ placeholder="e.g., 'Iterative approach preferred.' or 'Handle empty strings.'"
229
+ )
230
+ # NEW INPUT for User Tests
231
+ user_tests_tb = gr.Textbox(
232
+ lines=5, label="Python Unit Tests (Optional, one `assert` per line)",
233
+ placeholder="assert is_palindrome('Racecar!') == True\nassert is_palindrome('hello') == False\nassert is_palindrome('') == True",
234
+ info="For 'Python Algorithm with Tests' type. Ignored otherwise."
235
  )
236
 
237
  gr.Markdown("## ⚙️ 2. Configure The Forge")
238
  model_select_dd = gr.Dropdown(
239
  choices=list(AVAILABLE_MODELS.keys()),
240
+ value=DEFAULT_MODEL_KEY if DEFAULT_MODEL_KEY in AVAILABLE_MODELS else (list(AVAILABLE_MODELS.keys())[0] if AVAILABLE_MODELS else None),
241
  label="Select LLM Core Model"
242
  )
243
+ num_solutions_slider = gr.Slider(1, 3, value=2, step=1, label="Number of Initial Solutions (Genesis Engine)") # Max 3 for faster runs
244
 
245
  with gr.Accordion("Advanced LLM Parameters", open=False):
246
+ # ... (temp and max_tokens sliders - same as before) ...
247
  with gr.Row():
248
+ gen_temp_slider = gr.Slider(0.0, 1.0, value=0.7, step=0.05, label="Genesis Temp")
249
+ gen_max_tokens_slider = gr.Slider(200, 2048, value=768, step=64, label="Genesis Max Tokens")
250
  with gr.Row():
251
  eval_temp_slider = gr.Slider(0.0, 1.0, value=0.4, step=0.05, label="Crucible Temp")
252
+ eval_max_tokens_slider = gr.Slider(150, 1024, value=512, step=64, label="Crucible Max Tokens")
253
  with gr.Row():
254
  evolve_temp_slider = gr.Slider(0.0, 1.0, value=0.75, step=0.05, label="Evolution Temp")
255
+ evolve_max_tokens_slider = gr.Slider(200, 2048, value=1024, step=64, label="Evolution Max Tokens")
256
+
257
 
258
  submit_btn = gr.Button("🚀 ENGAGE ALGOFORGE PRIME™ 🚀", variant="primary", size="lg")
259
 
260
+ with gr.Column(scale=3): # Made output column wider
261
  gr.Markdown("## 🔥 3. The Forge's Output")
262
  with gr.Tabs():
263
+ with gr.TabItem("📜 Genesis Candidates & Evaluations"):
264
+ output_initial_solutions_md = gr.Markdown(label="Generated Solutions & Combined Evaluations")
265
  with gr.TabItem("🏆 Champion Candidate (Pre-Evolution)"):
266
+ output_best_solution_md = gr.Markdown(label="Top Pick for Refinement")
267
+ with gr.TabItem("🌟 Evolved Artifact (& Test Analysis)"):
268
+ output_evolved_solution_md = gr.Markdown(label="Refined Solution from Evolutionary Forge")
269
+ # output_final_thoughts_md = gr.Markdown(label="AI Analysis of Evolved Code's Tests") # Optional separate output
270
  with gr.TabItem("🛠️ Interaction Log (Dev View)"):
271
  output_interaction_log_md = gr.Markdown(label="Detailed Log of LLM Prompts & Responses")
272
+
273
+ outputs_list = [
274
+ output_initial_solutions_md, output_best_solution_md,
275
+ output_evolved_solution_md, output_interaction_log_md,
276
+ gr.Markdown() # Placeholder for final_thoughts_md if you add it as a separate component
277
+ ]
278
 
279
  submit_btn.click(
280
  fn=run_algoforge_simulation,
281
  inputs=[
282
+ problem_type_dd, problem_desc_tb, initial_hints_tb, user_tests_tb, # Added user_tests_tb
283
  num_solutions_slider, model_select_dd,
284
  gen_temp_slider, gen_max_tokens_slider,
285
  eval_temp_slider, eval_max_tokens_slider,
286
  evolve_temp_slider, evolve_max_tokens_slider
287
  ],
288
+ outputs=outputs_list
 
 
 
289
  )
290
  gr.Markdown("---")
291
  gr.Markdown(
292
+ "**Disclaimer:** Modular demo. (Simulated) unit testing is illustrative. **NEVER run LLM-generated code from an untrusted source in an unrestricted environment.** "
293
+ "Real sandboxing is complex and critical for safety."
294
  )
295
 
296
+ # --- Entry Point ---
297
  if __name__ == "__main__":
298
  print("="*80)
299
+ print("AlgoForge Prime™ (Modular Version) Starting...")
300
+ # ... (startup print messages for API key status - same as before) ...
301
+ print(f"UI default model key: {DEFAULT_MODEL_KEY}")
 
 
302
  print(f"Available models for UI: {list(AVAILABLE_MODELS.keys())}")
303
  print("="*80)
304
  demo.launch(debug=True, server_name="0.0.0.0")