diff --git a/agents/requirements.txt b/agents/requirements.txt index 01069812..3e455585 100644 --- a/agents/requirements.txt +++ b/agents/requirements.txt @@ -1,4 +1,4 @@ -sirji-messages==0.0.30 +sirji-messages==0.0.32 sirji-tools==0.0.16 openai==1.35.7 anthropic==0.29.0 diff --git a/agents/setup.py b/agents/setup.py index 1cf28851..202e7e6d 100644 --- a/agents/setup.py +++ b/agents/setup.py @@ -2,7 +2,7 @@ setup( name='sirji-agents', - version='0.0.45', + version='0.0.47', author='Sirji', description='Orchestrator, Generic Agent, and Research Agent components of the Sirji AI agentic framework.', license='MIT', diff --git a/agents/sirji_agents/__init__.py b/agents/sirji_agents/__init__.py index 881170c0..75a78e49 100644 --- a/agents/sirji_agents/__init__.py +++ b/agents/sirji_agents/__init__.py @@ -1,6 +1,6 @@ from .researcher import ResearchAgent, CleanupFactory from .llm.orchestrator import Orchestrator -from .llm.generic import GenericAgent +from .llm.generic.infer import GenericAgentInfer as GenericAgent __all__ = [ 'ResearchAgent', diff --git a/agents/sirji_agents/llm/generic/__init__.py b/agents/sirji_agents/llm/generic/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agents/sirji_agents/llm/generic/infer.py b/agents/sirji_agents/llm/generic/infer.py new file mode 100644 index 00000000..c300a9fa --- /dev/null +++ b/agents/sirji_agents/llm/generic/infer.py @@ -0,0 +1,86 @@ +from sirji_tools.logger import create_logger +from sirji_messages import message_parse, MessageParsingError, MessageValidationError, ActionEnum, AgentEnum, allowed_response_templates, permissions_dict, ActionEnum +from ..model_providers.factory import LLMProviderFactory +from .system_prompts.factory import SystemPromptsFactory +from ...decorators import retry_on_exception + +class GenericAgentInfer(): + def __init__(self, config, agent_output_folder_index, file_summaries=None): + # Initialize the logger as an instance variable + self.logger = create_logger(f"{config['id']}.log", 'debug') + + self.logger.info(config) + self.logger.info(agent_output_folder_index) + + self.config = config + self.agent_output_folder_index = agent_output_folder_index + self.file_summaries = file_summaries + + def message(self, input_message, history=[]): + conversation = self.__prepare_conversation(input_message, history) + + self.logger.info(f"Incoming: \n{input_message}") + self.logger.info("Calling OpenAI Chat Completions API\n") + + response_message, prompt_tokens, completion_tokens = self.__get_response(conversation) + + return response_message, conversation, prompt_tokens, completion_tokens + + def __prepare_conversation(self, input_message, history): + conversation = [] + + if not history: + conversation.append( + {"role": "system", "content": SystemPromptsFactory.get_system_prompt(self.config, self.agent_output_folder_index)}) + else: + if history[0]['role'] == "system": + history[0]['content'] = SystemPromptsFactory.get_system_prompt(self.config, self.agent_output_folder_index) + conversation = history + + parsed_input_message = message_parse(input_message) + conversation.append({"role": "user", "content": input_message, "parsed_content": parsed_input_message}) + + return conversation + + def __get_response(self, conversation): + retry_llm_count = 0 + response_message = '' + prompt_tokens = 0 + completion_tokens = 0 + + while(True): + response_message, current_prompt_tokens, current_completion_tokens = self.__call_llm(conversation) + + prompt_tokens += current_prompt_tokens + completion_tokens += current_completion_tokens + try: + # Attempt parsing + parsed_response_message = message_parse(response_message) + conversation.append({"role": "assistant", "content": response_message, "parsed_content": parsed_response_message}) + break + except (MessageParsingError, MessageValidationError) as e: + # Handling both MessageParsingError and MessageValidationError similarly + self.logger.info("Error while parsing the message.\n") + retry_llm_count += 1 + if retry_llm_count > 2: + raise e + self.logger.info(f"Requesting LLM to resend the message in correct format.\n") + conversation.append({"role": "assistant", "content": response_message, "parsed_content": {}}) + # Todo: @vaibhav - Change the error message language later. + conversation.append({"role": "user", "content": "Error! Your last response has two action in it and both has been discarded because of the below error:\nError in processing your last response. Your response must conform strictly to one of the allowed Response Templates, as it will be processed programmatically and only these templates are recognized. Your response must be enclosed within '***' at the beginning and end, without any additional text above or below these markers. Not conforming above rules will lead to response processing errors."}) + except Exception as e: + self.logger.info(f"Generic error while parsing message. Error: {e}\n") + raise e + + return response_message, prompt_tokens, completion_tokens + + @retry_on_exception() + def __call_llm(self, conversation): + history = [] + + for message in conversation: + history.append({"role": message['role'], "content": message['content']}) + + model_provider = LLMProviderFactory.get_instance() + + return model_provider.get_response(history, self.logger) \ No newline at end of file diff --git a/agents/sirji_agents/llm/generic/system_prompts/__init__.py b/agents/sirji_agents/llm/generic/system_prompts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/agents/sirji_agents/llm/generic/system_prompts/anthropic.py b/agents/sirji_agents/llm/generic/system_prompts/anthropic.py new file mode 100644 index 00000000..089da0c1 --- /dev/null +++ b/agents/sirji_agents/llm/generic/system_prompts/anthropic.py @@ -0,0 +1,110 @@ +import json +import os +import textwrap + +from sirji_messages import ActionEnum, AgentEnum, allowed_response_templates, permissions_dict, ActionEnum + +class AnthropicSystemPrompt: + def __init__(self, config, agent_output_folder_index): + self.config = config + self.agent_output_folder_index = agent_output_folder_index + pass + + def system_prompt(self): + + initial_intro = textwrap.dedent(f""" + You are an agent named "{self.config['name']}", a component of the Sirji AI agentic framework. Sirji is a framework that enables developers to create and run custom AI agents for their everyday development tasks. A Custom Agent is a modular AI component that performs specific tasks based on predefined pseudocode. + Your Agent ID: {self.config['id']} + Your OS (referred as SIRJI_OS later): {os.name} + + You are an expert having skill: {self.config['skills'][0]['skill']}""") + + instructions = textwrap.dedent(f""" + You must follow these instructions: + 1. Convert all points in your pseudo code into plain English steps with a maximum of 10 words each. Log these steps using the LOG_STEPS action. + 2. After logging the steps, follow your pseudo code step by step to the best of your ability. Following each pseudo code step in the specified order is mandatory. Dont miss to follow any of these steps. + 3. If any step is not applicable or cannot be followed, use the DO_NOTHING action to skip it.""") + + pseudo_code = "\nYour pseudo code which you must follow:\n" + self.config['skills'][0]['pseudo_code'] + + response_specifications = textwrap.dedent(f""" + Your response must adhere rigorously to the following rules, without exception, to avoid critical system failures: + - Conform precisely to one of the Allowed Response Templates, as the system processes only these templates correctly. + - Enclose the entire response within '***' markers at both the beginning and the end, without any additional text outside these markers. + - Respond with only one action at a time.""") + + understanding_the_folders = textwrap.dedent(""" + Terminologies: + 1. Project Folder: + - The Project Folder is your primary directory for accessing all user-specific project files, including code files, documentation, and other relevant resources. + - When initializing Sirji, the SIRJI_USER selects this folder as the primary workspace for the project. You should refer to this folder exclusively for accessing and modifying project-specific files. + + 2. Agent Output Folder: + - The Agent Output Folder is designated for storing the results and data outputs generated by the agents (like you) of Sirji. + - Ensure you do not confuse this folder with the Project Folder; remember, no project source files are stored here. + - This folder is different from the project folder and this ensures that operational data is kept separate from project files. + + 3. Agent Output Index: + - The Agent Output Index is an index file for the Agent Output Folder that keeps track of all files written by agents in that folder along with the a brief description of the file contents. + - The Agent Output Index will look as follows: + {{ + 'agent_id/file_name': {{ + 'description': 'description of the file contents' + 'created_by': 'agent_id' + }} + }}""") + + allowed_response_templates_str = textwrap.dedent(""" + Allowed Response Templates: + Below are all the possible allowed "Response Template" formats for each of the allowed recipients. You must always respond using one of them.""") + + if "sub_agents" in self.config and self.config["sub_agents"]: + for sub_agent in self.config["sub_agents"]: + + allowed_response_templates_str += textwrap.dedent(f""" + Allowed Response Templates to {sub_agent['id']}: + For invoking the {sub_agent['id']}, in a fresh session, use the following response template. Please respond with the following, including the starting and ending '***', with no commentary above or below. + + Response template: + *** + FROM: {{Your Agent ID}} + TO: {sub_agent['id']} + ACTION: INVOKE_AGENT + STEP: "provide the step number here for the ongoing step if any." + SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} + BODY: + {{Purpose of invocation.}} + ***""") + '\n' + + allowed_response_templates_str += textwrap.dedent(f""" + For invoking the {sub_agent['id']}, continuing over the existing session session, use the following response template. Please respond with the following, including the starting and ending '***', with no commentary above or below. + + Response template: + *** + FROM: {{Your Agent ID}} + TO: {sub_agent['id']} + ACTION: INVOKE_AGENT_EXISTING_SESSION + STEP: "provide the step number here for the ongoing step if any." + SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} + BODY: + {{Purpose of invocation.}} + ***""") + '\n' + + allowed_response_templates_str += '\n' + allowed_response_templates(AgentEnum.ANY, AgentEnum.SIRJI_USER, permissions_dict[(AgentEnum.ANY, AgentEnum.SIRJI_USER)]) + '\n' + + action_list = permissions_dict[(AgentEnum.ANY, AgentEnum.EXECUTOR)] + accessible_actions = self.config.get("accessible_actions", []) + if accessible_actions: + for action in accessible_actions: + action_list.add(ActionEnum[action]) + allowed_response_templates_str += '\n' + allowed_response_templates(AgentEnum.ANY, AgentEnum.EXECUTOR, action_list) + '\n' + + allowed_response_templates_str += "For updating in project folder use either FIND_AND_REPLACE, INSERT_ABOVE or INSERT_BELOW actions. Ensure you provide the exact matching string in find from file, with the exact number of lines and proper indentation for insert and replace actions.\n" + allowed_response_templates_str += '\n' + allowed_response_templates(AgentEnum.ANY, AgentEnum.CALLER, permissions_dict[(AgentEnum.ANY, AgentEnum.CALLER)]) + '\n' + + current_agent_output_index = f"Current contents of Agent Output Index:\n{json.dumps(self.agent_output_folder_index, indent=4)}" + + current_project_folder_structure = f"Recursive structure of the project folder:\n{os.environ.get('SIRJI_PROJECT_STRUCTURE')}" + + return f"{initial_intro}\n{instructions}\n{pseudo_code}\n{response_specifications}\n{understanding_the_folders}\n{allowed_response_templates_str}\n\n{current_agent_output_index}\n\n{current_project_folder_structure}".strip() + \ No newline at end of file diff --git a/agents/sirji_agents/llm/generic.py b/agents/sirji_agents/llm/generic/system_prompts/default.py similarity index 62% rename from agents/sirji_agents/llm/generic.py rename to agents/sirji_agents/llm/generic/system_prompts/default.py index b9b749ed..cef56dc3 100644 --- a/agents/sirji_agents/llm/generic.py +++ b/agents/sirji_agents/llm/generic/system_prompts/default.py @@ -1,96 +1,18 @@ -import textwrap -import os import json +import os +import textwrap -# TODO - log file should be dynamically created based on agent ID -from sirji_tools.logger import create_logger -from sirji_messages import message_parse, MessageParsingError, MessageValidationError, ActionEnum, AgentEnum, allowed_response_templates, permissions_dict, ActionEnum -from .model_providers.factory import LLMProviderFactory -from ..decorators import retry_on_exception +from sirji_messages import ActionEnum, AgentEnum, allowed_response_templates, permissions_dict, ActionEnum -class GenericAgent(): - def __init__(self, config, agent_output_folder_index, file_summaries=None): - # Initialize the logger as an instance variable - self.logger = create_logger(f"{config['id']}.log", 'debug') - - self.logger.info(config) - self.logger.info(agent_output_folder_index) - +class DefaultSystemPrompt: + def __init__(self, config, agent_output_folder_index): self.config = config self.agent_output_folder_index = agent_output_folder_index - self.file_summaries = file_summaries - - def message(self, input_message, history=[]): - conversation = self.__prepare_conversation(input_message, history) - - self.logger.info(f"Incoming: \n{input_message}") - self.logger.info("Calling OpenAI Chat Completions API\n") - - response_message, prompt_tokens, completion_tokens = self.__get_response(conversation) - - return response_message, conversation, prompt_tokens, completion_tokens - - def __prepare_conversation(self, input_message, history): - conversation = [] - - if not history: - conversation.append( - {"role": "system", "content": self.system_prompt()}) - else: - if history[0]['role'] == "system": - history[0]['content'] = self.system_prompt() - conversation = history - - parsed_input_message = message_parse(input_message) - conversation.append({"role": "user", "content": input_message, "parsed_content": parsed_input_message}) - - return conversation - - def __get_response(self, conversation): - retry_llm_count = 0 - response_message = '' - prompt_tokens = 0 - completion_tokens = 0 - - while(True): - response_message, current_prompt_tokens, current_completion_tokens = self.__call_llm(conversation) - - prompt_tokens += current_prompt_tokens - completion_tokens += current_completion_tokens - try: - # Attempt parsing - parsed_response_message = message_parse(response_message) - conversation.append({"role": "assistant", "content": response_message, "parsed_content": parsed_response_message}) - break - except (MessageParsingError, MessageValidationError) as e: - # Handling both MessageParsingError and MessageValidationError similarly - self.logger.info("Error while parsing the message.\n") - retry_llm_count += 1 - if retry_llm_count > 2: - raise e - self.logger.info(f"Requesting LLM to resend the message in correct format.\n") - conversation.append({"role": "assistant", "content": response_message, "parsed_content": {}}) - conversation.append({"role": "user", "content": "Error in processing your last response. Your response must conform strictly to one of the allowed Response Templates, as it will be processed programmatically and only these templates are recognized. Your response must be enclosed within '***' at the beginning and end, without any additional text above or below these markers. Not conforming above rules will lead to response processing errors."}) - except Exception as e: - self.logger.info(f"Generic error while parsing message. Error: {e}\n") - raise e - - return response_message, prompt_tokens, completion_tokens - - @retry_on_exception() - def __call_llm(self, conversation): - history = [] - - for message in conversation: - history.append({"role": message['role'], "content": message['content']}) - - model_provider = LLMProviderFactory.get_instance() - - return model_provider.get_response(history, self.logger) + pass def system_prompt(self): initial_intro = textwrap.dedent(f""" - You are an agent named "{self.config['name']}", a component of the Sirji AI agentic framework. + You are an agent named "{self.config['name']}", a component of the Sirji AI agentic framework. Sirji is a framework that enables developers to create and run custom AI agents for their everyday development tasks. A Custom Agent is a modular AI component that performs specific tasks based on predefined pseudocode. Your Agent ID: {self.config['id']} Your OS (referred as SIRJI_OS later): {os.name}""") @@ -126,7 +48,8 @@ def system_prompt(self): - Upon being invoked, identify which of your skills match the requirements of the task. - Execute the sub-tasks associated with each of these matching skills. - Do not respond with two actions in the same response. Respond with one action at a time. - - Always use STORE_IN_AGENT_OUTPUT and READ_AGENT_OUTPUT_FILES to write and read files to and from the agent output folder. + - Always use STORE_IN_AGENT_OUTPUT and READ_AGENT_OUTPUT_FILES to write and read files to and from the agent output folder. + - If any step is not applicable or cannot be followed, use the DO_NOTHING action to skip it. """) formatted_skills = self.__format_skills() @@ -182,13 +105,8 @@ def system_prompt(self): current_agent_output_index = f"Current contents of Agent Output Index:\n{json.dumps(self.agent_output_folder_index, indent=4)}" current_project_folder_structure = f"Recursive structure of the project folder:\n{os.environ.get('SIRJI_PROJECT_STRUCTURE')}" - file_summaries = "" - if self.file_summaries: - file_summaries = 'Here are the concise summaries of the responsibilities and functionalities for each file currently present in the project folder:\n' - file_summaries += f"File Summaries:\n{self.file_summaries}" - - - return f"{initial_intro}\n{response_specifications}{understanding_the_folders}\n{instructions}\n{formatted_skills}\n{allowed_response_templates_str}\n\n{current_agent_output_index}\n\n{current_project_folder_structure}\n\n{file_summaries}".strip() + + return f"{initial_intro}\n{response_specifications}{understanding_the_folders}\n{instructions}\n{formatted_skills}\n{allowed_response_templates_str}\n\n{current_agent_output_index}\n\n{current_project_folder_structure}".strip() def __format_skills(self): output_text = "" diff --git a/agents/sirji_agents/llm/generic/system_prompts/factory.py b/agents/sirji_agents/llm/generic/system_prompts/factory.py new file mode 100644 index 00000000..3243f41d --- /dev/null +++ b/agents/sirji_agents/llm/generic/system_prompts/factory.py @@ -0,0 +1,14 @@ +from .default import DefaultSystemPrompt +from .anthropic import AnthropicSystemPrompt +import os + +class SystemPromptsFactory: + @classmethod + def get_system_prompt(cls, config, agent_output_folder_index): + + provider_name = os.environ.get('SIRJI_MODEL_PROVIDER').lower() + + if provider_name == "anthropic": + return AnthropicSystemPrompt(config, agent_output_folder_index).system_prompt() + else: + return DefaultSystemPrompt(config, agent_output_folder_index).system_prompt() diff --git a/agents/sirji_agents/llm/model_providers/anthropic.py b/agents/sirji_agents/llm/model_providers/anthropic.py index e85b2025..0cecbad2 100644 --- a/agents/sirji_agents/llm/model_providers/anthropic.py +++ b/agents/sirji_agents/llm/model_providers/anthropic.py @@ -8,6 +8,7 @@ def __init__(self, api_key, model): def get_response(self, messages, logger): client = Anthropic( api_key=self.api_key, + default_headers= {"anthropic-beta": "max-tokens-3-5-sonnet-2024-07-15"}, # anthropic-beta header is required to use the 8k max tokens length timeout=60 ) @@ -18,7 +19,7 @@ def get_response(self, messages, logger): messages=messages[1:], model=self.model, temperature=0, - max_tokens=4095, + max_tokens=8192, ) response_message = message.content[0].text diff --git a/messages/setup.py b/messages/setup.py index fff797d8..9b15d119 100644 --- a/messages/setup.py +++ b/messages/setup.py @@ -2,7 +2,7 @@ setup( name='sirji-messages', - version='0.0.30', + version='0.0.32', author='Sirji', description='Sirji messaging protocol implementation to create, validate and parse messages.', license='MIT', diff --git a/messages/sirji_messages/action_enum.py b/messages/sirji_messages/action_enum.py index 123e0596..5097e1b0 100644 --- a/messages/sirji_messages/action_enum.py +++ b/messages/sirji_messages/action_enum.py @@ -34,6 +34,7 @@ class ActionEnum(Enum): SEARCH_FILE_IN_PROJECT = auto() SEARCH_CODE_IN_PROJECT = auto() STORE_IN_SCRATCH_PAD = auto() + DO_NOTHING = auto() STORE_IN_AGENT_OUTPUT = auto() LOG_STEPS = auto() SYNC_CODEBASE = auto() diff --git a/messages/sirji_messages/messages/actions/base.py b/messages/sirji_messages/messages/actions/base.py index e3fd4372..c641dbee 100644 --- a/messages/sirji_messages/messages/actions/base.py +++ b/messages/sirji_messages/messages/actions/base.py @@ -37,7 +37,7 @@ def generate(self, obj): obj["from_agent_id"] = self.from_agent except AttributeError: pass - + return self.template().format(**obj) def template(self): @@ -46,7 +46,7 @@ def template(self): FROM: {{from_agent_id}} TO: {{to_agent_id}} ACTION: {self.action} - STEP: "Provide the step number here for the ongoing step if any." + STEP: {{step}} SUMMARY: {{summary}} BODY: {{body}} ***""") diff --git a/messages/sirji_messages/messages/actions/do_nothing.py b/messages/sirji_messages/messages/actions/do_nothing.py new file mode 100644 index 00000000..6c28fe50 --- /dev/null +++ b/messages/sirji_messages/messages/actions/do_nothing.py @@ -0,0 +1,23 @@ +from sirji_messages import AgentEnum, ActionEnum; +from .base import BaseMessages; + +class DoNothing(BaseMessages): + + def __init__(self): + self.action = ActionEnum.DO_NOTHING.name + self.to_agent = AgentEnum.EXECUTOR.name + + super().__init__() + + def sample(self): + return self.generate({ + "from_agent_id": "{{Your Agent ID}}", + "step": "Provide the step number here for the ongoing step if any.", + "summary": "{{Display a concise summary to the user, describing the action using the present continuous tense.}}", + "body": "{{Provide the reason why the agent is doing nothing.}}"}) + + def description(self): + return "Do Nothing" + + def instructions(self): + return [ "The command is used to do nothing."] diff --git a/messages/sirji_messages/messages/actions/extract_dependencies.py b/messages/sirji_messages/messages/actions/extract_dependencies.py index c9b0a1b4..69b55428 100644 --- a/messages/sirji_messages/messages/actions/extract_dependencies.py +++ b/messages/sirji_messages/messages/actions/extract_dependencies.py @@ -20,7 +20,7 @@ def sample(self): {{Array of file paths}}""")}) def description(self): - return "Extract Dependencies of The Specified Files" + return "Extract files Paths of the dependencies (files which are imported/required) from the given files." def instructions(self): return [ "The file path must be relative to the project root."] diff --git a/messages/sirji_messages/messages/actions/log_steps.py b/messages/sirji_messages/messages/actions/log_steps.py index 39a0fb2d..3cc8bb91 100644 --- a/messages/sirji_messages/messages/actions/log_steps.py +++ b/messages/sirji_messages/messages/actions/log_steps.py @@ -14,7 +14,7 @@ def __init__(self): def sample(self): return self.generate({ "from_agent_id": "{{Your Agent ID}}", - "step": "Provide the step number here for the ongoing step if any.", + "step": "Empty", "summary": "{{Display a concise summary to the user, describing the action using the present continuous tense.}}", "body": textwrap.dedent(""" Steps: {{Array of steps}}""")}) diff --git a/messages/sirji_messages/messages/factory.py b/messages/sirji_messages/messages/factory.py index e23c2284..8dd5cd3c 100644 --- a/messages/sirji_messages/messages/factory.py +++ b/messages/sirji_messages/messages/factory.py @@ -23,6 +23,7 @@ from .actions.insert_above import InsertAbove from .actions.insert_below import InsertBelow from .actions.store_in_scratch_pad import StoreInScratchPad +from .actions.do_nothing import DoNothing from .actions.store_in_agent_output import StoreInAgentOutputMessage from .actions.log_steps import LogSteps from .actions.sync_codebase import SyncCodebase @@ -68,6 +69,7 @@ class MessageFactory(metaclass=MetaMessageFactory): ActionEnum.FETCH_RECIPE: FetchRecipeMessage, ActionEnum.FETCH_RECIPE_INDEX: FetchRecipeIndexMessage, ActionEnum.STORE_IN_SCRATCH_PAD: StoreInScratchPad, + ActionEnum.DO_NOTHING: DoNothing, ActionEnum.LOG_STEPS: LogSteps, ActionEnum.SYNC_CODEBASE: SyncCodebase, ActionEnum.CREATE_ASSISTANT: CreateAssistantMessage diff --git a/messages/sirji_messages/parser.py b/messages/sirji_messages/parser.py index ae6d9e01..249f0832 100644 --- a/messages/sirji_messages/parser.py +++ b/messages/sirji_messages/parser.py @@ -41,7 +41,7 @@ def _validate_message(message): raise MessageValidationError("Message must start and end with ***") message = message[3:-3].strip() lines = message.split("\n") - if len(lines) < 6: + if len(lines) < max(len(message_properties), 2): raise MessageValidationError( "Message does not meet the minimum length requirement") return lines diff --git a/messages/sirji_messages/permissions.py b/messages/sirji_messages/permissions.py index ace8109c..5cc1db59 100644 --- a/messages/sirji_messages/permissions.py +++ b/messages/sirji_messages/permissions.py @@ -17,6 +17,7 @@ ActionEnum.INSERT_BELOW, ActionEnum.EXTRACT_DEPENDENCIES, ActionEnum.STORE_IN_SCRATCH_PAD, + ActionEnum.DO_NOTHING, ActionEnum.LOG_STEPS, ActionEnum.INFER, ActionEnum.CREATE_ASSISTANT, diff --git a/messages/tests/test_base.py b/messages/tests/test_base.py index f92d957b..37223725 100644 --- a/messages/tests/test_base.py +++ b/messages/tests/test_base.py @@ -37,7 +37,7 @@ def test_base_message_generate(): FROM: AGENT_1 TO: AGENT_2 ACTION: TEST_ACTION - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: Test summary BODY: Test body ***""") diff --git a/messages/tests/test_create_project_file.py b/messages/tests/test_create_project_file.py index 867a4757..cc153574 100644 --- a/messages/tests/test_create_project_file.py +++ b/messages/tests/test_create_project_file.py @@ -11,7 +11,7 @@ def test_create_project_file_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: CREATE_PROJECT_FILE - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: File path: {{file path}} diff --git a/messages/tests/test_execute_command.py b/messages/tests/test_execute_command.py index 6bef8ca7..5bcf8777 100644 --- a/messages/tests/test_execute_command.py +++ b/messages/tests/test_execute_command.py @@ -11,7 +11,7 @@ def test_execute_command_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: EXECUTE_COMMAND - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: {{command}} diff --git a/messages/tests/test_extract_dependencies.py b/messages/tests/test_extract_dependencies.py index c5ecca1f..48dce8f7 100644 --- a/messages/tests/test_extract_dependencies.py +++ b/messages/tests/test_extract_dependencies.py @@ -12,7 +12,7 @@ def test_extract_dependencies_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: EXTRACT_DEPENDENCIES - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: {{Array of file paths}} @@ -22,7 +22,7 @@ def test_extract_dependencies_message_sample(): def test_extract_dependencies_message_description(): message = ExtractDependenciesMessage() - assert message.description() == "Extract Dependencies of The Specified Files" + assert message.description() == "Extract files Paths of the dependencies (files which are imported/required) from the given files." def test_extract_dependencies_message_instructions(): message = ExtractDependenciesMessage() diff --git a/messages/tests/test_infer.py b/messages/tests/test_infer.py index cf2f2213..4ca0464c 100644 --- a/messages/tests/test_infer.py +++ b/messages/tests/test_infer.py @@ -12,7 +12,7 @@ def test_infer_message_sample(): FROM: {{Your Agent ID}} TO: RESEARCHER ACTION: INFER - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: {{query}} diff --git a/messages/tests/test_invoke_agent.py b/messages/tests/test_invoke_agent.py index b6850f27..9caca15a 100644 --- a/messages/tests/test_invoke_agent.py +++ b/messages/tests/test_invoke_agent.py @@ -11,7 +11,7 @@ def test_invoke_agent_message_sample(): FROM: ORCHESTRATOR TO: {{To Agent ID}} ACTION: INVOKE_AGENT - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: {{Purpose of invocation}} diff --git a/messages/tests/test_invoke_agent_existing_session.py b/messages/tests/test_invoke_agent_existing_session.py index a5d18aa3..58bfe3c8 100644 --- a/messages/tests/test_invoke_agent_existing_session.py +++ b/messages/tests/test_invoke_agent_existing_session.py @@ -11,7 +11,7 @@ def test_invoke_agent_existing_session_message_sample(): FROM: ORCHESTRATOR TO: {{To Agent ID}} ACTION: INVOKE_AGENT_EXISTING_SESSION - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: {{Purpose of invocation}} diff --git a/messages/tests/test_question.py b/messages/tests/test_question.py index 1430048a..a8eb7f89 100644 --- a/messages/tests/test_question.py +++ b/messages/tests/test_question.py @@ -12,7 +12,7 @@ def test_question_message_sample(): FROM: {{Your Agent ID}} TO: SIRJI_USER ACTION: QUESTION - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: Empty BODY: {{Question}} diff --git a/messages/tests/test_read_agent_output_files.py b/messages/tests/test_read_agent_output_files.py index 0506c9fd..766dc042 100644 --- a/messages/tests/test_read_agent_output_files.py +++ b/messages/tests/test_read_agent_output_files.py @@ -11,7 +11,7 @@ def test_read_agent_output_files_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: READ_AGENT_OUTPUT_FILES - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: File paths: {{Array of file paths}} diff --git a/messages/tests/test_read_agent_output_index.py b/messages/tests/test_read_agent_output_index.py index 2e9f2ecf..40fe18e8 100644 --- a/messages/tests/test_read_agent_output_index.py +++ b/messages/tests/test_read_agent_output_index.py @@ -11,7 +11,7 @@ def test_read_agent_output_index_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: READ_AGENT_OUTPUT_INDEX - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: Empty diff --git a/messages/tests/test_read_project_files.py b/messages/tests/test_read_project_files.py index 21430c0b..db9a7a0d 100644 --- a/messages/tests/test_read_project_files.py +++ b/messages/tests/test_read_project_files.py @@ -12,7 +12,7 @@ def test_read_project_files_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: READ_PROJECT_FILES - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: File paths: {{Array of file paths}} diff --git a/messages/tests/test_response.py b/messages/tests/test_response.py index a515348c..688ec63f 100644 --- a/messages/tests/test_response.py +++ b/messages/tests/test_response.py @@ -11,7 +11,7 @@ def test_response_message_sample(): FROM: {{Agent Id of the agent sending the response}} TO: {{Your Agent ID}} ACTION: RESPONSE - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: Empty BODY: {{Response}} diff --git a/messages/tests/test_run_server.py b/messages/tests/test_run_server.py index 1b119ae4..9f65bb36 100644 --- a/messages/tests/test_run_server.py +++ b/messages/tests/test_run_server.py @@ -11,7 +11,7 @@ def test_run_server_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: RUN_SERVER - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: {{command}} diff --git a/messages/tests/test_search_code_in_project.py b/messages/tests/test_search_code_in_project.py index ba628514..1de498a7 100644 --- a/messages/tests/test_search_code_in_project.py +++ b/messages/tests/test_search_code_in_project.py @@ -13,7 +13,7 @@ def test_search_code_in_project_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: SEARCH_CODE_IN_PROJECT - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: Search Term: {{Search term}} diff --git a/messages/tests/test_search_file_in_project.py b/messages/tests/test_search_file_in_project.py index 45378a74..2ba5b0e3 100644 --- a/messages/tests/test_search_file_in_project.py +++ b/messages/tests/test_search_file_in_project.py @@ -12,7 +12,7 @@ def test_search_file_in_project_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: SEARCH_FILE_IN_PROJECT - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: Search: {{Search term}} diff --git a/messages/tests/test_solution_complete.py b/messages/tests/test_solution_complete.py index a8d8d732..049c63b7 100644 --- a/messages/tests/test_solution_complete.py +++ b/messages/tests/test_solution_complete.py @@ -12,7 +12,7 @@ def test_solution_complete_message_sample(): FROM: {{Your Agent ID}} TO: SIRJI_USER ACTION: SOLUTION_COMPLETE - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: {{Summarize what all was done for getting the solution.}} diff --git a/messages/tests/test_store_in_agent_output_file.py b/messages/tests/test_store_in_agent_output_file.py index afdbc5bc..492cd6f4 100644 --- a/messages/tests/test_store_in_agent_output_file.py +++ b/messages/tests/test_store_in_agent_output_file.py @@ -11,7 +11,7 @@ def test_store_in_agent_output_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: STORE_IN_AGENT_OUTPUT - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: File path: {{file path}} diff --git a/messages/tests/test_store_in_scratch_pad.py b/messages/tests/test_store_in_scratch_pad.py index 0324e7b3..3edc1389 100644 --- a/messages/tests/test_store_in_scratch_pad.py +++ b/messages/tests/test_store_in_scratch_pad.py @@ -11,7 +11,7 @@ def test_store_in_scratch_pad_message_sample(): FROM: {{Your Agent ID}} TO: EXECUTOR ACTION: STORE_IN_SCRATCH_PAD - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: {{notes}} ***""") diff --git a/messages/tests/test_train_using_search_term.py b/messages/tests/test_train_using_search_term.py index 2cd4e576..40d84fc6 100644 --- a/messages/tests/test_train_using_search_term.py +++ b/messages/tests/test_train_using_search_term.py @@ -12,7 +12,7 @@ def test_train_using_search_term_message_sample(): FROM: {{Your Agent ID}} TO: RESEARCHER ACTION: TRAIN_USING_SEARCH_TERM - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: Term: {{search term}} diff --git a/messages/tests/test_train_using_url.py b/messages/tests/test_train_using_url.py index 3b906e08..3757a748 100644 --- a/messages/tests/test_train_using_url.py +++ b/messages/tests/test_train_using_url.py @@ -12,7 +12,7 @@ def test_train_using_url_message_sample(): FROM: {{Your Agent ID}} TO: RESEARCHER ACTION: TRAIN_USING_URL - STEP: "Provide the step number here for the ongoing step if any." + STEP: Provide the step number here for the ongoing step if any. SUMMARY: {{Display a concise summary to the user, describing the action using the present continuous tense.}} BODY: URL: {{url}} diff --git a/sirji/vscode-extension/CHANGELOG.md b/sirji/vscode-extension/CHANGELOG.md index 817916ad..0d82dd73 100644 --- a/sirji/vscode-extension/CHANGELOG.md +++ b/sirji/vscode-extension/CHANGELOG.md @@ -8,7 +8,9 @@ - Default agents are now read from the `agents` folder within the `default` folder in the codebase if not present in the `agents` folder of Sirji Studio. - Deprecated the keys `rules` and `definitions` from agent YAML. - Introduced the code review agent. - +- Implemented Factory Design Pattern for system prompt to have model provider specific details. +- Ability to nudge the agent programmatically to bring it on track in case it skips over some pseudo code steps. +- New messaging protocol action DO_NOTHING introduced to make intentional skipping of steps possible. ## 0.0.29 diff --git a/sirji/vscode-extension/package.json b/sirji/vscode-extension/package.json index 9cffeb0d..73650cca 100644 --- a/sirji/vscode-extension/package.json +++ b/sirji/vscode-extension/package.json @@ -2,7 +2,7 @@ "name": "sirji", "displayName": "Sirji", "description": "Sirji is an AI Software Development Agent.", - "version": "0.0.30-beta.4", + "version": "0.0.30", "publisher": "TrueSparrow", "icon": "out/assets/sirji.png", "repository": { diff --git a/sirji/vscode-extension/src/defaults/agents/CODE_REVIEWER.yml b/sirji/vscode-extension/src/defaults/agents/CODE_REVIEWER.yml index db8bd21c..d64ceff6 100644 --- a/sirji/vscode-extension/src/defaults/agents/CODE_REVIEWER.yml +++ b/sirji/vscode-extension/src/defaults/agents/CODE_REVIEWER.yml @@ -1,28 +1,41 @@ id: CODE_REVIEWER name: Code Review Agent llm: - provider: openai - model: gpt-4o + provider: anthropic + model: claude-3-5-sonnet-20240620 skills: - - skill: Perform a comprehensive code review by gathering requirements, identifying changes with git diff, reading relevant project files, clarifying ambiguities, comparing changes against requirements, and documenting your findings in a detailed markdown report. + - skill: Evaluate the code changes by analyzing the diff files, reference files, and their dependencies. Identify issues, missing requirements, and incorrect code. Create a detailed review todo's document that includes actionable items grouped by file path. pseudo_code: | - 1. Ask SIRJI_USER to provide the requirements or the expected behavior of the code changes. + 1. Ask the SIRJI_USER to provide the requirements or the expected behavior of the code changes. 2. Run `git add -N .` 3. Run `git diff` in the project folder to identify the changes made. - 4. Run `git reset` - 5. Read the project files involved in the git diff. - 6. If there are any ambiguities in the requirement, clarify them by asking questions to SIRJI_USER. - 7. Read those files which are mentioned in the requirements provided by SIRJI_USER, if any. - 8. Identify the files which are required in the read files and read them too. - 9. Construct and store a detailed, standalone code review doc by following these rules: - - The code review doc must be in markdown format. - - The first section should include your understanding of the requirement. - - Compare the changes made in these files against the provided requirements to ensure they meet the expectations. Next section should be for listing down the code review comments for the files read by you, following these rules: - - Mention any security or optimization issues found in the read files. - - Mention any discrepancies between the code changes and the requirements. - - Mention if methods are incorrectly used. For example, wrong params sent, method not defined, etc. - - Ensure that you mention review comments even if the code commenting is not done as per the coding conventions of the repository files. - - Mention any additional code review comments. - - If you do not have any comment for a particular file, just mention as "No review comments". - - Store the code review doc in the Agent Output Folder. - 10. Respond back to the agent who invoked you, detailing your actions in the BODY. + 4. Run `git reset`. + 5. Using the project folder structure, determine the file paths for the following files and read them: + - The project files involved in the git diff. Let's call these files "diff files". + - The files mentioned in the requirements provided by the SIRJI_USER, if any. Let's call these files "reference files". + 6. EXTRACT_DEPENDENCIES file paths for the diff files and reference files. Let's call these dependency file paths as "first degree dependency file paths". + 7. Read the first degree dependency file paths to understand their usage in the diff files and reference files. + 8. Declare an array to store the review todo's. Loop over the diff files and reference files, and for each file in each iteration go line by line, evaluate the following questions. If any todo's found after review, add it to the review todo's array: + - Are there any discrepancies or typos in what is defined and its usage in the current file? Pay close attention to variables, functions, classes, and their usage. + - Is there any import/require statement missing in the current file? + - For every file required in the file, check if the file exists as per the project folder structure. + - Is there any security vulnerability in the current file? + - Is there any optimization that can be made in the current file? + - Is there any discrepancy between the code changes and the requirements? + - Go through the requirements provided by the SIRJI_USER and the necessary documentation of the requirements to ensure that the code changes are in line with the requirements. Example go over sequence diagrams, flowcharts, and other documentation line by line if available. + - Check if the responses from the external libs are used properly as per their response structure. + - Are there any additional changes needed? + - Are the comments in the code clear and helpful? + - Are there any obsolete or commented out code that should be removed? + - Is the code adhering to the project's coding standards and conventions? + - Is the code modular and reusable? + - Does the code have proper error handling and logging? + - Is the documentation updated to reflect the changes made? + - Are there any potential performance bottlenecks introduced by the changes? + - Are there any changes which will introduce the runtime errors? + 9. Using the review todo's array, construct and store a detailed, standalone review todo's document by following these rules: + - The document must be in markdown format. + - The first section should include your understanding of the requirements. + - The next section should list all the review todo's, grouped by file path. Do not include any other comments or information aside from the review todo's. It should contain only the review todo's. Each review todo should be actionable and clear. + 10. Store the review todo's document in the Agent Output folder. + 11. Respond back to the agent who invoked you, detailing your actions in the BODY. \ No newline at end of file diff --git a/sirji/vscode-extension/src/py_scripts/requirements-windows.txt b/sirji/vscode-extension/src/py_scripts/requirements-windows.txt index 3f2b2856..9d466f36 100644 --- a/sirji/vscode-extension/src/py_scripts/requirements-windows.txt +++ b/sirji/vscode-extension/src/py_scripts/requirements-windows.txt @@ -1,4 +1,4 @@ urllib3==2.2.1 pyyaml==6.0.1 -sirji-messages==0.0.30 -sirji-agents == 0.0.45 +sirji-messages==0.0.32 +sirji-agents == 0.0.47 diff --git a/sirji/vscode-extension/src/py_scripts/requirements.txt b/sirji/vscode-extension/src/py_scripts/requirements.txt index f7974aa1..d4ba2a57 100644 --- a/sirji/vscode-extension/src/py_scripts/requirements.txt +++ b/sirji/vscode-extension/src/py_scripts/requirements.txt @@ -1,4 +1,4 @@ urllib3==1.25.11 pyyaml==6.0.1 -sirji-messages==0.0.30 -sirji-agents == 0.0.45 +sirji-messages==0.0.32 +sirji-agents == 0.0.47 diff --git a/sirji/vscode-extension/src/utils/constants.ts b/sirji/vscode-extension/src/utils/constants.ts index 264f7880..f82b2e33 100644 --- a/sirji/vscode-extension/src/utils/constants.ts +++ b/sirji/vscode-extension/src/utils/constants.ts @@ -77,6 +77,7 @@ export const ACTION_ENUM = { EXTRACT_DEPENDENCIES: 'EXTRACT_DEPENDENCIES', SEARCH_CODE_IN_PROJECT: 'SEARCH_CODE_IN_PROJECT', STORE_IN_SCRATCH_PAD: 'STORE_IN_SCRATCH_PAD', + DO_NOTHING: 'DO_NOTHING', LOG_STEPS: 'LOG_STEPS' }; diff --git a/sirji/vscode-extension/src/utils/executor/executor.ts b/sirji/vscode-extension/src/utils/executor/executor.ts index 5ccdf73e..209519f6 100644 --- a/sirji/vscode-extension/src/utils/executor/executor.ts +++ b/sirji/vscode-extension/src/utils/executor/executor.ts @@ -85,6 +85,8 @@ export class Executor { return await searchCodeInProject(oThis.parsedMessage.BODY, oThis.projectRootPath); case ACTION_ENUM.STORE_IN_SCRATCH_PAD: return 'Done'; + case ACTION_ENUM.DO_NOTHING: + return 'OK'; case ACTION_ENUM.LOG_STEPS: console.log('LOG_STEPS', oThis.parsedMessage.BODY, oThis.agentStack); return await oThis.stepManager.createStepsFile(oThis.agentStack, oThis.parsedMessage.BODY); diff --git a/sirji/vscode-extension/src/utils/facilitator.ts b/sirji/vscode-extension/src/utils/facilitator.ts index f1a12655..6facd765 100644 --- a/sirji/vscode-extension/src/utils/facilitator.ts +++ b/sirji/vscode-extension/src/utils/facilitator.ts @@ -217,6 +217,19 @@ export class Facilitator { const oThis = this; oThis.envVars = await oThis.secretManager?.retrieveSecret(Constants.ENV_VARS_KEY); + + return oThis.envVars; + } + + private async readSecretKeys() { + const oThis = this; + + const secretKeys = await oThis.retrieveSecret(); + + this.chatPanel?.webview.postMessage({ + type: 'showSecretKeys', + content: secretKeys + }); } private async setSecretEnvVars(data: any) { @@ -416,62 +429,108 @@ export class Facilitator { break; + case 'requestSecretKeys': + await oThis.readSecretKeys(); + break; + default: vscode.window.showErrorMessage(`Unknown message received from chat panel: ${message}`); } } - private processMessages = () => { const oThis = this; try { - console.log('Processing async messages...'); - if (!fs.existsSync(oThis.asyncMessagesFolder)) { - return; - } - - const files = fs.readdirSync(oThis.asyncMessagesFolder); - - const jsonFiles = files - .filter(file => file.startsWith('notification_') && file.endsWith('.json')) - .sort((a, b) => { - const timestampA = a.match(/notification_(.*)\.json/)?.[1]; - const timestampB = b.match(/notification_(.*)\.json/)?.[1]; - return timestampA && timestampB ? timestampA.localeCompare(timestampB) : 0; - }); + console.log('Processing async messages...'); + if (!fs.existsSync(oThis.asyncMessagesFolder)) { + return; + } - for (const file of jsonFiles) { - const filePath = path.join(oThis.asyncMessagesFolder, file); - try { - const content = fs.readFileSync(filePath, 'utf-8'); - const message = JSON.parse(content); + const files = fs.readdirSync(oThis.asyncMessagesFolder); - console.log(`Message: ${message.content.message}`); - oThis.chatPanel?.webview.postMessage({ - type: 'botMessage', - content: message.content - }); + const jsonFiles = files + .filter((file) => file.startsWith('notification_') && file.endsWith('.json')) + .sort((a, b) => { + const timestampA = a.match(/notification_(.*)\.json/)?.[1]; + const timestampB = b.match(/notification_(.*)\.json/)?.[1]; + return timestampA && timestampB ? timestampA.localeCompare(timestampB) : 0; + }); - fs.unlinkSync(filePath); - } catch (err) { - console.error(`Error processing file ${file}:`, err); - } + for (const file of jsonFiles) { + const filePath = path.join(oThis.asyncMessagesFolder, file); + try { + const content = fs.readFileSync(filePath, 'utf-8'); + const message = JSON.parse(content); + + console.log(`Message: ${message.content.message}`); + oThis.chatPanel?.webview.postMessage({ + type: 'botMessage', + content: message.content + }); + + fs.unlinkSync(filePath); + } catch (err) { + console.error(`Error processing file ${file}:`, err); } + } } catch (err) { - console.error('Error reading async_messages folder:', err); + console.error('Error reading async_messages folder:', err); } -}; + }; async initFacilitation(rawMessage: string, parsedMessage: any) { const oThis = this; let keepFacilitating: Boolean = true; + oThis.inputFilePath = path.join(oThis.sirjiRunFolderPath, 'input.txt'); setInterval(oThis.processMessages, 5000); while (keepFacilitating) { oThis.displayParsedMessageSummaryToChatPanel(parsedMessage); - oThis.updateSteps(parsedMessage); + // Todo: Do not call updateSteps if action == 'LOG_STEPS' + let updateStepsRes = oThis.updateSteps(parsedMessage); + + console.log('parsedMessage +------+', parsedMessage); + + console.log('updateStepsRes------', updateStepsRes); + + if (updateStepsRes && Object.keys(updateStepsRes).length > 0 && updateStepsRes?.shouldDiscard) { + if (updateStepsRes?.isError) { + console.log('updateStepsRes------', updateStepsRes); + + let newParsedMessage = { + FROM: parsedMessage.TO, + TO: parsedMessage.FROM, + ACTION: ACTION_ENUM.RESPONSE, + STEP: 'Empty', + SUMMARY: 'Empty', + BODY: '\n' + updateStepsRes?.errorMessage + }; + + let newRawMessage = `*** + FROM: ${newParsedMessage.FROM} + TO: ${newParsedMessage.TO} + ACTION: ${newParsedMessage.ACTION} + STEP: ${newParsedMessage.STEP}, + SUMMARY: ${newParsedMessage.SUMMARY} + BODY: \n${newParsedMessage.BODY} + ***`; + + console.log('newRawMessage------', newRawMessage); + console.log('newParsedMessage------', newParsedMessage); + + rawMessage = newRawMessage; + parsedMessage = newParsedMessage; + + console.log('oThis.inputFilePath', oThis.inputFilePath); + + oThis.writeToFile(oThis.inputFilePath, newRawMessage); + console.log('Continuing with the next message...'); + continue; + } + } + oThis.lastMessageFrom = parsedMessage?.FROM; console.log('rawMessage-------', rawMessage); console.log('parsedMessage------', parsedMessage); @@ -791,26 +850,26 @@ export class Facilitator { }); } - private updateSteps(parsedMessage: any) { + private updateSteps(parsedMessage: any): any { const oThis = this; let agent_callstack = oThis.stackManager.getStack(); let stepNumber = parsedMessage.STEP; if (!stepNumber || stepNumber === undefined || stepNumber === null) { - return; + return {}; } stepNumber = Number(stepNumber.replace(/\D/g, '')); if (!stepNumber || isNaN(stepNumber)) { - return; + return {}; } - this.stepsManager?.updateStepStatus(agent_callstack, stepNumber); - setTimeout(() => { oThis.requestSteps(); }, 2000); + + return this.stepsManager?.updateStepStatus(agent_callstack, stepNumber); } private toCoderRelayToChatPanel(parsedMessage: any) { diff --git a/sirji/vscode-extension/src/utils/step_manager.ts b/sirji/vscode-extension/src/utils/step_manager.ts index a81b8ed2..8701eb39 100644 --- a/sirji/vscode-extension/src/utils/step_manager.ts +++ b/sirji/vscode-extension/src/utils/step_manager.ts @@ -1,6 +1,18 @@ import * as fs from 'fs'; import path from 'path'; + +interface Step { + status: string; + [key: string]: any; +} + +interface ValidationResponse { + isError: boolean; + shouldDiscard: boolean; + errorMessage: string; +} + export class StepManager { private filePath: string; @@ -86,46 +98,98 @@ export class StepManager { } } - public updateStepStatus(fileName: string, stepNumber: number): string | object { + // Method to validate the step number against the current in-progress step + private validateStepNumber(stepNumber: number, steps: Step[]): ValidationResponse { + // Find the index of the step that is currently in-progress + console.log('Starting validation:', stepNumber, steps); + let inProgressStepNumber = -1; + for (let i = 0; i < steps.length; i++) { + if (steps[i].status === 'in-progress') { + inProgressStepNumber = i + 1; + break; + } + } + + console.log('inProgressStepIndex:', inProgressStepNumber); + console.log('stepNumber:', stepNumber); + + // Check if the new step number is skipping more than one step from the in-progress step + if (inProgressStepNumber !== -1 && stepNumber - inProgressStepNumber > 1 ) { + return { + isError: true, + shouldDiscard: true, + errorMessage: `Error: You skipped some of the pseudo code steps. The highest executed step number as per my record is ${inProgressStepNumber}. Please make sure you do not skip any steps. I am discarding your last message.` + }; + } + + // No validation errors, return successful response + return { + isError: false, + shouldDiscard: false, + errorMessage: '' + }; + } + + // Method to update the status of the steps in the specified JSON file + public updateStepStatus(fileName: string, stepNumber: number): ValidationResponse { + const oThis = this; + console.log('Updating step status:', fileName, stepNumber); + + const completeFileName = fileName + '.json'; try { - const filePath = path.join(this.filePath, fileName + '.json'); + const filePath = path.join(oThis.filePath, completeFileName); + + // Check if the file exists if (!fs.existsSync(filePath)) { - return 'Error: File does not exist.'; + return { isError: true, shouldDiscard: false, errorMessage: 'Error: File does not exist.' }; } + // Read the file content and parse it as JSON const fileData = fs.readFileSync(filePath, 'utf8'); - const parsedData = JSON.parse(fileData); + const parsedData: { [key: string]: Step[] } = JSON.parse(fileData); - if (!parsedData || !parsedData[fileName + '.json']) { - return 'Error: Invalid file format.'; + // Validate the structure of the parsed data + if (!parsedData || !parsedData[completeFileName]) { + return { isError: true, shouldDiscard: false, errorMessage: 'Error: Invalid file format.' }; } - const steps = parsedData[fileName + '.json']; + const steps = parsedData[completeFileName]; + // Validate the provided step number if (stepNumber <= 0 || stepNumber > steps.length) { - return 'Error: Invalid step number.'; + return { isError: true, shouldDiscard: false, errorMessage: 'Error: Invalid step number.' }; } + // Validate the step number against the current in-progress step + console.log('Validating step number:', stepNumber, steps); + const validationResponse = oThis.validateStepNumber(stepNumber, steps); + + console.log('Validation response:', validationResponse); + if (validationResponse.isError) { + return validationResponse; + } + + // Update the status of each step based on the provided step number for (let i = 0; i < steps.length; i++) { const stepObj = steps[i]; - const stepKey = Object.keys(stepObj)[0]; if (i < stepNumber - 1) { - stepObj.status = 'completed'; + stepObj.status = 'completed'; } else if (i === stepNumber - 1) { - stepObj.status = 'in-progress'; + stepObj.status = 'in-progress'; } else { - stepObj.status = ''; + stepObj.status = ''; } } + // Write the updated steps back to the file fs.writeFileSync(filePath, JSON.stringify(parsedData, null, 4), 'utf8'); - return 'Done'; + return { isError: false, shouldDiscard: false, errorMessage: 'Done' }; } catch (error) { console.error('Error updating the steps file:', error); - return error || 'Error: An unexpected error occurred.'; + return { isError: true, shouldDiscard: false, errorMessage: `Error: An unexpected error occurred. ${error}` }; } } - + public updateAllStepsToCompleted(fileName: string): any { try { const filePath = path.join(this.filePath, fileName + '.json'); diff --git a/sirji/vscode-extension/src/views/chat/chat.css b/sirji/vscode-extension/src/views/chat/chat.css index a514f107..185ed72c 100644 --- a/sirji/vscode-extension/src/views/chat/chat.css +++ b/sirji/vscode-extension/src/views/chat/chat.css @@ -982,3 +982,7 @@ label { transform: rotate(360deg); } } + +.hidden{ + display: none !important; +} \ No newline at end of file diff --git a/sirji/vscode-extension/src/views/chat/chat.html b/sirji/vscode-extension/src/views/chat/chat.html index cc302045..55ad26a3 100644 --- a/sirji/vscode-extension/src/views/chat/chat.html +++ b/sirji/vscode-extension/src/views/chat/chat.html @@ -171,17 +171,20 @@