Skip to content

Commit f1e7593

Browse files
authored
Merge pull request #118 from EuniAI/dev
Implement Customize Build Commands and Test Commands
2 parents 7c8992e + 87e527c commit f1e7593

35 files changed

Lines changed: 439 additions & 213 deletions

docs/GitHub-Issue-Debug-Guide.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ After execution, the script outputs results in JSON format, including the follow
161161
"prometheus_result": {
162162
"patch": "Generated code patch",
163163
"passed_reproducing_test": true,
164-
"passed_build": true,
165164
"passed_existing_test": false,
166165
"passed_regression_test": true,
167166
"passed_reproduction_test": true,

prometheus/app/api/routes/issue.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,18 @@ async def answer_issue(issue: IssueRequest, request: Request) -> Response[IssueR
6060
code=400,
6161
message="workdir must be provided for user defined environment",
6262
)
63+
64+
# Validate build and test commands if required
65+
if issue.run_build and not issue.build_commands:
66+
raise ServerException(
67+
code=400, message="No build commands available, please provide build commands"
68+
)
69+
70+
if issue.run_existing_test and not issue.test_commands:
71+
raise ServerException(
72+
code=400, message="No test commands available, please provide test commands"
73+
)
74+
6375
# Ensure the repository is not currently being used
6476
if repository.is_working:
6577
raise ServerException(
@@ -83,7 +95,6 @@ async def answer_issue(issue: IssueRequest, request: Request) -> Response[IssueR
8395
(
8496
patch,
8597
passed_reproducing_test,
86-
passed_build,
8798
passed_regression_test,
8899
passed_existing_test,
89100
issue_response,
@@ -115,12 +126,11 @@ async def answer_issue(issue: IssueRequest, request: Request) -> Response[IssueR
115126
if (
116127
patch,
117128
passed_reproducing_test,
118-
passed_build,
119129
passed_regression_test,
120130
passed_existing_test,
121131
issue_response,
122132
issue_type,
123-
) == (None, False, False, False, False, None, None):
133+
) == (None, False, False, False, None, None):
124134
raise ServerException(
125135
code=500,
126136
message="Failed to process the issue. Please try again later.",
@@ -135,7 +145,6 @@ async def answer_issue(issue: IssueRequest, request: Request) -> Response[IssueR
135145
data=IssueResponse(
136146
patch=patch,
137147
passed_reproducing_test=passed_reproducing_test,
138-
passed_build=passed_build,
139148
passed_regression_test=passed_regression_test,
140149
passed_existing_test=passed_existing_test,
141150
issue_response=issue_response,

prometheus/app/models/response/issue.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
class IssueResponse(BaseModel):
77
patch: str | None = None
88
passed_reproducing_test: bool
9-
passed_build: bool
109
passed_regression_test: bool
1110
passed_existing_test: bool
1211
issue_response: str | None = None

prometheus/app/services/issue_service.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,7 @@ def answer_issue(
4848
dockerfile_content: Optional[str] = None,
4949
image_name: Optional[str] = None,
5050
workdir: Optional[str] = None,
51-
) -> (
52-
tuple[None, bool, bool, bool, bool, None, None]
53-
| tuple[str, bool, bool, bool, bool, str, IssueType]
54-
):
51+
) -> tuple[None, bool, bool, bool, None, None] | tuple[str, bool, bool, bool, str, IssueType]:
5552
"""
5653
Processes an issue, generates patches if needed, runs optional builds and tests, and returning the results.
5754
@@ -76,9 +73,10 @@ def answer_issue(
7673
Tuple containing:
7774
- edit_patch (str): The generated patch for the issue.
7875
- passed_reproducing_test (bool): Whether the reproducing test passed.
79-
- passed_build (bool): Whether the build passed.
76+
- passed_regression_test (bool): Whether the regression tests passed.
8077
- passed_existing_test (bool): Whether the existing tests passed.
8178
- issue_response (str): Response generated for the issue.
79+
- issue_type (IssueType): The type of the issue (BUG or QUESTION).
8280
"""
8381

8482
# Set up a dedicated logger for this thread
@@ -111,7 +109,6 @@ def answer_issue(
111109
kg=knowledge_graph,
112110
git_repo=repository,
113111
container=container,
114-
build_commands=build_commands,
115112
test_commands=test_commands,
116113
)
117114

@@ -131,15 +128,14 @@ def answer_issue(
131128
return (
132129
output_state["edit_patch"],
133130
output_state["passed_reproducing_test"],
134-
output_state["passed_build"],
135131
output_state["passed_regression_test"],
136132
output_state["passed_existing_test"],
137133
output_state["issue_response"],
138134
output_state["issue_type"],
139135
)
140136
except Exception as e:
141137
logger.error(f"Error in answer_issue: {str(e)}\n{traceback.format_exc()}")
142-
return None, False, False, False, False, None, None
138+
return None, False, False, False, None, None
143139
finally:
144140
logger.removeHandler(file_handler)
145141
file_handler.close()

prometheus/docker/base_container.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ def update_files(
9999
Creates a tar archive of the new files and copies them into the workdir of the container.
100100
101101
Args:
102-
new_project_path: Path to the directory containing new files.
102+
project_root_path: Path to the project root directory.
103+
updated_files: List of file paths (relative to project_root_path) to update in the container.
104+
removed_files: List of file paths (relative to project_root_path) to remove from the container.
103105
"""
104106
if not project_root_path.is_absolute():
105107
raise ValueError("project_root_path {project_root_path} must be a absolute path")
@@ -157,10 +159,10 @@ def execute_command(self, command: str) -> str:
157159
{command} timeout after {self.timeout} seconds
158160
*******************************************************************************
159161
"""
160-
timeout_command = f"timeout -k 5 {self.timeout}s {command}"
161-
command = f'/bin/bash -l -c "{timeout_command}"'
162+
bash_cmd = ["/bin/bash", "-lc", command]
163+
full_cmd = ["timeout", "-k", "5", f"{self.timeout}s", *bash_cmd]
162164
self._logger.debug(f"Running command in container: {command}")
163-
exec_result = self.container.exec_run(command, workdir=self.workdir)
165+
exec_result = self.container.exec_run(full_cmd, workdir=self.workdir)
164166
exec_result_str = exec_result.output.decode("utf-8")
165167

166168
if exec_result.exit_code in (124, 137):
@@ -169,13 +171,11 @@ def execute_command(self, command: str) -> str:
169171
self._logger.debug(f"Command output:\n{exec_result_str}")
170172
return exec_result_str
171173

172-
def restart_container(self):
173-
self._logger.info("Restarting the container")
174-
if self.container:
175-
self.container.stop(timeout=10)
176-
self.container.remove(force=True)
177-
178-
self.start_container()
174+
def reset_repository(self):
175+
"""Reset the git repository in the container to a clean state."""
176+
self._logger.info("Resetting git repository in the container")
177+
self.execute_command("git reset --hard")
178+
self.execute_command("git clean -fd")
179179

180180
def cleanup(self):
181181
"""Clean up container resources and temporary files.

prometheus/lang_graph/graphs/issue_graph.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ def __init__(
3030
kg: KnowledgeGraph,
3131
git_repo: GitRepository,
3232
container: BaseContainer,
33-
build_commands: Optional[Sequence[str]] = None,
3433
test_commands: Optional[Sequence[str]] = None,
3534
):
3635
self.git_repo = git_repo
@@ -52,7 +51,6 @@ def __init__(
5251
container=container,
5352
kg=kg,
5453
git_repo=git_repo,
55-
build_commands=build_commands,
5654
test_commands=test_commands,
5755
)
5856

prometheus/lang_graph/graphs/issue_state.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ class IssueState(TypedDict):
2626

2727
passed_regression_test: bool
2828
passed_reproducing_test: bool
29-
passed_build: bool
3029
passed_existing_test: bool
3130

3231
issue_response: str

prometheus/lang_graph/nodes/context_extraction_node.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,16 @@
6767

6868
HUMAN_MESSAGE = """\
6969
This is the original user query:
70+
71+
--- BEGIN ORIGINAL QUERY ---
7072
{original_query}
73+
--- END ORIGINAL QUERY ---
7174
7275
The context or file content that you have seen so far (Some of the context may be IRRELEVANT to the query!!!):
76+
77+
--- BEGIN CONTEXT ---
7378
{context}
79+
--- END CONTEXT ---
7480
7581
REMEMBER: Your task is to summarize the relevant contexts to a given query and return it in the specified format!
7682
"""
@@ -112,16 +118,6 @@ def __init__(self, model: BaseChatModel, root_path: str):
112118
f"thread-{threading.get_ident()}.prometheus.lang_graph.nodes.context_extraction_node"
113119
)
114120

115-
def get_human_message(self, state: ContextRetrievalState) -> str:
116-
full_context_str = transform_tool_messages_to_str(
117-
extract_last_tool_messages(state["context_provider_messages"])
118-
)
119-
original_query = state["query"]
120-
return HUMAN_MESSAGE.format(
121-
original_query=original_query,
122-
context=full_context_str,
123-
)
124-
125121
def __call__(self, state: ContextRetrievalState):
126122
"""
127123
Extract relevant code contexts from the codebase based on the user query and existing context.
@@ -130,9 +126,26 @@ def __call__(self, state: ContextRetrievalState):
130126
self._logger.info("Starting context extraction process")
131127
# Get Context List with existing context
132128
final_context = state.get("context", [])
133-
# Get a human message
134-
human_message = self.get_human_message(state)
129+
130+
# Transform the tool messages to a single string
131+
full_context_str = transform_tool_messages_to_str(
132+
extract_last_tool_messages(state["context_provider_messages"])
133+
)
134+
135+
# return existing context if no new context is available
136+
if not full_context_str:
137+
self._logger.debug(
138+
"No context available from tool messages, returning existing context"
139+
)
140+
return {"context": final_context}
141+
142+
# Format the human message
143+
human_message = HUMAN_MESSAGE.format(
144+
original_query=state["query"],
145+
context=full_context_str,
146+
)
135147
self._logger.debug(human_message)
148+
136149
# Summarize the context based on the last messages and system prompt
137150
response = self.model.invoke({"human_prompt": human_message})
138151
self._logger.debug(f"Model response: {response}")

prometheus/lang_graph/nodes/context_retrieval_subgraph_node.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Dict, Sequence
44

55
from langchain_core.language_models.chat_models import BaseChatModel
6+
from langgraph.errors import GraphRecursionError
67

78
from prometheus.graph.knowledge_graph import KnowledgeGraph
89
from prometheus.lang_graph.subgraphs.context_retrieval_subgraph import ContextRetrievalSubgraph
@@ -31,8 +32,12 @@ def __init__(
3132

3233
def __call__(self, state: Dict) -> Dict[str, Sequence[Context]]:
3334
self._logger.info("Enter context retrieval subgraph")
34-
output_state = self.context_retrieval_subgraph.invoke(
35-
state[self.query_key_name], state["max_refined_query_loop"]
36-
)
35+
try:
36+
output_state = self.context_retrieval_subgraph.invoke(
37+
state[self.query_key_name], state["max_refined_query_loop"]
38+
)
39+
except GraphRecursionError as e:
40+
self._logger.debug("Graph recursion limit reached, returning empty context")
41+
raise e
3742
self._logger.info(f"Context retrieved: {output_state['context']}")
3843
return {self.context_key_name: output_state["context"]}

prometheus/lang_graph/nodes/issue_bug_analyzer_message_node.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ def format_human_message(self, state: Dict):
8282
)
8383
elif "reproducing_test_fail_log" in state and state["reproducing_test_fail_log"]:
8484
edit_error = f"The patch failed to pass the bug exposing test cases:\n{state['reproducing_test_fail_log']}"
85-
elif "build_fail_log" in state and state["build_fail_log"]:
86-
edit_error = f"The patch failed to pass the build:\n{state['build_fail_log']}"
8785
elif "existing_test_fail_log" in state and state["existing_test_fail_log"]:
8886
edit_error = (
8987
f"The patch failed to existing test cases:\n{state['existing_test_fail_log']}"

0 commit comments

Comments
 (0)