From 7576bbf98f176684add06c7f338f8d6009ad34f1 Mon Sep 17 00:00:00 2001 From: Ayo Ayibiowu Date: Tue, 10 Dec 2024 15:20:33 +0100 Subject: [PATCH] initial complete poc for mergebot --- examples/agents/code_analysis_crew.yaml | 30 +++ .../agents/complexity_assessment_crew.yaml | 32 +++ examples/agents/impact_evaluator_crew.yaml | 54 +++++ examples/agents/mr_processing_crew.yaml | 34 +-- examples/agents/mr_publication_crew.yaml | 72 +++++++ examples/agents/risk_assessment_crew.yaml | 33 +++ examples/agents/test_coverage_crew.yaml | 34 +++ .../mr_decision_execution_crew.yaml | 1 - mergebot_flow.html | 202 ++++++++++++++++++ poetry.lock | 27 ++- pyproject.toml | 2 +- sage/flows/mergebot.py | 148 +++++++++++++ sage/tools/gitlab_tool.py | 131 +++++++++--- sage/validators/crew_ai.py | 10 +- 14 files changed, 745 insertions(+), 65 deletions(-) create mode 100644 examples/agents/code_analysis_crew.yaml create mode 100644 examples/agents/complexity_assessment_crew.yaml create mode 100644 examples/agents/impact_evaluator_crew.yaml create mode 100644 examples/agents/mr_publication_crew.yaml create mode 100644 examples/agents/risk_assessment_crew.yaml create mode 100644 examples/agents/test_coverage_crew.yaml create mode 100644 mergebot_flow.html create mode 100644 sage/flows/mergebot.py diff --git a/examples/agents/code_analysis_crew.yaml b/examples/agents/code_analysis_crew.yaml new file mode 100644 index 0000000..2417ce9 --- /dev/null +++ b/examples/agents/code_analysis_crew.yaml @@ -0,0 +1,30 @@ +name: Code Analysis Assessment Crew +cache: True +planning: True +planning_llm: azure/gpt4-128k +process: sequential +memory: False + +agents: + - role: Code Analysis Agent + goal: | + Analyze the code changes in the Merge Request (MR), regardless of programming language or project type. + Assess the significance, potential risks, and impact of these changes on the overall codebase. Assign an impact score based on factors such as complexity, criticality of modified components, and potential for introducing bugs or affecting existing functionality, on a scale from 0 to 10. + backstory: | + A versatile code analyst with deep understanding of software development across various languages and domains. Capable of interpreting code changes in any language, identifying critical modifications, and evaluating their implications for the project. This agent leverages advanced analytical techniques to provide meaningful insights into the codebase's health and stability. + +tasks: + - description: | + Analyze the code changes in the MR. Evaluate the significance, potential risks, and impact on the codebase. Consider all programming languages and project types. Provide insights into how the changes might affect the project's stability and maintainability. + + **Merge Request Details**: + {input} + + agent: Code Analysis Agent + expected_output: | + - An impact score between 0 and 10, reflecting the significance and potential risk of the code changes. + - A detailed analysis explaining the reasoning behind the score, including: + - Identification of critical components or modules affected. + - Assessment of code complexity introduced or modified. + - Potential risks or issues that may arise from the changes. + - Recommendations for mitigating identified risks or improving the code. diff --git a/examples/agents/complexity_assessment_crew.yaml b/examples/agents/complexity_assessment_crew.yaml new file mode 100644 index 0000000..1d2d606 --- /dev/null +++ b/examples/agents/complexity_assessment_crew.yaml @@ -0,0 +1,32 @@ +name: Complexity Assessment Crew +process: sequential + +agents: + - role: Complexity Assessment Agent + goal: | + Evaluate the complexity of the merge request (MR) changes using language-agnostic methods. Assess the difficulty, potential risks, and impact associated with the changes on a scale from 0 to 10. Consider factors such as the extent of code modifications, structural changes, dependencies affected, and introduction of complex algorithms or patterns. + + backstory: | + A seasoned software engineer with a broad understanding of various programming paradigms and architectures. Possesses the ability to analyze code changes without relying on specific language syntax. Utilizes reasoning and deduction to estimate complexity through universal software development principles and best practices. + +tasks: + - description: | + Analyze the changes made in the MR to evaluate their complexity. Use language-agnostic methods, considering aspects such as: + + - **Lines of Code Changed**: Total additions and deletions. + - **Structural Modifications**: Changes to the architecture or design patterns. + - **Introduction of Complex Algorithms**: Incorporation of advanced algorithms or data structures. + - **Dependencies Affected**: Impact on external libraries, modules, or services. + - **Refactoring Efforts**: Simplification or complication of existing code. + + **Merge Request Details**: + {input} + + agent: Complexity Assessment Agent + expected_output: | + - A **complexity impact score** between 0 and 10, where higher scores indicate greater complexity and potential risk. + - A detailed analysis explaining the reasoning behind the score, including: + - Summary of key changes contributing to the complexity. + - Identification of any complex algorithms or patterns introduced. + - Assessment of potential challenges in testing, integration, or maintenance. + - Recommendations for simplifying complex areas, if applicable. diff --git a/examples/agents/impact_evaluator_crew.yaml b/examples/agents/impact_evaluator_crew.yaml new file mode 100644 index 0000000..b7c5e63 --- /dev/null +++ b/examples/agents/impact_evaluator_crew.yaml @@ -0,0 +1,54 @@ +name: Impact Evaluator Crew +process: sequential +memory: False + +agents: + - role: Impact Evaluator Agent + goal: | + Integrate the assessments from all other agents, applying configured weights to each score, to compute an overall impact score on a scale from 0 to 10 for a Merge Request (MR). Based on the overall impact score and predefined thresholds, decide whether the MR qualifies for auto-approval and merging without human intervention. Provide a comprehensive impact assessment report and a clear recommendation. + + backstory: | + A methodical analyst who integrates various inputs to produce a comprehensive impact evaluation. Capable of reasoning to aggregate results, apply weights, and make decisions based on organizational policies regarding auto-approvals. Ensures that only MRs with acceptable risk levels are auto-approved to maintain codebase integrity. + +tasks: + - description: | + Aggregate the assessments from all agents. Apply weights to each score to compute an overall impact score on a scale from 0 to 10. Based on the overall impact score and predefined thresholds, determine whether the MR should be auto-approved and merged without human approval. Provide a comprehensive impact assessment report compiling all assessments, along with a clear recommendation. + + **Merge Request ID**: {input} + + **Agent Assessments**: + - **Code Analysis Agent**: {code_analysis_assessment} + - **Complexity Assessment Agent**: {complexity_assessment} + - **Test Coverage Agent**: {test_coverage} + - **Risk Assessment Agent**: {risk_assessment} + + agent: Impact Evaluator Agent + expected_output: | + - **Overall Impact Score**: A number between 0 and 10. + - **Recommendation**: A clear statement indicating whether the MR should be auto-approved and merged without human intervention. + - **Justification**: A detailed and formatted impact assessment report justifying the score and recommendation, ready for posting as a comment on the Merge Request. + + **Output Format (Markdown ready for MR comment):** + + ~~~ + # Impact Assessment Report for MR # + + **Overall Impact Score**: X.X + + **Recommendation**: [Auto-approve and merge] or [Requires human review] + + ## Detailed Assessments + + - **Code Analysis Agent**: Score X.X + - **Findings**: [Summary of code analysis] + - **Complexity Assessment Agent**: Score X.X + - **Findings**: [Summary of complexity assessment] + - **Test Coverage Agent**: Score X.X + - **Findings**: [Summary of test coverage] + - **Risk Assessment Agent**: Score X.X + - **Findings**: [Summary of risk assessment] + + ## Justification + + [Detailed reasoning behind the overall score and recommendation.] + ~~~ diff --git a/examples/agents/mr_processing_crew.yaml b/examples/agents/mr_processing_crew.yaml index a0fec90..36ac467 100644 --- a/examples/agents/mr_processing_crew.yaml +++ b/examples/agents/mr_processing_crew.yaml @@ -1,6 +1,7 @@ name: MR Input Processing Crew process: sequential -memory: True +memory: False +cache: True agents: - role: Input Handler @@ -11,38 +12,21 @@ agents: goal: Retrieve all data about the merge request for further processing from the GitLab API backstory: An adept communicator with GitLab, retrieving and presenting all information efficiently. tools: - - GitlabMergeRequestTool - - - role: Change Extractor - goal: Prepare structured and enriched change data of a Merge request, including detailed diffs/changes, annotations and categorizations, for a deep Impact Assessment evaluation. - backstory: A diligent analyst who dissects changes for clarity. + - name: GitlabMergeRequestTool + args: + result_as_answer: true tasks: - description: | Validate the provided MR input and extract identifiers. User Input: {input} agent: Input Handler - expected_output: Valid MR identifiers or error message. + expected_output: | + A structured object containing: + - mr_id: The ID of the merge request. + - project_name: The name of the project associated with the MR. - description: | Retrieve MR details using the GitLab API. agent: MR Retriever expected_output: | Obtain complete MR details, including metadata (e.g., title, author, status) and diffs. Ensure the full MR changes/diffs are included - - - description: | - Extract change information from the MR diffs and preprocess it. User Input: {input} - For each file change: - - Determine the file type (code, documentation, configuration, etc.) based on file extension and path. - - Detect the programming language or file format. - - Classify the change type (added, modified, deleted, renamed). - - Identify critical files. - - Detect test files based on naming conventions and directory structure. - - Parse the diff content to extract meaningful information and structure it for further analysis. - - Calculate preliminary code complexity metrics where applicable. - - Compile all extracted and enriched data into a structured format as per the expected output. - - Provide an enriched response that is finally used for an Impact Assessment of the overall MR change. - - Remember to include any further information about the MR that could be important for a deep-down impact assessment - - agent: Change Extractor - expected_output: Structured change data ready for impact assessment. diff --git a/examples/agents/mr_publication_crew.yaml b/examples/agents/mr_publication_crew.yaml new file mode 100644 index 0000000..3b34a02 --- /dev/null +++ b/examples/agents/mr_publication_crew.yaml @@ -0,0 +1,72 @@ +name: MR Publication Crew +process: sequential +memory: True + +agents: + - role: Publication Agent + goal: | + Publish the impact assessment report to the MR, ensuring that all stakeholders are informed of the evaluation results. + Format the report using Markdown for readability. + backstory: | + A diligent communicator who values transparency and clarity. + Skilled in formatting and presenting information effectively, making sure the assessment is accessible and understandable to all MR participants. + tools: + - GitlabMergeCommentTool + + - role: Execution Agent + goal: | + Execute the approval and merge actions based on the recommendation from the impact assessment report. + If the recommendation is to auto-approve and merge, proceed to approve and merge the MR using the GitLab API. + If the recommendation is for manual review, then do nothing. + backstory: | + A responsible executor who follows decisions. + Prioritizes adherence to established policies and procedures, ensuring that actions are carried out accurately and efficiently. + tools: + - GitlabMergeApprovalTool + - GitlabMergeCommentTool + +tasks: + - description: | + Receive the impact assessment report and the recommendation from the Impact Evaluator Crew. + + **Tasks**: + - Format the impact assessment report using Markdown for readability and consistency. + - Post the formatted report as a comment on the MR, making it available to all stakeholders. + + **Inputs**: + - **MR ID**: {input} + - **Impact Assessment Report**: + {impact_assessment_report} + + agent: Publication Agent + expected_output: | + - The impact assessment report is posted on the MR as a comment, properly formatted in Markdown. + - The comment is visible to all MR participants and stakeholders. + - Confirmation that the posting was successful, or an error message if it failed. + + - description: | + Based on the recommendation from the Impact Evaluator Crew, execute the following: + + **If the recommendation is to "Auto-approve and merge"**: + - Approve the MR using the GitLab API. + - Post a confirmation comment on the MR indicating that it has been approved. + + **If the recommendation is "Requires human review"**: + - Leave the MR open for manual review. + - Post a comment on the MR indicating that it requires further review due to its impact assessment. + + **Inputs**: + - **MR ID**: {input} + - **Impact Assessment Report**: + {impact_assessment_report} + agent: Execution Agent + expected_output: | + - **If auto-approved and merged**: + - The MR is approved. + - The MR is merged into the target branch. + - A confirmation comment is posted on the MR indicating successful approval and merge. + - Confirmation messages or logs indicating the actions were successful, or error messages if any actions failed. + + - **If not auto-approved**: + - The MR remains open for manual review. + - A comment is posted on the MR indicating that the MR requires further review due to its impact assessment. diff --git a/examples/agents/risk_assessment_crew.yaml b/examples/agents/risk_assessment_crew.yaml new file mode 100644 index 0000000..183bac0 --- /dev/null +++ b/examples/agents/risk_assessment_crew.yaml @@ -0,0 +1,33 @@ +name: Risk Assessment Crew +process: sequential + +agents: + - role: Risk Assessment Agent + goal: | + Evaluate the Merge Request (MR) changes to identify potential risks introduced by the modifications. Risks may include changes to critical files or configurations, security vulnerabilities, performance impacts, and compatibility issues. Assess the severity of these risks and assign a risk impact score from 0 to 10, where higher scores indicate higher levels of risk. Provide recommendations for mitigating identified risks. + + backstory: | + A seasoned risk management expert dedicated to ensuring the stability, security, and performance of the codebase. Possesses deep knowledge of software development practices and the ability to foresee potential issues arising from code changes. Uses analytical skills and experience to identify high-risk areas and suggest preventive measures. + +tasks: + - description: | + Analyze the MR changes to identify any potential risks introduced. Consider factors such as: + + - **Modifications to Critical Files or Configurations**: Changes to core modules, system configurations, or foundational code. + - **Security Vulnerabilities**: Introduction of code that may expose the system to security threats, such as injection vulnerabilities, improper authentication mechanisms, or insecure data handling. + - **Performance Impacts**: Changes that could degrade system performance, including inefficient algorithms or resource-intensive operations. + - **Compatibility Issues**: Modifications that might cause integration problems with existing systems, APIs, or dependencies. + - **Stability and Reliability**: Potential for crashes, data corruption, or other stability issues. + + Evaluate the severity of these risks and provide recommendations for mitigation. + + **Merge Request Details**: + {input} + + agent: Risk Assessment Agent + expected_output: | + - A **risk impact score** between 0 and 10, where higher scores indicate higher levels of risk. + - A detailed risk assessment report including: + - **Summary of Identified Risks**: Description of each potential risk found. + - **Severity Evaluation**: Explanation of why each risk is significant. + - **Recommendations**: Suggested actions to mitigate or address the identified risks. diff --git a/examples/agents/test_coverage_crew.yaml b/examples/agents/test_coverage_crew.yaml new file mode 100644 index 0000000..9208818 --- /dev/null +++ b/examples/agents/test_coverage_crew.yaml @@ -0,0 +1,34 @@ +name: Test Coverage Assessment Crew +process: sequential + +agents: + - role: Test Coverage Assessment Agent + goal: | + Evaluate the Merge Request (MR) changes to determine their impact on test coverage. Assess whether the changes negatively affect test coverage by reducing it or failing to include necessary tests. Assign a test coverage impact score from 0 to 10, where higher scores indicate a greater negative impact on coverage. Identify areas lacking adequate test coverage and provide recommendations to improve it. + + backstory: | + A dedicated quality assurance specialist committed to ensuring robust test coverage across the codebase. Possesses extensive experience in software testing methodologies and understands the importance of comprehensive testing for code reliability. Skilled at identifying gaps in test coverage and suggesting effective strategies to address them. + +tasks: + - description: | + Analyze the MR to assess its impact on test coverage. Specifically: + + - Determine if code changes lack corresponding tests or if existing tests are removed or insufficient. + - Evaluate whether the changes affect critical functionalities that require additional testing. + - Identify any reduction in test coverage due to code deletions, modifications, or lack of new tests. + - Provide recommendations to improve test coverage where it is inadequate. + + **Merge Request Details**: + {input} + + agent: Test Coverage Assessment Agent + expected_output: | + - A **test coverage impact score** between 0 and 10, where: + - Scores >7 indicate a significant negative impact on test coverage (e.g., decreased coverage, missing tests for new features). + - Scores 4-7 indicate some negative impact (e.g., insufficient tests, minor reductions in coverage). + - Scores <4 indicate minimal or no negative impact on test coverage. + + - A detailed report including: + - **Summary of Findings**: Overview of how the MR negatively affects test coverage. + - **Areas Lacking Coverage**: Specific code changes that are not adequately tested. + - **Recommendations**: Suggestions to add or improve tests to enhance coverage. diff --git a/examples/agents_bak/mr_decision_execution_crew.yaml b/examples/agents_bak/mr_decision_execution_crew.yaml index 2f73c35..e992867 100644 --- a/examples/agents_bak/mr_decision_execution_crew.yaml +++ b/examples/agents_bak/mr_decision_execution_crew.yaml @@ -1,6 +1,5 @@ name: MR Decision and Execution Crew process: sequential -memory: True agents: - role: Commenter Agent diff --git a/mergebot_flow.html b/mergebot_flow.html new file mode 100644 index 0000000..9b23a0c --- /dev/null +++ b/mergebot_flow.html @@ -0,0 +1,202 @@ + + + + + Flow Plot + + + + + +
+
+
+
+
+ + +
+
+
Start Method
+
+ +
+
+
Method
+
+ +
+
+
Crew Method
+
+ +
+
+
Router
+
+ +
+
+
Trigger
+
+ +
+
+
AND Trigger
+
+ +
+
+
Router Trigger
+
+ +
+
+ +
+ + +
+
+ + + + + + + + diff --git a/poetry.lock b/poetry.lock index 2129399..2d4bccf 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1076,30 +1076,31 @@ toml = ["tomli"] [[package]] name = "crewai" -version = "0.79.4" +version = "0.86.0" description = "Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks." optional = false python-versions = "<=3.13,>=3.10" files = [ - {file = "crewai-0.79.4-py3-none-any.whl", hash = "sha256:2236e471a17a3712005f7b590e6f5b627652153d0b16104ffc4747f45e8f6a48"}, - {file = "crewai-0.79.4.tar.gz", hash = "sha256:9ffc32f68ec3ff31e72f971d21b220946debb5d52c890afd19dddf09ad632f13"}, + {file = "crewai-0.86.0-py3-none-any.whl", hash = "sha256:ef1ff4b3df85a72eda2d64ea6fcd7f53461271e13822ff4937d0fa41055ef025"}, + {file = "crewai-0.86.0.tar.gz", hash = "sha256:30c8a1f185ea47c552e3aa4b4ef128c0cd5ca5e93c4b1454830b517c7bde55ed"}, ] [package.dependencies] appdirs = ">=1.4.4" auth0-python = ">=4.7.1" -chromadb = ">=0.4.24" +chromadb = ">=0.5.18" click = ">=8.1.7" -crewai-tools = ">=0.14.0" +crewai-tools = ">=0.17.0" instructor = ">=1.3.3" json-repair = ">=0.25.2" jsonref = ">=1.1.0" -langchain = ">=0.2.16" litellm = ">=1.44.22" openai = ">=1.13.3" +openpyxl = ">=3.1.5" opentelemetry-api = ">=1.22.0" opentelemetry-exporter-otlp-proto-http = ">=1.22.0" opentelemetry-sdk = ">=1.22.0" +pdfplumber = ">=0.11.4" pydantic = ">=2.4.2" python-dotenv = ">=1.0.0" pyvis = ">=0.3.2" @@ -1110,17 +1111,22 @@ uv = ">=0.4.25" [package.extras] agentops = ["agentops (>=0.3.0)"] +fastembed = ["fastembed (>=0.4.1)"] +mem0 = ["mem0ai (>=0.1.29)"] +openpyxl = ["openpyxl (>=3.1.5)"] +pandas = ["pandas (>=2.2.3)"] +pdfplumber = ["pdfplumber (>=0.11.4)"] tools = ["crewai-tools (>=0.14.0)"] [[package]] name = "crewai-tools" -version = "0.14.0" +version = "0.17.0" description = "Set of tools for the crewAI framework" optional = false python-versions = "<=3.13,>=3.10" files = [ - {file = "crewai_tools-0.14.0-py3-none-any.whl", hash = "sha256:0a804a828c29869c3af3253f4fc4c3967a3f80f06dab22e9bbe9526608a31564"}, - {file = "crewai_tools-0.14.0.tar.gz", hash = "sha256:510f3a194bcda4fdae4314bd775521964b5f229ddbe451e5d9e0216cae57f4e3"}, + {file = "crewai_tools-0.17.0-py3-none-any.whl", hash = "sha256:85cf15286684ecad579b5a497888c6bf8a079ca443f7dd63a52bf1709655e4a3"}, + {file = "crewai_tools-0.17.0.tar.gz", hash = "sha256:2a2986000775c76bad45b9f3a2be857d293cf5daffe5f316abc052e630b1e5ce"}, ] [package.dependencies] @@ -1130,7 +1136,6 @@ docker = ">=7.1.0" docx2txt = ">=0.8" embedchain = ">=0.1.114" lancedb = ">=0.5.4" -langchain = ">=0.3.1" openai = ">=1.12.0" pydantic = ">=2.6.1" pyright = ">=1.1.350" @@ -8679,4 +8684,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "8e1f54edbf0309254f610567146d9360cee4f686d1c37d7cd8b4b2c49924a068" +content-hash = "6a5f1560609e6bdee9d7168e0c5930b20d29acaa6a1d5033fe3c2447edaa67f4" diff --git a/pyproject.toml b/pyproject.toml index 1ac3556..8e4a1c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ aiosqlite = "^0.20.0" litellm = "^1.46.0" dataclasses-json = "^0.6.7" chainlit = "1.3.2" -crewai = "^0.79.4" +crewai = "^0.86.0" langchain-core = "^0.3.15" langchain = "^0.3.7" langchain-community = "^0.3.5" diff --git a/sage/flows/mergebot.py b/sage/flows/mergebot.py new file mode 100644 index 0000000..e1e6b2d --- /dev/null +++ b/sage/flows/mergebot.py @@ -0,0 +1,148 @@ +import random +from crewai.flow.flow import Flow, listen, router, start, and_ +from crewai import Crew +from pydantic import BaseModel +from sage.constants import agents_crew +import re + + +# Get al Crew Instances +all_instances = {crew.name: crew for crew in agents_crew} + +# Get the MR Processing Crew +mr_processing_crew: Crew = all_instances.get("MR Input Processing Crew") + +# Get all Impact Assessment Crews +code_analysis_crew: Crew = all_instances.get("Code Analysis Assessment Crew") +complexity_assessment_crew: Crew = all_instances.get("Complexity Assessment Crew") +risk_assessment_crew: Crew = all_instances.get("Risk Assessment Crew") +test_coverage_crew: Crew = all_instances.get("Test Coverage Assessment Crew") + +# Get the Impact Evaluator Crew +impact_evaluator_crew: Crew = all_instances.get("Impact Evaluator Crew") + +# Get the MR Decision and Execution Crew +mr_decision_crew: Crew = all_instances.get("MR Publication Crew") + + +# Sample Input +bot_input = "MR https://gitlab.its.getingecloud.net/dts/lestrade/aide-recorder/-/merge_requests/40" + + +def extract_merge_request_id(output_string): + pattern = r"https://.+/merge_requests/(\d+)" + + # Search the output string for the pattern + match = re.search(pattern, output_string) + + # Extract and return the Merge Request ID if found + if match: + return int(match.group(1)) + else: + return None + + +class MergeBotState(BaseModel): + """Defines the bot data state""" + + mr_details: str = "" + mr_id: int = None + code_analysis_assessment: str = "" + complexity_assessment: str = "" + test_coverage_assessment: str = "" + risk_assessment: str = "" + impact_assessment: str = "" + + +class MergeBotFlow(Flow[MergeBotState]): + initial_state = MergeBotState + + @start() + def begin(self): + print("Commencing and starting the MergeBot") + + @listen(begin) + def mr_retriever(self): + """Runs a Crew to extract Merge Request Details""" + mr_details = mr_processing_crew.kickoff(inputs={"input": bot_input}).raw + self.state.mr_details = mr_details + self.state.mr_id = extract_merge_request_id(mr_details) + + @listen(mr_retriever) + def code_analysis_assessment(self): + """Runs the Code Analysis Assessment on the MR details""" + + self.state.code_analysis_assessment = code_analysis_crew.kickoff( + inputs={"input": self.state.mr_details} + ).raw + + @listen(mr_retriever) + def complexity_assessment(self): + """Runs the complexity_assessment on the MR details""" + + self.state.complexity_assessment = complexity_assessment_crew.kickoff( + inputs={"input": self.state.mr_details} + ).raw + + @listen(mr_retriever) + def test_coverage_assessment(self): + """Runs the Test Coverage Assessement on the MR details""" + + self.state.test_coverage_assessment = test_coverage_crew.kickoff( + inputs={"input": self.state.mr_details} + ).raw + + @listen(mr_retriever) + def risk_assessment(self): + """Runs the Risk Analysis Assessement on the MR details""" + + self.state.risk_assessment = risk_assessment_crew.kickoff( + inputs={"input": self.state.mr_details} + ).raw + + @listen( + and_( + code_analysis_assessment, + complexity_assessment, + test_coverage_assessment, + risk_assessment, + ) + ) + def impact_evaluator(self): + """Runs the Impact Evaluator Analysis Assessement on the MR details""" + + self.state.impact_assessment = impact_evaluator_crew.kickoff( + inputs={ + "input": self.state.mr_id, + "code_analysis_assessment": self.state.code_analysis_assessment, + "complexity_assessment": self.state.complexity_assessment, + "test_coverage": self.state.test_coverage_assessment, + "risk_assessment": self.state.risk_assessment, + } + ).raw + print("\nFinal Impact Assessment Report:") + print(self.state.impact_assessment) + + @listen(impact_evaluator) + def mr_decision(self): + """Runs the MR decision crew on the impact assessment report""" + + result = mr_decision_crew.kickoff( + inputs={ + "input": self.state.mr_id, + "impact_assessment_report": self.state.impact_assessment, + } + ).raw + + print("\nFinal Response:") + print(result) + + @listen(mr_decision) + def finish(self): + print("MergeBot Processing Completed") + +mergebot = MergeBotFlow() + +if __name__ == "__main__": + mergebot.kickoff() + mergebot.plot("mergebot_flow") diff --git a/sage/tools/gitlab_tool.py b/sage/tools/gitlab_tool.py index 734ff28..d0fac8c 100644 --- a/sage/tools/gitlab_tool.py +++ b/sage/tools/gitlab_tool.py @@ -218,7 +218,7 @@ def post_merge_request_comment(self, mr_iid: int, comment: str) -> str: """ try: mr = self.gitlab_repo_instance.mergerequests.get(mr_iid) - mr.notes.create({"body": comment}) + mr.notes.create({"body": comment.strip()}) return f"Successfully posted comment to Merge Request {mr_iid}." except Exception as e: return f"Failed to post comment to Merge Request {mr_iid}: {str(e)}" @@ -277,7 +277,26 @@ def get_merge_request_comments(self, mr_iid: int) -> List[Dict[str, Any]]: } ] - def run(self, mode: str, query: str) -> str: + def approve_merge_request(self, mr_iid: int) -> str: + """ + Approves a merge request. + + Parameters: + mr_iid (int): The internal ID (iid) of the merge request. + + Returns: + str: Success or failure message. + """ + try: + # Retrieve the merge request + mr = self.gitlab_repo_instance.mergerequests.get(mr_iid) + # Approve the merge request + mr.approve() + return f"Successfully approved Merge Request {mr_iid}." + except Exception as e: + return f"Failed to approve Merge Request {mr_iid}: {str(e)}" + + def run(self, mode: str, query: str = "", body: dict = {}) -> str: # Parent class handling original_modes = { "get_issues", @@ -302,12 +321,14 @@ def run(self, mode: str, query: str) -> str: return "Invalid input. Please provide the Merge Request number as an integer." elif mode == "post_merge_request_comment": try: - mr_iid, comment = query.split("\n\n", 1) - mr_iid = int(mr_iid.strip()) - comment = comment.strip() - return self.post_merge_request_comment(mr_iid, comment) + return self.post_merge_request_comment(**body) except ValueError: return "Invalid input format. Expected:\n\n\n" + elif mode == "approve_merge_request": + try: + return self.approve_merge_request(**body) + except ValueError: + return "Invalid input format." elif mode == "post_merge_request_thread_comment": try: parts = query.split("\n\n", 2) @@ -332,10 +353,6 @@ def run(self, mode: str, query: str) -> str: # prompt.py (continued) -LIST_MERGE_REQUESTS_PROMPT = """ -This tool will fetch a list of the repository's merge requests. It will return basic details such as MR number, title, state, creation date, and author. It takes an optional input to filter by state (opened, closed, merged, all). If no input is provided, it defaults to 'all'. -Example input: "opened" -""" GET_MERGE_REQUEST_PROMPT = """ This tool will fetch the complete details of a specific merge request. @@ -343,17 +360,21 @@ def run(self, mode: str, query: str) -> str: """ POST_MERGE_REQUEST_COMMENT_PROMPT = """ -This tool is useful when you need to post a comment to a merge request in the GitLab repository. **VERY IMPORTANT**: Your input to this tool MUST strictly follow these rules: +This tool is useful when you need to post a comment to a merge request in the GitLab repository. -- First, you must specify the merge request number (IID) as an integer. -- Then, you must place two newlines. -- Then, you must specify your comment. +For example, to post a comment "Looks good to me!" to merge request number 10: + +Example input: {\"merge_request_iid\": \"10\", \"comment\": \"Looks good to me!\"} + +""" + +APPROVE_MERGE_REQUEST_PROMPT = """ +This tool is useful when you need to approve a merge request in a GitLab repository. -For example, to post a comment "Looks good to me!" to merge request number 10, you would pass in the following string: +For example, to approve a merge request number 10: -10 +Example input: {\"merge_request_iid\": \"10\"} -Looks good to me! """ POST_MERGE_REQUEST_THREAD_COMMENT_PROMPT = """ @@ -374,11 +395,6 @@ def run(self, mode: str, query: str) -> str: I agree with your assessment. """ -GET_MERGE_REQUEST_COMMENTS_PROMPT = """ -This tool will fetch all comments for a specific merge request. **VERY IMPORTANT**: You must specify the merge request number (IID) as an integer. -Example input: "10" -""" - class GitlabToolSchema(BaseModel): """Input for GitlabTools.""" @@ -412,7 +428,72 @@ def _run( return self.api_wrapper.run(self.mode, str(mr_iid)) -# Example usage +class GitlabCommentToolSchema(BaseModel): + """Input for GitlabTools.""" + + merge_request_iid: str = Field( + ..., description="The project level ID of the merge request" + ) + comment: str = Field( + ..., description="The merge request comment to be posted to the MR" + ) + + +class GitlabMergeCommentTool(BaseTool): + """Tool for posting merge comments using the Gitlab API.""" + + mode: str = "post_merge_request_comment" + name: str = "Post Merge Requests Comment" + description: str = POST_MERGE_REQUEST_COMMENT_PROMPT + args_schema: Type[BaseModel] = GitlabCommentToolSchema + api_wrapper: GitLabAPIWrapperExtra = GitLabAPIWrapperExtra( + gitlab_branch="main", + ) + + def _run( + self, + **kwargs: Any, + ) -> str: + """Use the GitLab API to run an operation.""" + mr_iid = kwargs.get("merge_request_iid") + mr_comment = kwargs.get("comment") + + if not (mr_iid or mr_comment): + return "The Merge Request IID and message are required." + + return self.api_wrapper.run( + self.mode, body={"mr_iid": mr_iid, "comment": mr_comment} + ) + + +class GitlabMergeApprovalTool(BaseTool): + """Tool for approving Gitlab merge requests.""" + + mode: str = "approve_merge_request" + name: str = "Approve Merge Requests Comment" + description: str = APPROVE_MERGE_REQUEST_PROMPT + args_schema: Type[BaseModel] = GitlabToolSchema + api_wrapper: GitLabAPIWrapperExtra = GitLabAPIWrapperExtra( + gitlab_branch="main", + ) + + def _run( + self, + **kwargs: Any, + ) -> str: + """Use the GitLab API to run an operation.""" + mr_iid = kwargs.get("merge_request_iid") + + if not mr_iid: + return "The Merge Request IID are required." + + return self.api_wrapper.run(self.mode, body={"mr_iid": int(mr_iid)}) + + +# # Example usage # if __name__ == "__main__": -# mr_iid = 50 # Replace with your MR IID -# print(analyze_merge_request(mr_iid)) +# mr_iid = 51 # Replace with your MR IID +# api_wrapper = GitLabAPIWrapperExtra( +# gitlab_branch="main", +# ) +# print(api_wrapper.approve_merge_request(mr_iid)) diff --git a/sage/validators/crew_ai.py b/sage/validators/crew_ai.py index 4a1d507..0e0601f 100644 --- a/sage/validators/crew_ai.py +++ b/sage/validators/crew_ai.py @@ -5,7 +5,7 @@ import yaml from crewai import Agent, Crew, Task from crewai.llm import LLM -from crewai.memory import EntityMemory, LongTermMemory, ShortTermMemory +from crewai.memory import EntityMemory, LongTermMemory, ShortTermMemory, UserMemory from crewai.memory.storage.ltm_sqlite_storage import LTMSQLiteStorage from pydantic import BaseModel, Field, field_validator, model_validator @@ -34,7 +34,7 @@ class AgentConfig(Agent): allow_delegation: bool = Field( default=False, description="Allow delegation of tasks to agents" ) - tools: Optional[List[ToolsConfig | BaseTool | str ]] = Field( + tools: Optional[List[ToolsConfig | BaseTool | str]] = Field( default_factory=list, description="Tools at agents' disposal" ) @@ -170,6 +170,12 @@ def create_crew_memory(self) -> "Crew": dimension=self.embedder["dimension"], ), ) + if hasattr(self, "memory_config") and self.memory_config is not None: + self._user_memory = ( + self.user_memory if self.user_memory else UserMemory(crew=self) + ) + else: + self._user_memory = None return self @field_validator("agents")