From e2d089fa6b342d7d07ac0ca1c831b85e3c725994 Mon Sep 17 00:00:00 2001 From: "jhewers@proximafusion.com" Date: Wed, 4 Mar 2026 14:24:04 +0000 Subject: [PATCH 1/2] fix: Catch case where current_changes_description is None --- openevolve/prompt/sampler.py | 100 ++++++++++++++++++++++++----------- 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/openevolve/prompt/sampler.py b/openevolve/prompt/sampler.py index 61a5b98ba0..b793957784 100644 --- a/openevolve/prompt/sampler.py +++ b/openevolve/prompt/sampler.py @@ -110,11 +110,17 @@ def build_prompt( if self.config.programs_as_changes_description: if self.config.system_message_changes_description: - system_message_changes_description = self.config.system_message_changes_description.strip() + system_message_changes_description = ( + self.config.system_message_changes_description.strip() + ) else: - system_message_changes_description = self.template_manager.get_template("system_message_changes_description") + system_message_changes_description = self.template_manager.get_template( + "system_message_changes_description" + ) - system_message = self.template_manager.get_template("system_message_with_changes_description").format( + system_message = self.template_manager.get_template( + "system_message_with_changes_description" + ).format( system_message=system_message, system_message_changes_description=system_message_changes_description, ) @@ -160,8 +166,10 @@ def build_prompt( **kwargs, ) - if self.config.programs_as_changes_description: - user_message = self.template_manager.get_template("user_message_with_changes_description").format( + if self.config.programs_as_changes_description and current_changes_description is not None: + user_message = self.template_manager.get_template( + "user_message_with_changes_description" + ).format( user_message=user_message, changes_description=current_changes_description.rstrip(), ) @@ -265,11 +273,8 @@ def _format_evolution_history( for i, program in enumerate(reversed(selected_previous)): attempt_number = len(previous_programs) - i - changes = ( - program.get("changes_description") - or program.get("metadata", {}).get( - "changes", self.template_manager.get_fragment("attempt_unknown_changes") - ) + changes = program.get("changes_description") or program.get("metadata", {}).get( + "changes", self.template_manager.get_fragment("attempt_unknown_changes") ) # Format performance metrics using safe formatting @@ -334,9 +339,7 @@ def _format_evolution_history( for i, program in enumerate(selected_top): use_changes = self.config.programs_as_changes_description program_code = ( - program.get("changes_description", "") - if use_changes - else program.get("code", "") + program.get("changes_description", "") if use_changes else program.get("code", "") ) if not program_code: program_code = "" if use_changes else "" @@ -351,11 +354,20 @@ def _format_evolution_history( for name, value in program.get("metrics", {}).items(): if isinstance(value, (int, float)): try: - key_features.append(self.template_manager.get_fragment("top_program_metrics_prefix") + f" {name} ({value:.4f})") + key_features.append( + self.template_manager.get_fragment("top_program_metrics_prefix") + + f" {name} ({value:.4f})" + ) except (ValueError, TypeError): - key_features.append(self.template_manager.get_fragment("top_program_metrics_prefix") + f" {name} ({value})") + key_features.append( + self.template_manager.get_fragment("top_program_metrics_prefix") + + f" {name} ({value})" + ) else: - key_features.append(self.template_manager.get_fragment("top_program_metrics_prefix") + f" {name} ({value})") + key_features.append( + self.template_manager.get_fragment("top_program_metrics_prefix") + + f" {name} ({value})" + ) key_features_str = ", ".join(key_features) @@ -385,7 +397,11 @@ def _format_evolution_history( # Use random sampling to get diverse programs diverse_programs = random.sample(remaining_programs, num_diverse) - diverse_programs_str += "\n\n## " + self.template_manager.get_fragment("diverse_programs_title") + "\n\n" + diverse_programs_str += ( + "\n\n## " + + self.template_manager.get_fragment("diverse_programs_title") + + "\n\n" + ) for i, program in enumerate(diverse_programs): use_changes = self.config.programs_as_changes_description @@ -404,7 +420,8 @@ def _format_evolution_history( key_features = program.get("key_features", []) if not key_features: key_features = [ - self.template_manager.get_fragment("diverse_program_metrics_prefix") + f" {name}" + self.template_manager.get_fragment("diverse_program_metrics_prefix") + + f" {name}" for name in list(program.get("metrics", {}).keys())[ :2 ] # Just first 2 metrics @@ -416,7 +433,9 @@ def _format_evolution_history( top_program_template.format( program_number=f"D{i + 1}", score=f"{score:.4f}", - language=("text" if self.config.programs_as_changes_description else language), + language=( + "text" if self.config.programs_as_changes_description else language + ), program_snippet=program_code, key_features=key_features_str, ) @@ -466,9 +485,7 @@ def _format_inspirations_section( for i, program in enumerate(inspirations): use_changes = self.config.programs_as_changes_description program_code = ( - program.get("changes_description", "") - if use_changes - else program.get("code", "") + program.get("changes_description", "") if use_changes else program.get("code", "") ) if not program_code: program_code = "" if use_changes else "" @@ -551,16 +568,24 @@ def _extract_unique_features(self, program: Dict[str, Any]) -> str: and self.config.include_changes_under_chars and len(changes) < self.config.include_changes_under_chars ): - features.append(self.template_manager.get_fragment("inspiration_changes_prefix").format(changes=changes)) + features.append( + self.template_manager.get_fragment("inspiration_changes_prefix").format( + changes=changes + ) + ) # Analyze metrics for standout characteristics metrics = program.get("metrics", {}) for metric_name, value in metrics.items(): if isinstance(value, (int, float)): if value >= 0.9: - features.append(f"{self.template_manager.get_fragment('inspiration_metrics_excellent').format(metric_name=metric_name, value=value)}") + features.append( + f"{self.template_manager.get_fragment('inspiration_metrics_excellent').format(metric_name=metric_name, value=value)}" + ) elif value <= 0.3: - features.append(f"{self.template_manager.get_fragment('inspiration_metrics_alternative').format(metric_name=metric_name)}") + features.append( + f"{self.template_manager.get_fragment('inspiration_metrics_alternative').format(metric_name=metric_name)}" + ) # Code-based features (simple heuristics) code = program.get("code", "") @@ -571,22 +596,32 @@ def _extract_unique_features(self, program: Dict[str, Any]) -> str: if "numpy" in code_lower or "np." in code_lower: features.append(self.template_manager.get_fragment("inspiration_code_with_numpy")) if "for" in code_lower and "while" in code_lower: - features.append(self.template_manager.get_fragment("inspiration_code_with_mixed_iteration")) + features.append( + self.template_manager.get_fragment("inspiration_code_with_mixed_iteration") + ) if ( self.config.concise_implementation_max_lines and len(code.split("\n")) <= self.config.concise_implementation_max_lines ): - features.append(self.template_manager.get_fragment("inspiration_code_with_concise_line")) + features.append( + self.template_manager.get_fragment("inspiration_code_with_concise_line") + ) elif ( self.config.comprehensive_implementation_min_lines and len(code.split("\n")) >= self.config.comprehensive_implementation_min_lines ): - features.append(self.template_manager.get_fragment("inspiration_code_with_comprehensive_line")) + features.append( + self.template_manager.get_fragment("inspiration_code_with_comprehensive_line") + ) # Default if no specific features found if not features: program_type = self._determine_program_type(program) - features.append(self.template_manager.get_fragment("inspiration_no_features_postfix").format(program_type=program_type)) + features.append( + self.template_manager.get_fragment("inspiration_no_features_postfix").format( + program_type=program_type + ) + ) # Use num_top_programs as limit for features (similar to how we limit programs) feature_limit = self.config.num_top_programs @@ -629,7 +664,12 @@ def _render_artifacts(self, artifacts: Dict[str, Union[str, bytes]]) -> str: sections.append(f"### {key}\n```\n{content}\n```") if sections: - return "## " + self.template_manager.get_fragment("artifact_title") + "\n\n" + "\n\n".join(sections) + return ( + "## " + + self.template_manager.get_fragment("artifact_title") + + "\n\n" + + "\n\n".join(sections) + ) else: return "" From 6eb0c060efe2ba866f68fbc656acff5a9fd96e4d Mon Sep 17 00:00:00 2001 From: "jhewers@proximafusion.com" Date: Wed, 4 Mar 2026 15:05:30 +0000 Subject: [PATCH 2/2] fix: Catch case where current_changes_description is None --- openevolve/prompt/sampler.py | 98 +++++++++++------------------------- 1 file changed, 29 insertions(+), 69 deletions(-) diff --git a/openevolve/prompt/sampler.py b/openevolve/prompt/sampler.py index b793957784..eeb49dabd5 100644 --- a/openevolve/prompt/sampler.py +++ b/openevolve/prompt/sampler.py @@ -110,17 +110,11 @@ def build_prompt( if self.config.programs_as_changes_description: if self.config.system_message_changes_description: - system_message_changes_description = ( - self.config.system_message_changes_description.strip() - ) + system_message_changes_description = self.config.system_message_changes_description.strip() else: - system_message_changes_description = self.template_manager.get_template( - "system_message_changes_description" - ) + system_message_changes_description = self.template_manager.get_template("system_message_changes_description") - system_message = self.template_manager.get_template( - "system_message_with_changes_description" - ).format( + system_message = self.template_manager.get_template("system_message_with_changes_description").format( system_message=system_message, system_message_changes_description=system_message_changes_description, ) @@ -167,9 +161,7 @@ def build_prompt( ) if self.config.programs_as_changes_description and current_changes_description is not None: - user_message = self.template_manager.get_template( - "user_message_with_changes_description" - ).format( + user_message = self.template_manager.get_template("user_message_with_changes_description").format( user_message=user_message, changes_description=current_changes_description.rstrip(), ) @@ -273,8 +265,11 @@ def _format_evolution_history( for i, program in enumerate(reversed(selected_previous)): attempt_number = len(previous_programs) - i - changes = program.get("changes_description") or program.get("metadata", {}).get( - "changes", self.template_manager.get_fragment("attempt_unknown_changes") + changes = ( + program.get("changes_description") + or program.get("metadata", {}).get( + "changes", self.template_manager.get_fragment("attempt_unknown_changes") + ) ) # Format performance metrics using safe formatting @@ -339,7 +334,9 @@ def _format_evolution_history( for i, program in enumerate(selected_top): use_changes = self.config.programs_as_changes_description program_code = ( - program.get("changes_description", "") if use_changes else program.get("code", "") + program.get("changes_description", "") + if use_changes + else program.get("code", "") ) if not program_code: program_code = "" if use_changes else "" @@ -354,20 +351,11 @@ def _format_evolution_history( for name, value in program.get("metrics", {}).items(): if isinstance(value, (int, float)): try: - key_features.append( - self.template_manager.get_fragment("top_program_metrics_prefix") - + f" {name} ({value:.4f})" - ) + key_features.append(self.template_manager.get_fragment("top_program_metrics_prefix") + f" {name} ({value:.4f})") except (ValueError, TypeError): - key_features.append( - self.template_manager.get_fragment("top_program_metrics_prefix") - + f" {name} ({value})" - ) + key_features.append(self.template_manager.get_fragment("top_program_metrics_prefix") + f" {name} ({value})") else: - key_features.append( - self.template_manager.get_fragment("top_program_metrics_prefix") - + f" {name} ({value})" - ) + key_features.append(self.template_manager.get_fragment("top_program_metrics_prefix") + f" {name} ({value})") key_features_str = ", ".join(key_features) @@ -397,11 +385,7 @@ def _format_evolution_history( # Use random sampling to get diverse programs diverse_programs = random.sample(remaining_programs, num_diverse) - diverse_programs_str += ( - "\n\n## " - + self.template_manager.get_fragment("diverse_programs_title") - + "\n\n" - ) + diverse_programs_str += "\n\n## " + self.template_manager.get_fragment("diverse_programs_title") + "\n\n" for i, program in enumerate(diverse_programs): use_changes = self.config.programs_as_changes_description @@ -420,8 +404,7 @@ def _format_evolution_history( key_features = program.get("key_features", []) if not key_features: key_features = [ - self.template_manager.get_fragment("diverse_program_metrics_prefix") - + f" {name}" + self.template_manager.get_fragment("diverse_program_metrics_prefix") + f" {name}" for name in list(program.get("metrics", {}).keys())[ :2 ] # Just first 2 metrics @@ -433,9 +416,7 @@ def _format_evolution_history( top_program_template.format( program_number=f"D{i + 1}", score=f"{score:.4f}", - language=( - "text" if self.config.programs_as_changes_description else language - ), + language=("text" if self.config.programs_as_changes_description else language), program_snippet=program_code, key_features=key_features_str, ) @@ -485,7 +466,9 @@ def _format_inspirations_section( for i, program in enumerate(inspirations): use_changes = self.config.programs_as_changes_description program_code = ( - program.get("changes_description", "") if use_changes else program.get("code", "") + program.get("changes_description", "") + if use_changes + else program.get("code", "") ) if not program_code: program_code = "" if use_changes else "" @@ -568,24 +551,16 @@ def _extract_unique_features(self, program: Dict[str, Any]) -> str: and self.config.include_changes_under_chars and len(changes) < self.config.include_changes_under_chars ): - features.append( - self.template_manager.get_fragment("inspiration_changes_prefix").format( - changes=changes - ) - ) + features.append(self.template_manager.get_fragment("inspiration_changes_prefix").format(changes=changes)) # Analyze metrics for standout characteristics metrics = program.get("metrics", {}) for metric_name, value in metrics.items(): if isinstance(value, (int, float)): if value >= 0.9: - features.append( - f"{self.template_manager.get_fragment('inspiration_metrics_excellent').format(metric_name=metric_name, value=value)}" - ) + features.append(f"{self.template_manager.get_fragment('inspiration_metrics_excellent').format(metric_name=metric_name, value=value)}") elif value <= 0.3: - features.append( - f"{self.template_manager.get_fragment('inspiration_metrics_alternative').format(metric_name=metric_name)}" - ) + features.append(f"{self.template_manager.get_fragment('inspiration_metrics_alternative').format(metric_name=metric_name)}") # Code-based features (simple heuristics) code = program.get("code", "") @@ -596,32 +571,22 @@ def _extract_unique_features(self, program: Dict[str, Any]) -> str: if "numpy" in code_lower or "np." in code_lower: features.append(self.template_manager.get_fragment("inspiration_code_with_numpy")) if "for" in code_lower and "while" in code_lower: - features.append( - self.template_manager.get_fragment("inspiration_code_with_mixed_iteration") - ) + features.append(self.template_manager.get_fragment("inspiration_code_with_mixed_iteration")) if ( self.config.concise_implementation_max_lines and len(code.split("\n")) <= self.config.concise_implementation_max_lines ): - features.append( - self.template_manager.get_fragment("inspiration_code_with_concise_line") - ) + features.append(self.template_manager.get_fragment("inspiration_code_with_concise_line")) elif ( self.config.comprehensive_implementation_min_lines and len(code.split("\n")) >= self.config.comprehensive_implementation_min_lines ): - features.append( - self.template_manager.get_fragment("inspiration_code_with_comprehensive_line") - ) + features.append(self.template_manager.get_fragment("inspiration_code_with_comprehensive_line")) # Default if no specific features found if not features: program_type = self._determine_program_type(program) - features.append( - self.template_manager.get_fragment("inspiration_no_features_postfix").format( - program_type=program_type - ) - ) + features.append(self.template_manager.get_fragment("inspiration_no_features_postfix").format(program_type=program_type)) # Use num_top_programs as limit for features (similar to how we limit programs) feature_limit = self.config.num_top_programs @@ -664,12 +629,7 @@ def _render_artifacts(self, artifacts: Dict[str, Union[str, bytes]]) -> str: sections.append(f"### {key}\n```\n{content}\n```") if sections: - return ( - "## " - + self.template_manager.get_fragment("artifact_title") - + "\n\n" - + "\n\n".join(sections) - ) + return "## " + self.template_manager.get_fragment("artifact_title") + "\n\n" + "\n\n".join(sections) else: return ""