From 7b1fbbe2beac5be59af78818e053e7bdb6f42e8e Mon Sep 17 00:00:00 2001 From: TIANYOU CHEN <42710806+CTY-git@users.noreply.github.com> Date: Wed, 16 Apr 2025 12:21:47 +0800 Subject: [PATCH 1/9] add git tool --- patchwork/common/tools/git_tool.py | 44 ++++++++++++++++++++++ patchwork/common/tools/github_tool.py | 2 +- patchwork/steps/GitHubAgent/GitHubAgent.py | 6 ++- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 patchwork/common/tools/git_tool.py diff --git a/patchwork/common/tools/git_tool.py b/patchwork/common/tools/git_tool.py new file mode 100644 index 000000000..4b32765aa --- /dev/null +++ b/patchwork/common/tools/git_tool.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +import os +import subprocess + +from patchwork.common.tools.tool import Tool + + +class GitTool(Tool, tool_name="git_tool", abc_register=False): + def __init__(self, path: str): + super().__init__() + self.path = path + + @property + def json_schema(self) -> dict: + return { + "name": "git_tool", + "description": """\ +Access to the Git CLI, the command is also `git` all args provided are used as is +""", + "input_schema": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": {"type": "string"}, + "description": "The args to run `git` command with.", + } + }, + "required": ["args"], + }, + } + + def execute(self, args: list[str]) -> str: + env = os.environ.copy() + p = subprocess.run( + ["gh", *args], + env=env, + cwd=self.path, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + return p.stdout diff --git a/patchwork/common/tools/github_tool.py b/patchwork/common/tools/github_tool.py index aa5d5effe..16e418c08 100644 --- a/patchwork/common/tools/github_tool.py +++ b/patchwork/common/tools/github_tool.py @@ -6,7 +6,7 @@ from patchwork.common.tools.tool import Tool -class GitHubTool(Tool, tool_name="github_tool"): +class GitHubTool(Tool, tool_name="github_tool", abc_register=False): def __init__(self, path: str, gh_token: str): super().__init__() self.path = path diff --git a/patchwork/steps/GitHubAgent/GitHubAgent.py b/patchwork/steps/GitHubAgent/GitHubAgent.py index bc8d319c1..0ac014538 100644 --- a/patchwork/steps/GitHubAgent/GitHubAgent.py +++ b/patchwork/steps/GitHubAgent/GitHubAgent.py @@ -5,6 +5,7 @@ AgentConfig, AgenticStrategyV2, ) +from patchwork.common.tools.git_tool import GitTool from patchwork.common.tools.github_tool import GitHubTool from patchwork.common.utils.utils import mustache_render from patchwork.step import Step @@ -34,7 +35,10 @@ def __init__(self, inputs): AgentConfig( name="Assistant", model="gemini-2.0-flash", - tool_set=dict(github_tool=GitHubTool(base_path, inputs["github_api_key"])), + tool_set=dict( + github_tool=GitHubTool(base_path, inputs["github_api_key"]), + git_tool=GitTool(base_path), + ), system_prompt="""\ You are a senior software developer helping the program manager to obtain some data from GitHub. You can access github through the `gh` CLI app. From 5bafb638391958f89f114d7d0a7973200d30a9d4 Mon Sep 17 00:00:00 2001 From: TIANYOU CHEN <42710806+CTY-git@users.noreply.github.com> Date: Wed, 16 Apr 2025 12:25:11 +0800 Subject: [PATCH 2/9] bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ab89dee54..d136584c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "patchwork-cli" -version = "0.0.123" +version = "0.0.124" description = "" authors = ["patched.codes"] license = "AGPL" From c953072cb18039a0d17b007a95f14aafc65414e4 Mon Sep 17 00:00:00 2001 From: TIANYOU CHEN <42710806+CTY-git@users.noreply.github.com> Date: Wed, 16 Apr 2025 13:33:43 +0800 Subject: [PATCH 3/9] fixes --- patchwork/common/tools/git_tool.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/patchwork/common/tools/git_tool.py b/patchwork/common/tools/git_tool.py index 4b32765aa..d5acbfa05 100644 --- a/patchwork/common/tools/git_tool.py +++ b/patchwork/common/tools/git_tool.py @@ -16,7 +16,7 @@ def json_schema(self) -> dict: return { "name": "git_tool", "description": """\ -Access to the Git CLI, the command is also `git` all args provided are used as is +Access to the Git CLI, the command is also `git` all args provided are used as is. """, "input_schema": { "type": "object", @@ -24,7 +24,12 @@ def json_schema(self) -> dict: "args": { "type": "array", "items": {"type": "string"}, - "description": "The args to run `git` command with.", + "description": """ +The args to run `git` command with. +E.g. +[\"commit\", \"-m\", \"A commit message\"] to commit changes with a commit message. +[\"add\", \".\"] to stage all changed files. +""", } }, "required": ["args"], @@ -34,7 +39,7 @@ def json_schema(self) -> dict: def execute(self, args: list[str]) -> str: env = os.environ.copy() p = subprocess.run( - ["gh", *args], + ["git", *args], env=env, cwd=self.path, text=True, From 74dd5681a14b492b4ca2cb65acf4dd6d3ad48db4 Mon Sep 17 00:00:00 2001 From: Arpit Roopchandani <17565234+whoisarpit@users.noreply.github.com> Date: Wed, 16 Apr 2025 13:54:33 +0800 Subject: [PATCH 4/9] Add additional system prompt for better usage --- patchwork/steps/GitHubAgent/GitHubAgent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patchwork/steps/GitHubAgent/GitHubAgent.py b/patchwork/steps/GitHubAgent/GitHubAgent.py index 0ac014538..ede41c109 100644 --- a/patchwork/steps/GitHubAgent/GitHubAgent.py +++ b/patchwork/steps/GitHubAgent/GitHubAgent.py @@ -41,7 +41,7 @@ def __init__(self, inputs): ), system_prompt="""\ You are a senior software developer helping the program manager to obtain some data from GitHub. -You can access github through the `gh` CLI app. +You can access github through the `gh` CLI app through the `github_tool`, and `git` through the `git_tool`. Your `gh` app has already been authenticated. """, ) From 7df446ee64b99d2c3cd4a3f80034bebf78b1d550 Mon Sep 17 00:00:00 2001 From: "patched.codes[bot]" <298395+patched.codes[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 05:57:52 +0000 Subject: [PATCH 5/9] Patched tests/cicd/generate_docstring/kotlin_test_file.kt --- .../generate_docstring/kotlin_test_file.kt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/cicd/generate_docstring/kotlin_test_file.kt b/tests/cicd/generate_docstring/kotlin_test_file.kt index 03aec1bcb..77ceaaa31 100644 --- a/tests/cicd/generate_docstring/kotlin_test_file.kt +++ b/tests/cicd/generate_docstring/kotlin_test_file.kt @@ -5,9 +5,27 @@ import java.sql.ResultSet import kotlin.random.Random +/** + * Computes the sum of two numeric values by converting them to Double. + * + * This function accepts any type that extends Number, converts the values to Double, + * and returns the sum as a Double. + * + * @param a The first numeric value of type T, where T extends Number. + * @param b The second numeric value of type T, where T extends Number. + * @return The sum of a and b as a Double. + */ fun aPlusB(a: T, b: T): Double = a.toDouble() + b.toDouble() +/** + * Executes a SQL query on a given database connection and returns the results as a list of lists. + * Each inner list represents a row from the result set, with each element corresponding to a column value. + * + * @param db The database connection to use for executing the query. + * @param query The SQL query to be executed on the database. + * @return A list of rows, where each row is represented as a list of objects. Each object corresponds to a column value in the result set. Returns an empty list if no results are found. + */ fun sqlite(db: Connection, query: String): List> { db.createStatement().use { statement -> statement.executeQuery(query).use { resultSet -> @@ -27,6 +45,15 @@ fun sqlite(db: Connection, query: String): List> { } +/** + * Compares two items using a provided key mapping function, which extracts a comparable value from each item. + * Returns -1 if the first item is less than the second, 1 if it is greater, and 0 if they are equal, based on the comparable value. + * + * @param keyMap A function that maps an item of type T to a comparable value of type R. + * @param item1 The first item to be compared. + * @param item2 The second item to be compared. + * @return An integer result of the comparison: -1, 0, or 1. + */ fun > compare(keyMap: (T) -> R, item1: T, item2: T): Int { return when { keyMap(item1) < keyMap(item2) -> -1 @@ -36,6 +63,13 @@ fun > compare(keyMap: (T) -> R, item1: T, item2: T): Int { } +/** + * Generates a random string of alphabets with the specified length. + * The string includes both lowercase and uppercase English letters. + * + * @param length The desired length of the randomly generated string. + * @return A string consisting of random uppercase and lowercase alphabets. + */ fun randomAlphabets(length: Int): String { val charPool = ('a'..'z') + ('A'..'Z') return (1..length) From 3740728d920dc1494c02d71d6f00df7bfb6a8519 Mon Sep 17 00:00:00 2001 From: "patched.codes[bot]" <298395+patched.codes[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 05:57:52 +0000 Subject: [PATCH 6/9] Patched tests/cicd/generate_docstring/java_test_file.java --- .../cicd/generate_docstring/java_test_file.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/cicd/generate_docstring/java_test_file.java b/tests/cicd/generate_docstring/java_test_file.java index 51a073a3a..b6daddfed 100644 --- a/tests/cicd/generate_docstring/java_test_file.java +++ b/tests/cicd/generate_docstring/java_test_file.java @@ -1,8 +1,25 @@ class Test { + /** + * Calculates the sum of two integers. + * + * @param a The first integer to be added. + * @param b The second integer to be added. + * @return The sum of the two integers. + */ public static int a_plus_b(Integer a, Integer b) { return a + b; } + /** + * Compares two objects based on their keys mapped by a specified key mapping function. + * + * @param keymap A function that maps an object to a comparable value. + * @param a The first object to be compared. + * @param b The second object to be compared. + * @return An integer representing the comparison result: -1 if the key of 'a' is less than the key of 'b', + * 1 if the key of 'a' is greater than the key of 'b', and 0 if the keys are equal. + */ + public static int a_plus_b(Function keymap, object a, Object b) { if (keymap(a) < keymap(b)) { return -1; From 30b699a3703419143d01d662521b78b08da3b72c Mon Sep 17 00:00:00 2001 From: "patched.codes[bot]" <298395+patched.codes[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 05:57:52 +0000 Subject: [PATCH 7/9] Patched tests/cicd/generate_docstring/js_test_file.py.js --- .../generate_docstring/js_test_file.py.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/cicd/generate_docstring/js_test_file.py.js b/tests/cicd/generate_docstring/js_test_file.py.js index 3289c1d6f..6b679b68b 100644 --- a/tests/cicd/generate_docstring/js_test_file.py.js +++ b/tests/cicd/generate_docstring/js_test_file.py.js @@ -1,8 +1,22 @@ +/** + * Computes the sum of two numbers. + * @param {number} a - The first number. + * @param {number} b - The second number. + * @returns {number} The sum of the two numbers. + */ function a_plus_b(a, b) { return a + b; } +/** + * Compares two objects based on the value associated with a given key. + * @param {String} keymap - The key name to be used for comparison. + * @param {Object} a - The first object to compare. + * @param {Object} b - The second object to compare. + * @returns {Number} - Returns -1 if the value of 'a' is less than the value of 'b', + * 1 if greater, or 0 if they are equal. + */ const compare = function (keymap, a, b) { if (a[keymap] < b[keymap]) { return -1; @@ -13,6 +27,13 @@ const compare = function (keymap, a, b) { } } +/** + * Executes a query on a given SQLite database and applies a callback function to each result row. + * @param {Object} db - The SQLite database object to be queried. + * @param {string} query - The SQL query string to be executed on the database. + * @param {Function} callback - A function that will be called with each row of the result set. + * @returns {void} + */ const sqlite = (db, query, callback) => { db.serialize(function () { db.each(query, callback); From 434c93b081628f5aceca23115a9f24664623d7e2 Mon Sep 17 00:00:00 2001 From: "patched.codes[bot]" <298395+patched.codes[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 05:57:52 +0000 Subject: [PATCH 8/9] Patched tests/cicd/generate_docstring/python_test_file.py --- .../generate_docstring/python_test_file.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/cicd/generate_docstring/python_test_file.py b/tests/cicd/generate_docstring/python_test_file.py index f2b0b70d5..087984676 100644 --- a/tests/cicd/generate_docstring/python_test_file.py +++ b/tests/cicd/generate_docstring/python_test_file.py @@ -1,15 +1,44 @@ # fmt: off def a_plus_b(a, b): + """Adds two numbers together. + + Args: + a (int or float): The first number to be added. + b (int or float): The second number to be added. + + Returns: + int or float: The sum of the two numbers. + """ return a + b def sqlite(db, query): + """Executes a given SQL query on a SQLite database and returns the results. + + Args: + db (sqlite3.Connection): A SQLite database connection object. + query (str): The SQL query to be executed on the database. + + Returns: + list: A list of tuples containing the results of the query. + """ + cursor = db.cursor() cursor.execute(query) return cursor.fetchall() def compare(key_map, item1, item2): + """Compares two items based on a key mapping function and determines their order. + + Args: + key_map (function): A function that extracts a comparison key from each item. + item1 (any): The first item to compare. + item2 (any): The second item to compare. + + Returns: + int: -1 if item1 is less than item2, 1 if item1 is greater than item2, and 0 if they are equal. + """ if key_map(item1) < key_map(item2): return -1 elif key_map(item1) > key_map(item2): @@ -21,4 +50,12 @@ def compare(key_map, item1, item2): def random_alphabets( length: int ): + """Generates a random string of alphabets. + + Args: + length (int): The desired length of the output string. + + Returns: + str: A randomly generated string consisting of ASCII alphabets (both lower and uppercase) of the specified length. + """ return ''.join(random.choices(string.ascii_letters, k=length)) From c1db0c2fc5c62b0d56fe3aa6c919ce478ede06f0 Mon Sep 17 00:00:00 2001 From: "patched.codes[bot]" <298395+patched.codes[bot]@users.noreply.github.com> Date: Wed, 16 Apr 2025 05:57:52 +0000 Subject: [PATCH 9/9] Patched tests/cicd/generate_docstring/cpp_test_file.cpp --- .../cicd/generate_docstring/cpp_test_file.cpp | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/cicd/generate_docstring/cpp_test_file.cpp b/tests/cicd/generate_docstring/cpp_test_file.cpp index 53a919ba7..d8adeb0fb 100644 --- a/tests/cicd/generate_docstring/cpp_test_file.cpp +++ b/tests/cicd/generate_docstring/cpp_test_file.cpp @@ -6,11 +6,28 @@ template +/** + * Adds two values of the same type. + * + * @param a The first value to be added. + * @param b The second value to be added. + * @return The result of adding a and b. + */ T a_plus_b(T a, T b) { return a + b; } +/** + * Executes a SQL query on the given SQLite database and returns the results. + * + * @param db A pointer to the SQLite database object. + * @param query A string containing the SQL query to be executed. + * @return A vector of vectors of strings where each sub-vector represents a row from the query result, + * with each string in the sub-vector corresponding to a column value. Returns an empty vector + * if the query fails to prepare. + */ + std::vector> sqlite(sqlite3* db, const std::string& query) { std::vector> results; sqlite3_stmt* stmt; @@ -38,6 +55,15 @@ std::vector> sqlite(sqlite3* db, const std::string& que template +/** + * Compares two items based on a key mapping function and returns an integer indicating their order. + * + * @param key_map A function that extracts a comparison key from an item. + * @param item1 The first item to be compared. + * @param item2 The second item to be compared. + * @return -1 if the first item is less than the second, 1 if the first item is greater than the second, + * and 0 if they are equal based on the mapping function. + */ int compare(F key_map, const T& item1, const T& item2) { auto val1 = key_map(item1); auto val2 = key_map(item2); @@ -48,6 +74,12 @@ int compare(F key_map, const T& item1, const T& item2) { } +/** + * Generates a random string composed of lowercase and uppercase alphabets. + * + * @param length The length of the random string to be generated. + * @return A random string containing only alphabetic characters with the specified length. + */ std::string random_alphabets(int length) { static const std::string chars = "abcdefghijklmnopqrstuvwxyz"