From da381237559f498ba4dc1392697b2a69c59bc910 Mon Sep 17 00:00:00 2001 From: zhangzhiyong Date: Mon, 6 Jan 2025 11:07:16 +0800 Subject: [PATCH 1/3] chore(hive): update WritePythonCode.java and ExecutePythonCode.java --- .../java/run/mone/hive/actions/python/ExecutePythonCode.java | 1 - .../main/java/run/mone/hive/actions/python/WritePythonCode.java | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/python/ExecutePythonCode.java b/jcommon/hive/src/main/java/run/mone/hive/actions/python/ExecutePythonCode.java index b58ad75af..223df09c0 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/python/ExecutePythonCode.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/python/ExecutePythonCode.java @@ -9,7 +9,6 @@ import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeUnit; public class ExecutePythonCode extends Action { diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/python/WritePythonCode.java b/jcommon/hive/src/main/java/run/mone/hive/actions/python/WritePythonCode.java index ffdebe632..3f3062337 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/python/WritePythonCode.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/python/WritePythonCode.java @@ -30,6 +30,8 @@ def execute(params): """; public WritePythonCode() { + setName("WritePythonCode"); + setDescription(""); setFunction((req, action) -> { String message = req.getMessage().getContent(); String str = AiTemplate.renderTemplate(prompt, ImmutableMap.of("requirements", message)); From 0f9378a33e515d08584cfbd40e630b1cb8f6fd8b Mon Sep 17 00:00:00 2001 From: zhangzhiyong Date: Mon, 6 Jan 2025 18:16:08 +0800 Subject: [PATCH 2/3] chore: update multiple files in hive project with no specific changes --- .../java/run/mone/hive/actions/Action.java | 4 +- .../hive/actions/AnalyzeArchitecture.java | 2 +- .../java/run/mone/hive/actions/WriteCode.java | 6 +- .../run/mone/hive/actions/WriteDesign.java | 2 +- .../actions/python/ExecutePythonCode.java | 109 +++------- .../hive/actions/python/FixPythonBug.java | 3 +- .../hive/actions/python/WritePythonCode.java | 35 ++- .../run/mone/hive/common/CustomStreamTag.java | 45 ++++ .../mone/hive/common/StreamingXmlParser.java | 200 ++++++++++++++++++ .../mone/hive/common/XmlParserCallback.java | 18 ++ .../hive/common/XmlParserCallbackAdapter.java | 32 +++ .../java/run/mone/hive/schema/ActionReq.java | 3 + .../java/run/mone/hive/schema/Message.java | 7 +- .../java/run/mone/hive/schema/MetaKey.java | 22 ++ .../java/run/mone/hive/schema/MetaValue.java | 23 ++ .../actions/python/ExecutePythonCodeTest.java | 60 ++++++ .../java/run/mone/hive/common/StreamTest.java | 55 +++++ 17 files changed, 529 insertions(+), 97 deletions(-) create mode 100644 jcommon/hive/src/main/java/run/mone/hive/common/CustomStreamTag.java create mode 100644 jcommon/hive/src/main/java/run/mone/hive/common/StreamingXmlParser.java create mode 100644 jcommon/hive/src/main/java/run/mone/hive/common/XmlParserCallback.java create mode 100644 jcommon/hive/src/main/java/run/mone/hive/common/XmlParserCallbackAdapter.java create mode 100644 jcommon/hive/src/main/java/run/mone/hive/schema/MetaKey.java create mode 100644 jcommon/hive/src/main/java/run/mone/hive/schema/MetaValue.java create mode 100644 jcommon/hive/src/test/java/run/mone/hive/actions/python/ExecutePythonCodeTest.java create mode 100644 jcommon/hive/src/test/java/run/mone/hive/common/StreamTest.java diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/Action.java b/jcommon/hive/src/main/java/run/mone/hive/actions/Action.java index c5f904cc9..bd4da0bb2 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/Action.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/Action.java @@ -22,7 +22,7 @@ public class Action { protected LLM llm; - protected BiFunction function = (req, action) -> this.getClass().getName(); + protected BiFunction function = (req, action) -> Message.builder().content(this.getClass().getName()).build(); @ToString.Exclude private Role role; @@ -34,7 +34,7 @@ public Action(String name, String description) { } public CompletableFuture run(ActionReq req) { - return CompletableFuture.supplyAsync(() -> Message.builder().role(req.getRole().getName()).content(this.function.apply(req, this)).build()); + return CompletableFuture.supplyAsync(() -> Message.builder().role(req.getRole().getName()).content(this.function.apply(req, this).getContent()).build()); } } \ No newline at end of file diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/AnalyzeArchitecture.java b/jcommon/hive/src/main/java/run/mone/hive/actions/AnalyzeArchitecture.java index 9180c4059..ee55063e3 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/AnalyzeArchitecture.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/AnalyzeArchitecture.java @@ -17,6 +17,6 @@ public class AnalyzeArchitecture extends Action { @Override public CompletableFuture run(ActionReq req) { log.info("AnalyzeArchitecture"); - return CompletableFuture.supplyAsync(() -> Message.builder().sendTo(Lists.newArrayList("Design")).role(req.getRole().getProfile()).content(this.function.apply(req, this)).build()); + return CompletableFuture.supplyAsync(() -> Message.builder().sendTo(Lists.newArrayList("Design")).role(req.getRole().getProfile()).content(this.function.apply(req, this).getContent()).build()); } } diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/WriteCode.java b/jcommon/hive/src/main/java/run/mone/hive/actions/WriteCode.java index 2c315f7c4..b4f21d6b2 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/WriteCode.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/WriteCode.java @@ -19,7 +19,11 @@ public class WriteCode extends Action { @Override public CompletableFuture run(ActionReq req) { log.info("WriteCode"); - return CompletableFuture.supplyAsync(() -> Message.builder().role(req.getRole().getProfile()).content(this.function.apply(req, this)).build()); + return CompletableFuture.supplyAsync(() -> { + Message msg = this.function.apply(req, this); + msg.setRole(req.getRole().getName()); + return msg; + }); } } diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/WriteDesign.java b/jcommon/hive/src/main/java/run/mone/hive/actions/WriteDesign.java index 3359bf110..4360eab92 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/WriteDesign.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/WriteDesign.java @@ -17,6 +17,6 @@ public class WriteDesign extends Action { @Override public CompletableFuture run(ActionReq req) { log.info("WriteDesign"); - return CompletableFuture.supplyAsync(() -> Message.builder().role(req.getRole().getProfile()).sendTo(Lists.newArrayList("Engineer")).content(this.function.apply(req, this)).build()); + return CompletableFuture.supplyAsync(() -> Message.builder().role(req.getRole().getProfile()).sendTo(Lists.newArrayList("Engineer")).content(this.function.apply(req, this).getContent()).build()); } } \ No newline at end of file diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/python/ExecutePythonCode.java b/jcommon/hive/src/main/java/run/mone/hive/actions/python/ExecutePythonCode.java index 223df09c0..77102a915 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/python/ExecutePythonCode.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/python/ExecutePythonCode.java @@ -3,127 +3,70 @@ import com.google.common.collect.ImmutableMap; import run.mone.hive.actions.Action; import run.mone.hive.common.AiTemplate; +import run.mone.hive.schema.Message; import java.io.BufferedReader; -import java.io.File; import java.io.InputStreamReader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.TimeUnit; + public class ExecutePythonCode extends Action { private final String prompt = """ - You are a Python code executor. Your task is to execute the following Python code: - - - ${code} - - The code should be executed with the following parameters: - - ${params} - - Please provide the execution result without any additional explanations. Wrap the result in tags. - """; - - private final String paramGenerationPrompt = """ - You are an AI assistant specialized in analyzing Python code and generating appropriate test parameters. Your task is to examine the following Python code and suggest suitable parameters for testing: + You are a Python code executor. Your task is to generate a single line of Python code that executes the following function with appropriate parameters: ${code} - Please provide a set of test parameters that will effectively exercise the main functionality of this code. Consider the following guidelines: + Generate a line of code that calls the main function with suitable parameters and prints the result. + The line should be in the format: print(function_name(param1, param2, ...)) - 1. Identify the main function or entry point of the code. - 2. Determine the expected input types (e.g., integers, strings, lists, dictionaries). - 3. Include edge cases and typical use cases in your parameter suggestions. - 4. If the code requires specific formats (e.g., date strings, file paths), provide examples that match those formats. - 5. If the code interacts with external resources (e.g., files, APIs), suggest mock data or placeholders. + Provide only the generated line of code without any additional explanations. - Format your response as a Python dictionary or a list of arguments, depending on how the main function accepts input. For example: - - {"param1": value1, "param2": value2} or [arg1, arg2, arg3] - - Provide only the parameter suggestions without any additional explanation. + example: + print(sun(11,22)) """; public ExecutePythonCode() { setFunction((req, action) -> { String code = req.getMessage().getContent(); - String params = generateParameters(code); String renderedPrompt = AiTemplate.renderTemplate(prompt, ImmutableMap.of( - "code", code, - "params", params + "code", code )); + String exeCmd = llm.chat(renderedPrompt); String result = null; try { - result = executePythonCode(code, params); + result = executePythonCode(code + "\n" + exeCmd).getContent(); } catch (Exception e) { throw new RuntimeException(e); } - return "" + result + ""; + return Message.builder().content(result).build(); }); } - private String generateParameters(String code) { - String promptForParams = AiTemplate.renderTemplate(paramGenerationPrompt, ImmutableMap.of( - "code", code - )); - // Use LLMProvider to generate parameters - return llm.chat(promptForParams); - } - private String executePythonCode(String code, String params) throws Exception { - // Create a temporary file to store the Python code - Path tempFile = Files.createTempFile("python_code_", ".py"); - - - Files.write(tempFile, code.getBytes()); - - // Prepare the command to execute Python code - ProcessBuilder processBuilder = new ProcessBuilder("python3", tempFile.toString()); - processBuilder.redirectErrorStream(true); - - // Set the working directory to the user's home directory - processBuilder.directory(new File(System.getProperty("user.home"))); - // Start the process - StringBuilder output = new StringBuilder(); - int exitCode = -1; - Process process = processBuilder.start(); - // Read the output - - - // Read the output - try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + //写一段代码 调用 python ```$code``` 执行代码,并且捕获输出如果有错误也包含错误(class) + public Message executePythonCode(String code) { + try { + ProcessBuilder processBuilder = new ProcessBuilder("python", "-c", code); + processBuilder.redirectErrorStream(true); + Process process = processBuilder.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + StringBuilder output = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { output.append(line).append("\n"); } - } - - // Wait for the process to complete - boolean completed = process.waitFor(30, TimeUnit.SECONDS); - - // Delete the temporary file - Files.delete(tempFile); - if (!completed) { - process.destroyForcibly(); - output.append("Error: Python code execution timed out after 30 seconds."); - } else { - exitCode = process.exitValue(); + int exitCode = process.waitFor(); if (exitCode != 0) { - output.append("Error: Python code execution failed with exit code: ") - .append(exitCode) - .append("\n"); + throw new RuntimeException("Python code execution failed with exit code " + exitCode); } + return Message.builder().content(output.toString().trim()).build(); + } catch (Exception e) { + return Message.builder().content(e.getMessage()).build(); } - String result = output.toString().trim(); - if (result.isEmpty()) { - result = "No output or error message received."; - } - return result; } + } \ No newline at end of file diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/python/FixPythonBug.java b/jcommon/hive/src/main/java/run/mone/hive/actions/python/FixPythonBug.java index 32e1ba6eb..269bc14a4 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/python/FixPythonBug.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/python/FixPythonBug.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap; import run.mone.hive.actions.Action; import run.mone.hive.common.AiTemplate; +import run.mone.hive.schema.Message; import run.mone.hive.utils.PythonExecutor; import java.util.regex.Matcher; @@ -35,7 +36,7 @@ public FixPythonBug() { } catch (Exception e) { throw new RuntimeException(e); } - return "" + fixedCode + ""; + return Message.builder().content("" + fixedCode + "").build(); }); } diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/python/WritePythonCode.java b/jcommon/hive/src/main/java/run/mone/hive/actions/python/WritePythonCode.java index 3f3062337..8a037f1ba 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/python/WritePythonCode.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/python/WritePythonCode.java @@ -3,6 +3,14 @@ import com.google.common.collect.ImmutableMap; import run.mone.hive.actions.WriteCode; import run.mone.hive.common.AiTemplate; +import run.mone.hive.common.StreamingXmlParser; +import run.mone.hive.common.XmlParserCallbackAdapter; +import run.mone.hive.schema.Message; +import run.mone.hive.schema.MetaKey; +import run.mone.hive.schema.MetaValue; + +import java.util.ArrayList; +import java.util.List; /** * @author goodjava@qq.com @@ -15,16 +23,16 @@ public class WritePythonCode extends WriteCode { ${requirements} - Please provide only the function implementation without any additional explanations. Wrap the code in tags. + Please provide only the function implementation without any additional explanations. Wrap the code in tags. Here's an example of a sum function: - + def execute(params): a = params.get('a', 0) b = params.get('b', 0) return a + b - + Now, please implement the function based on the given requirements. """; @@ -35,7 +43,26 @@ public WritePythonCode() { setFunction((req, action) -> { String message = req.getMessage().getContent(); String str = AiTemplate.renderTemplate(prompt, ImmutableMap.of("requirements", message)); - return this.llm.chat(str); + List codeList = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + new StreamingXmlParser(new XmlParserCallbackAdapter() { + @Override + public void onActionStart(String type, String subType, String filePath) { + sb.setLength(0); + } + + @Override + public void onActionEnd() { + codeList.add(sb.toString()); + sb.setLength(0); + } + + @Override + public void onContentChar(char c) { + sb.append(c); + } + }).append(str); + return Message.builder().content(this.llm.chat(str)).meta(ImmutableMap.of(MetaKey.builder().key("code").build(), MetaValue.builder().value(codeList).build())).build(); }); } } \ No newline at end of file diff --git a/jcommon/hive/src/main/java/run/mone/hive/common/CustomStreamTag.java b/jcommon/hive/src/main/java/run/mone/hive/common/CustomStreamTag.java new file mode 100644 index 000000000..c0e764867 --- /dev/null +++ b/jcommon/hive/src/main/java/run/mone/hive/common/CustomStreamTag.java @@ -0,0 +1,45 @@ +package run.mone.hive.common; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author HawickMason@xiaomi.com + * @date 11/25/24 4:00 PM + */ +public enum CustomStreamTag { + + UNKNOWN(0, "unknown"), + ACTION(1, "boltAction"), + ARTIFACT(2, "boltArtifact"); + + private final int code; + private final String tagName; + + private static final Map valMap = Arrays.stream(values()).collect(Collectors.toMap(CustomStreamTag::getCode, Function.identity())); + + private static final Map nameMap = Arrays.stream(values()).collect(Collectors.toMap(CustomStreamTag::getTagName, Function.identity())); + + CustomStreamTag(int code, String tagName) { + this.code = code; + this.tagName = tagName; + } + + public int getCode() { + return code; + } + + public String getTagName() { + return tagName; + } + + public static CustomStreamTag getTagByName(String tagName) { + return nameMap.getOrDefault(tagName, UNKNOWN); + } + + public static boolean isValidTagName(String tagName) { + return !UNKNOWN.getTagName().equals(tagName) && nameMap.containsKey(tagName); + } +} diff --git a/jcommon/hive/src/main/java/run/mone/hive/common/StreamingXmlParser.java b/jcommon/hive/src/main/java/run/mone/hive/common/StreamingXmlParser.java new file mode 100644 index 000000000..d7b8800ec --- /dev/null +++ b/jcommon/hive/src/main/java/run/mone/hive/common/StreamingXmlParser.java @@ -0,0 +1,200 @@ +package run.mone.hive.common; + +import java.util.ArrayDeque; +import java.util.Deque; + +import static run.mone.hive.common.CustomStreamTag.ACTION; +import static run.mone.hive.common.CustomStreamTag.ARTIFACT; + +public class StreamingXmlParser { + private final XmlParserCallback callback; + private final StringBuilder tagBuffer; + private final StringBuilder contentBuffer; + private final Deque elementStack; + private ParserState state; + +// public StringBuilder sss = new StringBuilder(); + + private enum ParserState { + CONTENT, // 正在处理内容 + TAG_START, // 可能是标签开始 + IN_TAG // 确认在标签内 + } + + public StreamingXmlParser(XmlParserCallback callback) { + this.callback = callback; + this.tagBuffer = new StringBuilder(); + this.contentBuffer = new StringBuilder(); + this.elementStack = new ArrayDeque<>(); + this.state = ParserState.CONTENT; + } + + public void append(String text) { +// sss.append(text); + if (text == null || text.isEmpty()) { + return; + } + + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + processChar(c, i > 0 ? text.charAt(i - 1) : '\0', + i < text.length() - 1 ? text.charAt(i + 1) : '\0'); + } + } + + private void processChar(char current, char previous, char next) { + switch (state) { + case CONTENT: + if (current == '<' && isStartOfTag(next)) { + state = ParserState.TAG_START; + flushContent(); + tagBuffer.append(current); + } else { + contentBuffer.append(current); + } + break; + + case TAG_START: + tagBuffer.append(current); + if (isValidTagChar(current)) { + state = ParserState.IN_TAG; + } else { + // 不是有效的标签,回退到内容处理 + state = ParserState.CONTENT; + contentBuffer.append(tagBuffer); + tagBuffer.setLength(0); + } + break; + + case IN_TAG: + tagBuffer.append(current); + if (current == '>') { + processTag(tagBuffer.toString()); + tagBuffer.setLength(0); + state = ParserState.CONTENT; + } + break; + } + } + + private boolean isStartOfTag(char next) { + // 检查是否是有效的标签开始 + // boltArtifact 或 boltAction 或 结束标签 + return next == 'b' || next == '/'; + } + + private boolean isValidTagChar(char c) { + // 检查是否是有效的标签字符 + return Character.isLetterOrDigit(c) || c == '/' || c == '-' || c == '_' || c == '"' + || c == '=' || c == ' ' || c == '\t' || c == '\n' || c == '\r'; + } + + private void flushContent() { + if (!contentBuffer.isEmpty()) { + String content = contentBuffer.toString(); + // 逐字符发送内容 + for (char c : content.toCharArray()) { + callback.onContentChar(c); + } + contentBuffer.setLength(0); + } + } + + private void processTag(String tag) { + try { + if (tag.startsWith(" { private Role role; + private Memory memory; + private Environment env; public T getWithKey(String key) { diff --git a/jcommon/hive/src/main/java/run/mone/hive/schema/Message.java b/jcommon/hive/src/main/java/run/mone/hive/schema/Message.java index 68c404362..d86ec7a77 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/schema/Message.java +++ b/jcommon/hive/src/main/java/run/mone/hive/schema/Message.java @@ -3,10 +3,7 @@ import lombok.*; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.util.*; @Data @Builder @@ -28,6 +25,8 @@ public class Message implements Serializable { private Object data; + private Map meta = new HashMap<>(); + public Message(String content) { this(content, "user", null, null); } diff --git a/jcommon/hive/src/main/java/run/mone/hive/schema/MetaKey.java b/jcommon/hive/src/main/java/run/mone/hive/schema/MetaKey.java new file mode 100644 index 000000000..f88578a84 --- /dev/null +++ b/jcommon/hive/src/main/java/run/mone/hive/schema/MetaKey.java @@ -0,0 +1,22 @@ +package run.mone.hive.schema; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author goodjava@qq.com + * @date 2025/1/6 16:24 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class MetaKey { + + private String key; + + private String desc; + +} diff --git a/jcommon/hive/src/main/java/run/mone/hive/schema/MetaValue.java b/jcommon/hive/src/main/java/run/mone/hive/schema/MetaValue.java new file mode 100644 index 000000000..d37fe56e2 --- /dev/null +++ b/jcommon/hive/src/main/java/run/mone/hive/schema/MetaValue.java @@ -0,0 +1,23 @@ +package run.mone.hive.schema; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author goodjava@qq.com + * @date 2025/1/6 16:25 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class MetaValue { + + private Object value; + + private String desc; + + +} diff --git a/jcommon/hive/src/test/java/run/mone/hive/actions/python/ExecutePythonCodeTest.java b/jcommon/hive/src/test/java/run/mone/hive/actions/python/ExecutePythonCodeTest.java new file mode 100644 index 000000000..e3c74e363 --- /dev/null +++ b/jcommon/hive/src/test/java/run/mone/hive/actions/python/ExecutePythonCodeTest.java @@ -0,0 +1,60 @@ + +package run.mone.hive.actions.python; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import run.mone.hive.configs.LLMConfig; +import run.mone.hive.llm.LLM; +import run.mone.hive.schema.ActionReq; +import run.mone.hive.schema.Message; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ExecutePythonCodeTest { + + private ExecutePythonCode executePythonCode; + + private LLM llm; + + @BeforeEach + void setUp() { + executePythonCode = new ExecutePythonCode(); + llm = new LLM(LLMConfig.builder().build()); + executePythonCode.setLlm(llm); + } + + @Test + void testExecutePythonCode_Success() { + String code = "def add(a, b):\n return a + b\n"; + String result = executePythonCode.executePythonCode(code + "print(add(2, 3))").getContent(); + assertEquals("5", result); + } + + @Test + void testExecutePythonCode_Error() { + String code = "def divide(a, b):\n return a / b\n"; + String result = executePythonCode.executePythonCode(code + "print(divide(1, 0))").getContent(); + assertTrue(result.startsWith("")); + assertTrue(result.contains("ZeroDivisionError")); + } + + @Test + void testRun_Success() throws Exception { + ActionReq req = new ActionReq(); + req.setMessage(new Message("def add(a, b):\n return a + b")); + Message result = executePythonCode.getFunction().apply(req, executePythonCode); + System.out.println(result); + } + + @Test + void testRun_Error() throws Exception { + ActionReq req = new ActionReq(); + req.setMessage(new Message("def divide(a, b):\n return a / b")); + + Message result = executePythonCode.getFunction().apply(req, executePythonCode); + + assertTrue(result.getContent().startsWith("")); + assertTrue(result.getContent().contains("ZeroDivisionError")); + } +} diff --git a/jcommon/hive/src/test/java/run/mone/hive/common/StreamTest.java b/jcommon/hive/src/test/java/run/mone/hive/common/StreamTest.java new file mode 100644 index 000000000..c8842067a --- /dev/null +++ b/jcommon/hive/src/test/java/run/mone/hive/common/StreamTest.java @@ -0,0 +1,55 @@ +package run.mone.hive.common; + +import org.junit.jupiter.api.Test; + +/** + * @author goodjava@qq.com + * @date 2025/1/6 17:05 + */ +public class StreamTest { + + @Test + public void test1() { + String code = """ + You are a Python code generator. Your task is to write a Python function named 'execute' that takes a single parameter 'params' of type dict. The function should implement the following requirements: + + 计算一个excel有多少个工作区 + + Please provide only the function implementation without any additional explanations. Wrap the code in tags. + + Here's an example of a sum function: + + + def execute(params): + a = params.get('a', 0) + b = params.get('b', 0) + return a + b + + + Now, please implement the function based on the given requirements. + + """; + + StringBuilder sb = new StringBuilder(); + StreamingXmlParser parser = new StreamingXmlParser(new XmlParserCallbackAdapter(){ + + @Override + public void onActionStart(String type, String subType, String filePath) { + sb.setLength(0); + } + + @Override + public void onActionEnd() { + System.out.println(sb.toString()); + } + + @Override + public void onContentChar(char c) { + sb.append(c); + } + }); + parser.append(code); + + } + +} From 9d59a94dfab359d6b00bb57b8c5dd9b6909bcfb1 Mon Sep 17 00:00:00 2001 From: zhangzhiyong Date: Mon, 6 Jan 2025 19:12:59 +0800 Subject: [PATCH 3/3] chore: update FixPythonBug and FixPythonBugTest without changes --- .../hive/actions/python/FixPythonBug.java | 43 ++------- .../hive/actions/python/FixPythonBugTest.java | 95 +++++++++++++++++++ 2 files changed, 103 insertions(+), 35 deletions(-) create mode 100644 jcommon/hive/src/test/java/run/mone/hive/actions/python/FixPythonBugTest.java diff --git a/jcommon/hive/src/main/java/run/mone/hive/actions/python/FixPythonBug.java b/jcommon/hive/src/main/java/run/mone/hive/actions/python/FixPythonBug.java index 269bc14a4..e189737a5 100644 --- a/jcommon/hive/src/main/java/run/mone/hive/actions/python/FixPythonBug.java +++ b/jcommon/hive/src/main/java/run/mone/hive/actions/python/FixPythonBug.java @@ -2,6 +2,8 @@ package run.mone.hive.actions.python; import com.google.common.collect.ImmutableMap; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import run.mone.hive.actions.Action; import run.mone.hive.common.AiTemplate; import run.mone.hive.schema.Message; @@ -30,58 +32,29 @@ public FixPythonBug() { this.pythonExecutor = new PythonExecutor(); setFunction((req, action) -> { String buggyCode = req.getMessage().getContent(); + JsonObject obj = new JsonParser().parse(buggyCode).getAsJsonObject(); String fixedCode = null; try { - fixedCode = fixBug(buggyCode); + fixedCode = fixBug(obj.get("code").getAsString(), obj.get("error").getAsString()); } catch (Exception e) { throw new RuntimeException(e); } - return Message.builder().content("" + fixedCode + "").build(); + return Message.builder().content(fixedCode).build(); }); } - private String fixBug(String buggyCode) throws Exception { - String params = generateParameters(buggyCode); - String executionResult = pythonExecutor.executePythonCode(buggyCode, params); - - if (!executionResult.toLowerCase().contains("error")) { - return buggyCode; // No error, return original code - } + private String fixBug(String buggyCode, String error) throws Exception { + String renderedPrompt = AiTemplate.renderTemplate(bugFixPrompt, ImmutableMap.of( "code", buggyCode, - "error", executionResult + "error", error )); String fixedCodeWithTags = llm.chat(renderedPrompt); return extractCodeFromTags(fixedCodeWithTags); } - private String generateParameters(String code) { - // Reuse the parameter generation logic from ExecutePythonCode - String paramGenerationPrompt = """ - You are an AI assistant specialized in analyzing Python code and generating appropriate test parameters. Your task is to examine the following Python code and suggest suitable parameters for testing: - - ${code} - - Please provide a set of test parameters that will effectively exercise the main functionality of this code. Consider the following guidelines: - - 1. Identify the main function or entry point of the code. - 2. Determine the expected input types (e.g., integers, strings, lists, dictionaries). - 3. Include edge cases and typical use cases in your parameter suggestions. - 4. If the code requires specific formats (e.g., date strings, file paths), provide examples that match those formats. - 5. If the code interacts with external resources (e.g., files, APIs), suggest mock data or placeholders. - - Format your response as a Python dictionary or a list of arguments, depending on how the main function accepts input. For example: - - {"param1": value1, "param2": value2} or [arg1, arg2, arg3] - - Provide only the parameter suggestions without any additional explanation. - """; - - String promptForParams = AiTemplate.renderTemplate(paramGenerationPrompt, ImmutableMap.of("code", code)); - return llm.chat(promptForParams); - } private String extractCodeFromTags(String codeWithTags) { Pattern pattern = Pattern.compile("(.*?)", Pattern.DOTALL); diff --git a/jcommon/hive/src/test/java/run/mone/hive/actions/python/FixPythonBugTest.java b/jcommon/hive/src/test/java/run/mone/hive/actions/python/FixPythonBugTest.java new file mode 100644 index 000000000..f784eb71a --- /dev/null +++ b/jcommon/hive/src/test/java/run/mone/hive/actions/python/FixPythonBugTest.java @@ -0,0 +1,95 @@ + +package run.mone.hive.actions.python; + +import com.google.gson.JsonObject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import run.mone.hive.configs.LLMConfig; +import run.mone.hive.llm.LLM; +import run.mone.hive.roles.Role; +import run.mone.hive.schema.ActionReq; +import run.mone.hive.schema.Message; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.*; + +class FixPythonBugTest { + + private FixPythonBug fixPythonBug; + private LLM llm; + + @BeforeEach + void setUp() { + llm = new LLM(LLMConfig.builder().json(false).build()); + fixPythonBug = new FixPythonBug(); + fixPythonBug.setLlm(llm); + } + + @Test + void testFixBugWithSyntaxError() throws ExecutionException, InterruptedException { + String buggyCode = """ + def add_numbers(a, b) + return a + b + + result = add_numbers(3, 4) + print(result) + """; + + ActionReq req = new ActionReq(); + req.setMessage(new Message(buggyCode)); + + CompletableFuture future = fixPythonBug.run(req); + Message result = future.get(); + + assertNotNull(result); + } + + @Test + void testFixBugWithLogicError() throws ExecutionException, InterruptedException { + String buggyCode = """ + def divide_numbers(a, b): + return a - b # Bug: subtraction instead of division + + result = divide_numbers(10, 2) + print(result) + """; + + JsonObject jo = new JsonObject(); + jo.addProperty("code",buggyCode); + jo.addProperty("error","计算出来的数字不对,帮我看看问题"); + + ActionReq req = new ActionReq(); + req.setMessage(new Message(jo.toString())); + req.setRole(Role.builder().name("user").build()); + + CompletableFuture future = fixPythonBug.run(req); + Message result = future.get(); + + assertNotNull(result); + assertTrue(result.getContent().contains("return a / b")); + } + + @Test + void testFixBugWithNoError() throws ExecutionException, InterruptedException { + String correctCode = """ + def multiply_numbers(a, b): + return a * b + + result = multiply_numbers(3, 4) + print(result) + """; + + ActionReq req = new ActionReq(); + req.setMessage(new Message(correctCode)); + + CompletableFuture future = fixPythonBug.run(req); + Message result = future.get(); + + assertNotNull(result); + assertEquals(correctCode, result.getContent()); + } + + // ... existing code ... +}