From 494189ba1676220341cf7a2443055c64109c8a9e Mon Sep 17 00:00:00 2001 From: Michael Panchenko Date: Thu, 22 May 2025 23:00:15 +0200 Subject: [PATCH] Towards finalization of mode and context handling, WIP --- config/prompt_templates/system_prompt.yml | 5 +- scripts/gen_prompt_factory.py | 4 +- src/interprompt/promt_template.py | 5 +- src/serena/agent.py | 59 ++++++++----------- .../generated/generated_prompt_factory.py | 35 +++++++++++ src/serena/prompt_factory.py | 33 ++--------- 6 files changed, 73 insertions(+), 68 deletions(-) create mode 100644 src/serena/generated/generated_prompt_factory.py diff --git a/config/prompt_templates/system_prompt.yml b/config/prompt_templates/system_prompt.yml index 494471a1..f6d1f856 100644 --- a/config/prompt_templates/system_prompt.yml +++ b/config/prompt_templates/system_prompt.yml @@ -8,7 +8,6 @@ prompts: files containing general information about the codebase. You operate in a frugal and intelligent manner, always keeping in mind to not read or generate content that is not needed for the task at hand. - {context_system_prompt} + {{ context_system_prompt }} - {mode_system_prompt} - \ No newline at end of file + {{ mode_system_prompt }} diff --git a/scripts/gen_prompt_factory.py b/scripts/gen_prompt_factory.py index c4e4f696..4b1354fd 100644 --- a/scripts/gen_prompt_factory.py +++ b/scripts/gen_prompt_factory.py @@ -1,5 +1,5 @@ """ -Autogenerates the `prompt_factory.py` module +Autogenerates the `prompt_factory.py` module """ from pathlib import Path @@ -15,7 +15,7 @@ log = logging.getLogger(__name__) def main(): autogenerate_prompt_factory_module( prompts_dir=PROMPT_TEMPLATES_DIR, - target_module_path=str(Path(REPO_ROOT) / "src" / "serena" / "prompt_factory.py"), + target_module_path=str(Path(REPO_ROOT) / "src" / "serena" / "generated" / "generated_prompt_factory.py"), ) diff --git a/src/interprompt/promt_template.py b/src/interprompt/promt_template.py index 2cdb43ee..86344289 100644 --- a/src/interprompt/promt_template.py +++ b/src/interprompt/promt_template.py @@ -357,11 +357,12 @@ def autogenerate_prompt_factory_module(prompts_dir: str, target_module_path: str :param target_module_path: the path to the target module file. Important: The module will be overwritten! """ generated_code = """ -# NOTE: This class is auto-generated from interprompt.autogenerate_prompt_factory_module, do not edit manually! # ruff: noqa # black: skip # mypy: ignore-errors +# NOTE: This module is auto-generated from interprompt.autogenerate_prompt_factory_module, do not edit manually! + from interprompt.promt_template import PromptFactoryBase, PromptList from typing import Any @@ -390,7 +391,7 @@ class PromptFactory(PromptFactoryBase): def get_list_{prompt_list_name}(self) -> PromptList: return self._get_prompt_list('{prompt_list_name}') """ - + os.makedirs(os.path.dirname(target_module_path), exist_ok=True) with open(target_module_path, "w", encoding="utf-8") as f: f.write(generated_code) log.info(f"Prompt factory generated successfully in {target_module_path}") diff --git a/src/serena/agent.py b/src/serena/agent.py index 34cbe1d4..87f25851 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -29,7 +29,7 @@ from multilspy.multilspy_config import Language, MultilspyConfig from multilspy.multilspy_logger import MultilspyLogger from multilspy.multilspy_types import SymbolKind from serena import serena_root_path, serena_version -from serena.prompt_factory import PromptFactory +from serena.prompt_factory import PromptFactory, SerenaPromptFactory from serena.symbol import SymbolLocation, SymbolManager from serena.text_utils import search_files from serena.util.class_decorators import singleton @@ -225,7 +225,7 @@ class SerenaAgent: log.info("Available projects: {}".format(", ".join(self.serena_config.project_names))) # Initialize the prompt factory - self.prompt_factory = PromptFactory() + self.prompt_factory = SerenaPromptFactory() self._project_activation_callback = project_activation_callback # Load context and mode configuration @@ -311,20 +311,13 @@ class SerenaAgent: :param context: Name or path of the context to use """ - try: - context_config = self.config_loader.get_context(context) - self.current_context = context_config + context_config = self.config_loader.get_context(context) + self.current_context = context_config - # Update the prompt factory with the new context - self.prompt_factory.set_context(context_config.system_prompt_extension) + # Update tool configurations + self._update_active_tools() - # Update tool configurations - self._update_active_tools() - - log.info(f"Set context to '{context_config.name}': {context_config.description}") - except Exception as e: - log.error(f"Failed to set context '{context}': {e}") - raise + log.info(f"Set context to '{context_config.name}': {context_config.description}") def set_modes(self, modes: list[str]) -> None: """ @@ -332,29 +325,29 @@ class SerenaAgent: :param modes: List of mode names or paths to use """ - try: - # Check for tool activation conflicts between modes - mode_configs = [self.config_loader.get_mode(mode) for mode in modes] + mode_configs = [self.config_loader.get_mode(mode) for mode in modes] + self._check_mode_conflicts(mode_configs) + self.current_modes = mode_configs + self._update_active_tools() - # Check for conflicts in tool exclusions between modes - self._check_mode_conflicts(mode_configs) + log.info(f"Set modes to {[mode.name for mode in mode_configs]}") - self.current_modes = mode_configs + def _get_modes_system_prompt(self) -> str: + return "\n".join([mode.system_prompt_extension for mode in self.current_modes]) - # Update the prompt factory with the new modes - mode_extensions = [mode.system_prompt_extension for mode in mode_configs] - self.prompt_factory.set_modes(mode_extensions) + def _get_context_system_prompt(self) -> str: + return self.current_context.system_prompt_extension - # Update tool configurations - self._update_active_tools() + def create_system_prompt(self) -> str: + context_system_prompt = self._get_context_system_prompt() + mode_system_prompt = self._get_modes_system_prompt() + return self.prompt_factory.create_system_prompt( + context_system_prompt=context_system_prompt, + mode_system_prompt=mode_system_prompt, + ) - mode_names = [mode.name for mode in mode_configs] - log.info(f"Set modes to {mode_names}") - except Exception as e: - log.error(f"Failed to set modes {modes}: {e}") - raise - - def _check_mode_conflicts(self, mode_configs: list["ConfigData"]) -> None: + @staticmethod + def _check_mode_conflicts(mode_configs: list["ConfigData"]) -> None: """ Check for conflicts in tool exclusions between modes. @@ -1549,7 +1542,7 @@ class InitialInstructionsTool(Tool): Get the initial instructions for the current coding project. You should always call this tool before starting to work (including using any other tool) on any programming task! """ - return self.agent.prompt_factory.create_system_prompt() + return self.agent.create_system_prompt() def iter_tool_classes(same_module_only: bool = True) -> Generator[type[Tool], None, None]: diff --git a/src/serena/generated/generated_prompt_factory.py b/src/serena/generated/generated_prompt_factory.py new file mode 100644 index 00000000..7f177118 --- /dev/null +++ b/src/serena/generated/generated_prompt_factory.py @@ -0,0 +1,35 @@ +# ruff: noqa +# black: skip +# mypy: ignore-errors + +# NOTE: This module is auto-generated from interprompt.autogenerate_prompt_factory_module, do not edit manually! + +from interprompt.promt_template import PromptFactoryBase, PromptList +from typing import Any + + +class PromptFactory(PromptFactoryBase): + """ + A class for retrieving and rendering prompt templates and prompt lists. + """ + + def create_onboarding_prompt(self, *, system: Any) -> str: + return self._render_prompt("onboarding_prompt", system=system) + + def create_think_about_collected_information(self) -> str: + return self._render_prompt("think_about_collected_information") + + def create_think_about_task_adherence(self) -> str: + return self._render_prompt("think_about_task_adherence") + + def create_think_about_whether_you_are_done(self) -> str: + return self._render_prompt("think_about_whether_you_are_done") + + def create_summarize_changes(self) -> str: + return self._render_prompt("summarize_changes") + + def create_prepare_for_new_conversation(self) -> str: + return self._render_prompt("prepare_for_new_conversation") + + def create_system_prompt(self, *, context_system_prompt: Any, mode_system_prompt: Any) -> str: + return self._render_prompt("system_prompt", context_system_prompt=context_system_prompt, mode_system_prompt=mode_system_prompt) diff --git a/src/serena/prompt_factory.py b/src/serena/prompt_factory.py index 504cd6d1..a14d34ba 100644 --- a/src/serena/prompt_factory.py +++ b/src/serena/prompt_factory.py @@ -1,34 +1,11 @@ +from serena.constants import PROMPT_TEMPLATES_DIR +from serena.generated.generated_prompt_factory import PromptFactory -# NOTE: This class is auto-generated from interprompt.autogenerate_prompt_factory_module, do not edit manually! -# ruff: noqa -# black: skip -# mypy: ignore-errors -from interprompt.promt_template import PromptFactoryBase, PromptList -from typing import Any - -class PromptFactory(PromptFactoryBase): +class SerenaPromptFactory(PromptFactory): """ A class for retrieving and rendering prompt templates and prompt lists. """ - def create_onboarding_prompt(self, *, system: Any) -> str: - return self._render_prompt("onboarding_prompt", system=system) - - def create_think_about_collected_information(self) -> str: - return self._render_prompt("think_about_collected_information") - - def create_think_about_task_adherence(self) -> str: - return self._render_prompt("think_about_task_adherence") - - def create_think_about_whether_you_are_done(self) -> str: - return self._render_prompt("think_about_whether_you_are_done") - - def create_summarize_changes(self) -> str: - return self._render_prompt("summarize_changes") - - def create_prepare_for_new_conversation(self) -> str: - return self._render_prompt("prepare_for_new_conversation") - - def create_system_prompt(self) -> str: - return self._render_prompt("system_prompt") + def __init__(self): + super().__init__(PROMPT_TEMPLATES_DIR)