Spaces:
Sleeping
Sleeping
Kai Izumoto
commited on
Update app.py
Browse files
app.py
CHANGED
|
@@ -28,6 +28,7 @@ PYTHON_MODEL = "Nxcode-CQ-7B-orpo"
|
|
| 28 |
OTHER_MODEL = "Qwen/Qwen2.5-Coder-32B-Instruct"
|
| 29 |
FALLBACK_MODELS = [
|
| 30 |
"Qwen/Qwen2.5-Coder-32B-Instruct",
|
|
|
|
| 31 |
"OpenCodeInterpreter-DS-33B"
|
| 32 |
]
|
| 33 |
|
|
@@ -167,6 +168,8 @@ def call_model(client: InferenceClient, system: str, user: str, is_python: bool,
|
|
| 167 |
primary_model = PYTHON_MODEL if is_python else OTHER_MODEL
|
| 168 |
models_to_try = [primary_model] + FALLBACK_MODELS
|
| 169 |
|
|
|
|
|
|
|
| 170 |
messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
|
| 171 |
|
| 172 |
last_exception = None
|
|
@@ -232,33 +235,66 @@ def validate_files_dict(files: Dict[str, str]) -> bool:
|
|
| 232 |
def parse_meta(text: str) -> Optional[Dict[str, Any]]:
|
| 233 |
"""Parses model output to extract code files, trying structured JSON first, then falling back to heuristics."""
|
| 234 |
# Strict JSON/META block parsing
|
| 235 |
-
for pattern in [r"```json\s*(.*?)```", r"```meta\s*(.*?)```"]:
|
| 236 |
match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
|
| 237 |
if match:
|
| 238 |
try:
|
| 239 |
content = match.group(1).strip()
|
| 240 |
parsed = json.loads(content)
|
| 241 |
if "files" in parsed and validate_files_dict(parsed["files"]):
|
|
|
|
| 242 |
return parsed
|
| 243 |
-
except (json.JSONDecodeError, TypeError):
|
|
|
|
| 244 |
continue
|
| 245 |
|
| 246 |
# Fallback to heuristic parsing of code blocks
|
| 247 |
files = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 248 |
code_blocks = re.findall(r"```(?:\w+)?\s*([\s\S]*?)```", text, re.DOTALL)
|
|
|
|
| 249 |
if not code_blocks:
|
|
|
|
| 250 |
return None
|
| 251 |
|
| 252 |
-
#
|
| 253 |
-
# This is a simplified heuristic; more advanced logic could be added
|
| 254 |
-
potential_filenames = re.findall(r'#\s*File:\s*([\w/._-]+)', text)
|
| 255 |
for i, block in enumerate(code_blocks):
|
| 256 |
-
|
| 257 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
|
| 259 |
-
if validate_files_dict(files):
|
|
|
|
| 260 |
return {"files": files, "changelog": "Extracted files via heuristic parsing."}
|
| 261 |
-
|
|
|
|
| 262 |
return None
|
| 263 |
|
| 264 |
# ---------- Enhanced evaluators ----------
|
|
@@ -468,11 +504,16 @@ Return the perfected code in META format."""
|
|
| 468 |
response = call_model(self.client, system, prompt, self.is_python, **self.settings)
|
| 469 |
|
| 470 |
if "<<ERROR" in response:
|
|
|
|
| 471 |
return {"success": False, "warning": "Model error - keeping previous code"}
|
| 472 |
|
| 473 |
meta = parse_meta(response)
|
| 474 |
if not meta or not meta.get("files"):
|
| 475 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 476 |
|
| 477 |
files = meta["files"]
|
| 478 |
write_files(workdir, files)
|
|
@@ -508,17 +549,22 @@ Return the perfected code in META format."""
|
|
| 508 |
if self.stop_flag.exists():
|
| 509 |
try:
|
| 510 |
self.stop_flag.unlink()
|
| 511 |
-
|
| 512 |
-
|
|
|
|
|
|
|
| 513 |
break
|
| 514 |
|
| 515 |
-
yield self.format_output(f"Iteration {iteration}/{max_iterations} running...", iteration, max_iterations)
|
| 516 |
|
| 517 |
result = self.perform_iteration(iteration)
|
| 518 |
|
| 519 |
if not result.get("success"):
|
| 520 |
-
|
| 521 |
-
|
|
|
|
|
|
|
|
|
|
| 522 |
iteration += 1
|
| 523 |
continue
|
| 524 |
|
|
@@ -539,6 +585,10 @@ Return the perfected code in META format."""
|
|
| 539 |
self.best_zip = result.get("zip")
|
| 540 |
self.best_review = result.get("review", "")
|
| 541 |
self.best_readme = result.get("readme", "")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 542 |
|
| 543 |
if result.get("workdir") and result.get("workdir") != self.best_workspace:
|
| 544 |
self.cleanup_workdir(Path(result["workdir"]))
|
|
@@ -669,9 +719,15 @@ def create_ui():
|
|
| 669 |
|
| 670 |
def set_stop(controller_state_val):
|
| 671 |
if controller_state_val and (stop_path_str := controller_state_val.get("stop_flag_path")):
|
| 672 |
-
Path(stop_path_str)
|
| 673 |
-
|
| 674 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 675 |
|
| 676 |
outputs = [
|
| 677 |
run_log, model_display, progress_display, metrics_html, eval_json,
|
|
|
|
| 28 |
OTHER_MODEL = "Qwen/Qwen2.5-Coder-32B-Instruct"
|
| 29 |
FALLBACK_MODELS = [
|
| 30 |
"Qwen/Qwen2.5-Coder-32B-Instruct",
|
| 31 |
+
"Nxcode-CQ-7B-orpo",
|
| 32 |
"OpenCodeInterpreter-DS-33B"
|
| 33 |
]
|
| 34 |
|
|
|
|
| 168 |
primary_model = PYTHON_MODEL if is_python else OTHER_MODEL
|
| 169 |
models_to_try = [primary_model] + FALLBACK_MODELS
|
| 170 |
|
| 171 |
+
logging.info(f"Calling model for {'Python' if is_python else 'Other'} project. Primary: {primary_model}")
|
| 172 |
+
|
| 173 |
messages = [{"role": "system", "content": system}, {"role": "user", "content": user}]
|
| 174 |
|
| 175 |
last_exception = None
|
|
|
|
| 235 |
def parse_meta(text: str) -> Optional[Dict[str, Any]]:
|
| 236 |
"""Parses model output to extract code files, trying structured JSON first, then falling back to heuristics."""
|
| 237 |
# Strict JSON/META block parsing
|
| 238 |
+
for pattern in [r"```json\s*(.*?)```", r"```meta\s*(.*?)```", r"```META\s*(.*?)```"]:
|
| 239 |
match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
|
| 240 |
if match:
|
| 241 |
try:
|
| 242 |
content = match.group(1).strip()
|
| 243 |
parsed = json.loads(content)
|
| 244 |
if "files" in parsed and validate_files_dict(parsed["files"]):
|
| 245 |
+
logging.info(f"Successfully parsed META JSON with {len(parsed['files'])} files")
|
| 246 |
return parsed
|
| 247 |
+
except (json.JSONDecodeError, TypeError) as e:
|
| 248 |
+
logging.warning(f"JSON parse failed: {e}")
|
| 249 |
continue
|
| 250 |
|
| 251 |
# Fallback to heuristic parsing of code blocks
|
| 252 |
files = {}
|
| 253 |
+
|
| 254 |
+
# Try to find filename markers before code blocks
|
| 255 |
+
# Patterns like: # File: main.py or ## main.py or **main.py**
|
| 256 |
+
filename_patterns = [
|
| 257 |
+
r'#\s*[Ff]ile:\s*([\w/._-]+\.[\w]+)',
|
| 258 |
+
r'##\s*([\w/._-]+\.[\w]+)',
|
| 259 |
+
r'\*\*\s*([\w/._-]+\.[\w]+)\s*\*\*'
|
| 260 |
+
]
|
| 261 |
+
|
| 262 |
+
all_filenames = []
|
| 263 |
+
for pattern in filename_patterns:
|
| 264 |
+
all_filenames.extend(re.findall(pattern, text))
|
| 265 |
+
|
| 266 |
code_blocks = re.findall(r"```(?:\w+)?\s*([\s\S]*?)```", text, re.DOTALL)
|
| 267 |
+
|
| 268 |
if not code_blocks:
|
| 269 |
+
logging.warning("No code blocks found in model response")
|
| 270 |
return None
|
| 271 |
|
| 272 |
+
# Match filenames with code blocks
|
|
|
|
|
|
|
| 273 |
for i, block in enumerate(code_blocks):
|
| 274 |
+
block_content = block.strip()
|
| 275 |
+
if not block_content:
|
| 276 |
+
continue
|
| 277 |
+
|
| 278 |
+
if i < len(all_filenames):
|
| 279 |
+
filename = all_filenames[i]
|
| 280 |
+
else:
|
| 281 |
+
# Guess filename based on content
|
| 282 |
+
if "def test_" in block_content or "import pytest" in block_content:
|
| 283 |
+
filename = f"test_main.py"
|
| 284 |
+
elif "requirements" in text.lower() and i == 0:
|
| 285 |
+
filename = "requirements.txt"
|
| 286 |
+
elif "# README" in block_content or "## " in block_content[:100]:
|
| 287 |
+
filename = "README.md"
|
| 288 |
+
else:
|
| 289 |
+
filename = f"main.py" if i == 0 else f"file_{i}.py"
|
| 290 |
+
|
| 291 |
+
files[filename] = block_content
|
| 292 |
|
| 293 |
+
if validate_files_dict(files) and files:
|
| 294 |
+
logging.info(f"Heuristic parsing extracted {len(files)} files: {list(files.keys())}")
|
| 295 |
return {"files": files, "changelog": "Extracted files via heuristic parsing."}
|
| 296 |
+
|
| 297 |
+
logging.error("Failed to extract any valid files from model response")
|
| 298 |
return None
|
| 299 |
|
| 300 |
# ---------- Enhanced evaluators ----------
|
|
|
|
| 504 |
response = call_model(self.client, system, prompt, self.is_python, **self.settings)
|
| 505 |
|
| 506 |
if "<<ERROR" in response:
|
| 507 |
+
logging.error(f"Model returned error: {response[:200]}")
|
| 508 |
return {"success": False, "warning": "Model error - keeping previous code"}
|
| 509 |
|
| 510 |
meta = parse_meta(response)
|
| 511 |
if not meta or not meta.get("files"):
|
| 512 |
+
logging.error(f"Parse failed. Response preview: {response[:500]}")
|
| 513 |
+
# Save failed response for debugging
|
| 514 |
+
with open(f"/tmp/failed_response_{iteration}.txt", "w") as f:
|
| 515 |
+
f.write(response)
|
| 516 |
+
return {"success": False, "warning": f"Parse failed - see /tmp/failed_response_{iteration}.txt"}
|
| 517 |
|
| 518 |
files = meta["files"]
|
| 519 |
write_files(workdir, files)
|
|
|
|
| 549 |
if self.stop_flag.exists():
|
| 550 |
try:
|
| 551 |
self.stop_flag.unlink()
|
| 552 |
+
logging.info("Stop flag detected - stopping generation")
|
| 553 |
+
except OSError:
|
| 554 |
+
pass
|
| 555 |
+
yield self.format_output("⛔ Stopped by user", iteration, max_iterations)
|
| 556 |
break
|
| 557 |
|
| 558 |
+
yield self.format_output(f"🔄 Iteration {iteration}/{max_iterations} running...", iteration, max_iterations)
|
| 559 |
|
| 560 |
result = self.perform_iteration(iteration)
|
| 561 |
|
| 562 |
if not result.get("success"):
|
| 563 |
+
warning_msg = result.get("warning", "Unknown iteration error")
|
| 564 |
+
logging.warning(f"Iteration {iteration} failed: {warning_msg}")
|
| 565 |
+
# CRITICAL: Still yield output so UI updates and shows the previous best code
|
| 566 |
+
yield self.format_output(f"⚠️ Iteration {iteration} failed: {warning_msg}", iteration, max_iterations)
|
| 567 |
+
time.sleep(2) # Give more time before retry
|
| 568 |
iteration += 1
|
| 569 |
continue
|
| 570 |
|
|
|
|
| 585 |
self.best_zip = result.get("zip")
|
| 586 |
self.best_review = result.get("review", "")
|
| 587 |
self.best_readme = result.get("readme", "")
|
| 588 |
+
logging.info(f"New best score: {score}/100")
|
| 589 |
+
else:
|
| 590 |
+
# Even if score didn't improve, still update current_files for next iteration
|
| 591 |
+
logging.info(f"Score {score}/100 - keeping best: {self.best_score}/100")
|
| 592 |
|
| 593 |
if result.get("workdir") and result.get("workdir") != self.best_workspace:
|
| 594 |
self.cleanup_workdir(Path(result["workdir"]))
|
|
|
|
| 719 |
|
| 720 |
def set_stop(controller_state_val):
|
| 721 |
if controller_state_val and (stop_path_str := controller_state_val.get("stop_flag_path")):
|
| 722 |
+
stop_path = Path(stop_path_str)
|
| 723 |
+
try:
|
| 724 |
+
stop_path.touch()
|
| 725 |
+
logging.info(f"Stop flag created at {stop_path}")
|
| 726 |
+
return "⛔ Stop signal sent! Will stop after current iteration completes..."
|
| 727 |
+
except Exception as e:
|
| 728 |
+
logging.error(f"Failed to create stop flag: {e}")
|
| 729 |
+
return f"❌ Failed to stop: {e}"
|
| 730 |
+
return "❌ Could not stop: No active process found. Start generation first."
|
| 731 |
|
| 732 |
outputs = [
|
| 733 |
run_log, model_display, progress_display, metrics_html, eval_json,
|