bioguider 0.2.52__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. bioguider/__init__.py +0 -0
  2. bioguider/agents/__init__.py +0 -0
  3. bioguider/agents/agent_task.py +92 -0
  4. bioguider/agents/agent_tools.py +176 -0
  5. bioguider/agents/agent_utils.py +504 -0
  6. bioguider/agents/collection_execute_step.py +182 -0
  7. bioguider/agents/collection_observe_step.py +125 -0
  8. bioguider/agents/collection_plan_step.py +156 -0
  9. bioguider/agents/collection_task.py +184 -0
  10. bioguider/agents/collection_task_utils.py +142 -0
  11. bioguider/agents/common_agent.py +137 -0
  12. bioguider/agents/common_agent_2step.py +215 -0
  13. bioguider/agents/common_conversation.py +61 -0
  14. bioguider/agents/common_step.py +85 -0
  15. bioguider/agents/consistency_collection_step.py +102 -0
  16. bioguider/agents/consistency_evaluation_task.py +57 -0
  17. bioguider/agents/consistency_evaluation_task_utils.py +14 -0
  18. bioguider/agents/consistency_observe_step.py +110 -0
  19. bioguider/agents/consistency_query_step.py +77 -0
  20. bioguider/agents/dockergeneration_execute_step.py +186 -0
  21. bioguider/agents/dockergeneration_observe_step.py +154 -0
  22. bioguider/agents/dockergeneration_plan_step.py +158 -0
  23. bioguider/agents/dockergeneration_task.py +158 -0
  24. bioguider/agents/dockergeneration_task_utils.py +220 -0
  25. bioguider/agents/evaluation_installation_task.py +270 -0
  26. bioguider/agents/evaluation_readme_task.py +767 -0
  27. bioguider/agents/evaluation_submission_requirements_task.py +172 -0
  28. bioguider/agents/evaluation_task.py +206 -0
  29. bioguider/agents/evaluation_tutorial_task.py +169 -0
  30. bioguider/agents/evaluation_tutorial_task_prompts.py +187 -0
  31. bioguider/agents/evaluation_userguide_prompts.py +179 -0
  32. bioguider/agents/evaluation_userguide_task.py +154 -0
  33. bioguider/agents/evaluation_utils.py +127 -0
  34. bioguider/agents/identification_execute_step.py +181 -0
  35. bioguider/agents/identification_observe_step.py +104 -0
  36. bioguider/agents/identification_plan_step.py +140 -0
  37. bioguider/agents/identification_task.py +270 -0
  38. bioguider/agents/identification_task_utils.py +22 -0
  39. bioguider/agents/peo_common_step.py +64 -0
  40. bioguider/agents/prompt_utils.py +253 -0
  41. bioguider/agents/python_ast_repl_tool.py +69 -0
  42. bioguider/agents/rag_collection_task.py +130 -0
  43. bioguider/conversation.py +67 -0
  44. bioguider/database/code_structure_db.py +500 -0
  45. bioguider/database/summarized_file_db.py +146 -0
  46. bioguider/generation/__init__.py +39 -0
  47. bioguider/generation/benchmark_metrics.py +610 -0
  48. bioguider/generation/change_planner.py +189 -0
  49. bioguider/generation/document_renderer.py +157 -0
  50. bioguider/generation/llm_cleaner.py +67 -0
  51. bioguider/generation/llm_content_generator.py +1128 -0
  52. bioguider/generation/llm_injector.py +809 -0
  53. bioguider/generation/models.py +85 -0
  54. bioguider/generation/output_manager.py +74 -0
  55. bioguider/generation/repo_reader.py +37 -0
  56. bioguider/generation/report_loader.py +166 -0
  57. bioguider/generation/style_analyzer.py +36 -0
  58. bioguider/generation/suggestion_extractor.py +436 -0
  59. bioguider/generation/test_metrics.py +189 -0
  60. bioguider/managers/benchmark_manager.py +785 -0
  61. bioguider/managers/evaluation_manager.py +215 -0
  62. bioguider/managers/generation_manager.py +686 -0
  63. bioguider/managers/generation_test_manager.py +107 -0
  64. bioguider/managers/generation_test_manager_v2.py +525 -0
  65. bioguider/rag/__init__.py +0 -0
  66. bioguider/rag/config.py +117 -0
  67. bioguider/rag/data_pipeline.py +651 -0
  68. bioguider/rag/embedder.py +24 -0
  69. bioguider/rag/rag.py +138 -0
  70. bioguider/settings.py +103 -0
  71. bioguider/utils/code_structure_builder.py +59 -0
  72. bioguider/utils/constants.py +135 -0
  73. bioguider/utils/default.gitignore +140 -0
  74. bioguider/utils/file_utils.py +215 -0
  75. bioguider/utils/gitignore_checker.py +175 -0
  76. bioguider/utils/notebook_utils.py +117 -0
  77. bioguider/utils/pyphen_utils.py +73 -0
  78. bioguider/utils/python_file_handler.py +65 -0
  79. bioguider/utils/r_file_handler.py +551 -0
  80. bioguider/utils/utils.py +163 -0
  81. bioguider-0.2.52.dist-info/LICENSE +21 -0
  82. bioguider-0.2.52.dist-info/METADATA +51 -0
  83. bioguider-0.2.52.dist-info/RECORD +84 -0
  84. bioguider-0.2.52.dist-info/WHEEL +4 -0
@@ -0,0 +1,110 @@
1
+
2
+
3
+ from langchain.prompts import ChatPromptTemplate
4
+ from langchain_openai.chat_models.base import BaseChatOpenAI
5
+ from pydantic import BaseModel, Field
6
+ from bioguider.agents.common_agent_2step import CommonAgentTwoSteps
7
+ from bioguider.agents.consistency_evaluation_task_utils import ConsistencyEvaluationState
8
+ from bioguider.agents.peo_common_step import PEOCommonStep
9
+
10
+ CONSISTENCY_OBSERVE_SYSTEM_PROMPT = """
11
+ You are an expert developer specializing in the biomedical domain.
12
+ Your task is to analyze both:
13
+ 1. the provided file related to {domain} documentation,
14
+ 2. the code definitions related to the {domain} documentation
15
+ and generate a structured consistency assessment based on the following criteria.
16
+
17
+ ---
18
+
19
+ ### **Evaluation Criteria**
20
+
21
+ **Consistency**:
22
+ * **Score**: [a number between 0 and 100 representing the consistency quality rating.]
23
+ * **Assessment**: [Your evaluation of whether the {domain} documentation is consistent with the code definitions]
24
+ * **Development**: [A list of inconsistent function/class/method name and inconsistent docstring, and describe how they are inconsistent, please be as specific as possible]
25
+ * **Strengths**: [A list of strengths of the {domain} documentation on consistency]
26
+
27
+ ---
28
+
29
+ ### **Output Format**
30
+ Your output **must exactly match** the following format:
31
+ ```
32
+ **Consistency**:
33
+ * **Score**: [a number between 0 and 100 representing the consistency quality rating.]
34
+ * **Assessment**: [Your evaluation of whether the {domain} documentation is consistent with the code definitions]
35
+ * **Development**: [A list of inconsistent function/class/method name and inconsistent docstring, and describe how they are inconsistent, please be as specific as possible]
36
+ * **Strengths**: [A list of strengths of the {domain} documentation on consistency]
37
+ ```
38
+
39
+ ### **Output Example**
40
+
41
+ ```
42
+ **Consistency**:
43
+ * **Score**: [a number between 0 and 100 representing the consistency quality rating.]
44
+ * **Assessment**: [Your evaluation of whether the {domain} documentation is consistent with the code definitions]
45
+ * **Development**:
46
+ - Inconsistent function/class/method name 1
47
+ - Inconsistent docstring 1
48
+ - Inconsistent function/class/method name 2
49
+ - Inconsistent docstring 2
50
+ - ...
51
+ * **Strengths**:
52
+ - Strengths 1
53
+ - Strengths 2
54
+ - ...
55
+ ```
56
+
57
+ ---
58
+
59
+ ### **Input {domain} Documentation**
60
+ {documentation}
61
+
62
+ ### **Code Definitions**
63
+ {code_definitions}
64
+
65
+
66
+ """
67
+
68
+ class ConsistencyEvaluationObserveResult(BaseModel):
69
+ consistency_score: int=Field(description="A number between 0 and 100 representing the consistency quality rating.")
70
+ consistency_assessment: str=Field(description="Your evaluation of whether the documentation is consistent with the code definitions")
71
+ consistency_development: list[str]=Field(description="A list of inconsistent function/class/method name and inconsistent docstring")
72
+ consistency_strengths: list[str]=Field(description="A list of strengths of the documentation on consistency")
73
+
74
+
75
+ class ConsistencyObserveStep(PEOCommonStep):
76
+ def __init__(self, llm: BaseChatOpenAI):
77
+ super().__init__(llm)
78
+ self.step_name = "Consistency Observe Step"
79
+
80
+ def _prepare_system_prompt(self, state: ConsistencyEvaluationState):
81
+ all_query_rows = state["all_query_rows"]
82
+ documentation = state["documentation"]
83
+ domain = state["domain"]
84
+ code_definition = ""
85
+ for row in all_query_rows:
86
+ content = f"name: {row['name']}\nfile_path: {row['path']}\nparent: {row['parent']}\nparameters: {row['params']}\ndoc_string: {row['doc_string']}"
87
+ code_definition += content
88
+ code_definition += "\n\n\n"
89
+ return ChatPromptTemplate.from_template(CONSISTENCY_OBSERVE_SYSTEM_PROMPT).format(
90
+ code_definitions=code_definition,
91
+ documentation=documentation,
92
+ domain=domain,
93
+ )
94
+
95
+ def _execute_directly(self, state: ConsistencyEvaluationState):
96
+ system_prompt = self._prepare_system_prompt(state)
97
+ agent = CommonAgentTwoSteps(llm=self.llm)
98
+ res, _, token_usage, reasoning_process = agent.go(
99
+ system_prompt=system_prompt,
100
+ instruction_prompt="Now, let's begin the consistency evaluation step.",
101
+ schema=ConsistencyEvaluationObserveResult,
102
+ )
103
+ res: ConsistencyEvaluationObserveResult = res
104
+ state["consistency_score"] = res.consistency_score
105
+ state["consistency_assessment"] = res.consistency_assessment
106
+ state["consistency_development"] = res.consistency_development
107
+ state["consistency_strengths"] = res.consistency_strengths
108
+ return state, token_usage
109
+
110
+
@@ -0,0 +1,77 @@
1
+
2
+
3
+ from bioguider.agents.common_step import CommonStep
4
+ from bioguider.agents.consistency_evaluation_task_utils import ConsistencyEvaluationState
5
+ from bioguider.database.code_structure_db import CodeStructureDb
6
+ from bioguider.utils.constants import DEFAULT_TOKEN_USAGE
7
+
8
+
9
+ class ConsistencyQueryStep(CommonStep):
10
+ def __init__(self, code_structure_db: CodeStructureDb):
11
+ super().__init__()
12
+ self.step_name = "Consistency Query Step"
13
+ self.code_structure_db = code_structure_db
14
+
15
+ def _execute_directly(self, state: ConsistencyEvaluationState):
16
+ functions_and_classes = state["functions_and_classes"]
17
+ all_rows: list[any] = []
18
+ for function_or_class in functions_and_classes:
19
+ function_or_class_name = function_or_class["name"] if "name" in function_or_class else "N/A"
20
+ function_or_class_file_path = function_or_class["file_path"] if "file_path" in function_or_class else "N/A"
21
+ function_or_class_parameters = function_or_class["parameters"] if "parameters" in function_or_class else "N/A"
22
+ function_or_class_parent = function_or_class["parent"] if "parent" in function_or_class else "N/A"
23
+ self._print_step(state, step_output=(
24
+ f"Consistency Query Step: \n{function_or_class_name},\n"
25
+ f" {function_or_class_file_path},\n"
26
+ f" {function_or_class_parameters},\n"
27
+ f" {function_or_class_parent}"
28
+ ))
29
+ file_path = None
30
+ parent = None
31
+ name = None
32
+ if "file_path" in function_or_class and function_or_class["file_path"] != "N/A":
33
+ file_path = function_or_class["file_path"]
34
+ if "parent" in function_or_class and function_or_class["parent"] != "N/A":
35
+ parent = function_or_class["parent"]
36
+ if "name" in function_or_class and function_or_class["name"] != "N/A":
37
+ name = function_or_class["name"]
38
+
39
+ rows: list[any] | None = None
40
+ if name is None:
41
+ if file_path is not None:
42
+ rows = self.code_structure_db.select_by_path(file_path)
43
+ elif parent is not None:
44
+ rows = self.code_structure_db.select_by_parent(parent)
45
+ else:
46
+ if file_path is not None and parent is not None:
47
+ rows = self.code_structure_db.select_by_name_and_parent_and_path(name, parent, file_path)
48
+ rows = rows if rows is None else [rows]
49
+ if rows is None or len(rows) == 0:
50
+ rows = self.code_structure_db.select_by_name_and_path(name, file_path)
51
+ rows = rows if rows is None else [rows]
52
+ if rows is None or len(rows) == 0:
53
+ rows = self.code_structure_db.select_by_name_and_parent(name, parent)
54
+ if rows is None or len(rows) == 0:
55
+ rows = self.code_structure_db.select_by_name(name)
56
+ elif file_path is not None:
57
+ rows = self.code_structure_db.select_by_name_and_path(name, file_path)
58
+ rows = rows if rows is None else [rows]
59
+ if rows is None or len(rows) == 0:
60
+ rows = self.code_structure_db.select_by_name(name)
61
+ elif parent is not None:
62
+ rows = self.code_structure_db.select_by_name_and_parent(name, parent)
63
+ if rows is None or len(rows) == 0:
64
+ rows = self.code_structure_db.select_by_name(name)
65
+ else:
66
+ rows = self.code_structure_db.select_by_name(name)
67
+ if rows is None or len(rows) == 0:
68
+ self._print_step(state, step_output=f"No such function or class {name}")
69
+ continue
70
+ all_rows.extend(rows)
71
+
72
+ state["all_query_rows"] = all_rows
73
+
74
+ return state, {**DEFAULT_TOKEN_USAGE}
75
+
76
+
77
+
@@ -0,0 +1,186 @@
1
+
2
+ import logging
3
+ from langchain_openai.chat_models.base import BaseChatOpenAI
4
+ from langchain.tools import BaseTool
5
+ from langchain.agents import create_react_agent, AgentExecutor
6
+ from langchain_community.callbacks.openai_info import OpenAICallbackHandler
7
+
8
+ from bioguider.utils.constants import DEFAULT_TOKEN_USAGE
9
+ from bioguider.agents.agent_utils import (
10
+ CustomPromptTemplate,
11
+ CustomOutputParser,
12
+ )
13
+ from bioguider.agents.peo_common_step import PEOCommonStep
14
+ from bioguider.agents.dockergeneration_task_utils import (
15
+ DockerGenerationWorkflowState,
16
+ generate_Dockerfile_tool,
17
+ )
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ DOCKERGENERATION_EXECUTION_SYSTEM_PROMPT = """You are an expert in software containerization and reproducibility engineering.
22
+ You are given a **plan** and must complete it strictly using Python code and the available tools.
23
+
24
+ ---
25
+ ### **Available Tools**
26
+ {tools}
27
+
28
+ ---
29
+ ### **Your Task**
30
+ Follow the given plan step by step using the exact format below:
31
+
32
+ ```
33
+ Thought: Describe what you are thinking or planning to do next.
34
+ Action: The tool you are going to use (must be one of: {tool_names})
35
+ Action Input: The input to the selected action
36
+ Observation: The result returned by the action
37
+ ```
38
+
39
+ You may repeat the **Thought → Action → Action Input → Observation** loop as needed.
40
+
41
+ Once all steps in the plan have been executed, end the loop and output all the results and generated Dockerfile using this format:
42
+
43
+ ```
44
+ Thought: I have completed the plan.
45
+ Final Answer:
46
+ Action: {{tool_name}}
47
+ Action Input: {{file_name1}}
48
+ Action Observation: {{Observation1}}
49
+ ---
50
+ Action: {{tool_name}}
51
+ Action Input: {{file_name2}}
52
+ Action Observation: {{Observation2}}
53
+ ---
54
+ **Dockerfile file name**: {{docker file path}}
55
+ ...
56
+ ```
57
+
58
+ ---
59
+
60
+ ### **Important Notes**
61
+
62
+ - You must strictly follow the provided plan.
63
+ - **Do not take any additional or alternative actions**, even if:
64
+ - No relevant result is found
65
+ - The file content is missing, empty, or irrelevant
66
+ - If no information is found in a step, simply proceed to the next action in the plan without improvising.
67
+ - Only use the tools specified in the plan actions. No independent decisions or extra steps are allowed.
68
+ ---
69
+
70
+ ### **Plan**
71
+ {plan_actions}
72
+
73
+ ### **Plan Thoughts**
74
+ {plan_thoughts}
75
+
76
+ ### **Actions Already Taken**
77
+ {agent_scratchpad}
78
+
79
+ ---
80
+
81
+ {input}
82
+
83
+ ---
84
+ """
85
+
86
+ class DockerGenerationExecuteStep(PEOCommonStep):
87
+ def __init__(
88
+ self,
89
+ llm: BaseChatOpenAI,
90
+ repo_path: str,
91
+ repo_structure: str,
92
+ gitignore_path: str,
93
+ custom_tools: list[BaseTool] | None = None,
94
+ ):
95
+ super().__init__(llm)
96
+ self.step_name = "Docker Generation Execute Step"
97
+ self.repo_path = repo_path
98
+ self.repo_structure = repo_structure
99
+ self.gitignore_path = gitignore_path
100
+ self.custom_tools = custom_tools if custom_tools is not None else []
101
+ self.generate_tool: generate_Dockerfile_tool | None = None
102
+
103
+ def set_generate_Dockerfile_tool(self, tool: generate_Dockerfile_tool):
104
+ self.generate_tool = tool
105
+
106
+ def _execute_directly(self, state: DockerGenerationWorkflowState):
107
+ plan_actions = state["plan_actions"]
108
+ plan_thoughts = state["plan_thoughts"]
109
+ step_output = state["step_output"] if "step_output" in state and \
110
+ state["step_output"] is not None else "N/A"
111
+ step_dockerfile_content = state["step_dockerfile_content"] if "step_dockerfile_content" in state and \
112
+ state["step_dockerfile_content"] is not None else "N/A"
113
+ self.generate_tool.set_intermediate_output(
114
+ plan_thoughts=plan_thoughts,
115
+ step_error=step_output,
116
+ step_dockerfile_content=step_dockerfile_content,
117
+ )
118
+ prompt = CustomPromptTemplate(
119
+ template=DOCKERGENERATION_EXECUTION_SYSTEM_PROMPT,
120
+ tools=self.custom_tools,
121
+ plan_actions=plan_actions,
122
+ input_variables=[
123
+ "tools", "tool_names", "agent_scratchpad",
124
+ "intermediate_steps", "plan_actions", "plan_thoughts",
125
+ ],
126
+ )
127
+ output_parser = CustomOutputParser()
128
+ agent = create_react_agent(
129
+ llm = self.llm,
130
+ tools = self.custom_tools,
131
+ prompt = prompt,
132
+ output_parser=output_parser,
133
+ stop_sequence=["\nObservation:"],
134
+ )
135
+ callback_handler = OpenAICallbackHandler()
136
+ agent_executor = AgentExecutor(
137
+ agent=agent,
138
+ tools=self.custom_tools,
139
+ max_iterations=10,
140
+ )
141
+ response = agent_executor.invoke(
142
+ input={
143
+ "plan_actions": plan_actions,
144
+ "plan_thoughts": plan_thoughts,
145
+ "input": "Now, let's begin."
146
+ },
147
+ config={
148
+ "callbacks": [callback_handler],
149
+ "recursion_limit": 20,
150
+ }
151
+ )
152
+ if "output" in response:
153
+ output = response["output"]
154
+ self._print_step(state, step_output=f"**Execute Output:** \n{output}")
155
+ if "**Final Answer**" in output:
156
+ final_answer = output.split("**Final Answer:**")[-1].strip().strip(":")
157
+ step_output = final_answer
158
+ elif "Final Answer" in output:
159
+ final_answer = output.split("Final Answer")[-1].strip().strip(":")
160
+ step_output = final_answer
161
+ else:
162
+ step_output = output
163
+ self._print_step(state, step_output=step_output)
164
+ state["step_output"] = step_output
165
+ if "**Dockerfile file name**" in step_output:
166
+ dockerfile: str = step_output.split("**Dockerfile file name**")[-1]
167
+ dockerfile = dockerfile.strip().strip(":")
168
+ dockerfile = dockerfile.strip("```").strip()
169
+ state["dockerfile"] = dockerfile
170
+ else:
171
+ state["dockerfile"] = None
172
+ # state["dockerfile"] = f"demo-bioguider-{docker_id}.Dockerfile"
173
+ else:
174
+ logger.error("No output found in the response.")
175
+ self._print_step(
176
+ state,
177
+ step_output="Error: No output found in the response.",
178
+ )
179
+ state["step_output"] = "Error: No output found in the response."
180
+
181
+
182
+ token_usage = vars(callback_handler)
183
+ token_usage = {**DEFAULT_TOKEN_USAGE, **token_usage}
184
+
185
+ return state, token_usage
186
+
@@ -0,0 +1,154 @@
1
+
2
+ import os
3
+ from langchain.prompts import ChatPromptTemplate
4
+ from pydantic import BaseModel, Field
5
+
6
+ from bioguider.utils.constants import DEFAULT_TOKEN_USAGE
7
+ from bioguider.agents.agent_utils import read_file
8
+ from bioguider.utils.utils import run_command
9
+ from bioguider.agents.dockergeneration_task_utils import DockerGenerationWorkflowState
10
+ from bioguider.agents.common_agent_2step import CommonAgentTwoChainSteps, CommonAgentTwoSteps
11
+ from bioguider.agents.peo_common_step import PEOCommonStep
12
+
13
+ DOCKERGENERATION_OBSERVE_SYSTEM_PROMPT = """You are an expert in software containerization and reproducibility engineering.
14
+ We have a generated **Dockerfile**, here is its content:
15
+ {dockerfile_content}
16
+
17
+ Here is the output of docker image building with command "docker build":
18
+ ```{docker_build_output}```
19
+
20
+ Here is the output of running docker image with command "docker run":
21
+ ```{docker_run_output}```
22
+
23
+ ### **Instructions**
24
+ 1. Carefully review **Dockerfile**, output of building docker image and output of running docker image, give your
25
+ thoughts and advice as the following format:
26
+ ```
27
+ **Thoughts**: you thoughts here
28
+ ```
29
+ 2. Be precise and support your reasoning with evidence from the input.
30
+
31
+ ### **Notes**
32
+ - We are generating Dockerfile over multiple rounds, your thoughts and the output of this step will be persisted,
33
+ we'll continue with the next round accordingly
34
+ """
35
+
36
+ class DockerGenerationObserveResult(BaseModel):
37
+ thoughts: str = Field(description="thoughts on input")
38
+
39
+ MAX_TIMEOUT = 900 # 15 mins
40
+ MAX_ERROR_OUTPTU_LENGTH = 2048 # 2k
41
+ class DockerGenerationObserveStep(PEOCommonStep):
42
+ def __init__(self, llm, repo_path: str):
43
+ super().__init__(llm)
44
+ self.step_name = "Docker Generation Observe"
45
+ self.repo_path = repo_path
46
+
47
+ def _build_system_prompt(
48
+ self,
49
+ state: DockerGenerationWorkflowState,
50
+ build_error: str,
51
+ run_error: str,
52
+ ):
53
+ dockerfile=state["dockerfile"]
54
+ dockerfile_path = os.path.join(self.repo_path, dockerfile)
55
+ dockerfile_content = read_file(dockerfile_path)
56
+ return ChatPromptTemplate.from_template(DOCKERGENERATION_OBSERVE_SYSTEM_PROMPT).format(
57
+ dockerfile_content=dockerfile_content,
58
+ docker_build_output=build_error,
59
+ docker_run_output=run_error,
60
+ )
61
+
62
+ @staticmethod
63
+ def _extract_error_message(output: str):
64
+ if isinstance(output, bytes):
65
+ output = output.decode('utf-8')
66
+ extracted_msg = ""
67
+ output_lower = output.lower()
68
+ if "error:" in output_lower:
69
+ ix = output_lower.find("error:")
70
+ extracted_msg = output[ix:]
71
+ elif "error" in output_lower:
72
+ ix = output_lower.find("error")
73
+ extracted_msg = output[ix:]
74
+ else:
75
+ extracted_msg = output
76
+ if len(extracted_msg) > MAX_ERROR_OUTPTU_LENGTH:
77
+ extracted_msg = extracted_msg[((-1) * MAX_ERROR_OUTPTU_LENGTH):]
78
+ return extracted_msg
79
+
80
+ def _execute_directly(self, state: DockerGenerationWorkflowState):
81
+ token_usage = {**DEFAULT_TOKEN_USAGE}
82
+ if "dockerfile" in state and len(state["dockerfile"]) > 0:
83
+ dockerfile=state["dockerfile"]
84
+ dockerfile_path = os.path.join(self.repo_path, dockerfile)
85
+ docker_image_name: str = os.path.splitext(dockerfile)[0]
86
+ docker_image_name = docker_image_name.lower()
87
+
88
+ out, error, code = run_command([
89
+ "docker", "build",
90
+ "-t", docker_image_name,
91
+ "-f", dockerfile_path,
92
+ self.repo_path
93
+ ], timeout=MAX_TIMEOUT)
94
+ if code != 0:
95
+ error_msg = DockerGenerationObserveStep._extract_error_message(error)
96
+ system_prompt = self._build_system_prompt(state, error_msg, "N/A")
97
+ agent = CommonAgentTwoChainSteps(llm=self.llm)
98
+ res, _, token_usage, reasoning = agent.go(
99
+ system_prompt=system_prompt,
100
+ instruction_prompt="Now, let's begin observing.",
101
+ schema=DockerGenerationObserveResult,
102
+ )
103
+ state["step_dockerfile_content"] = read_file(dockerfile_path)
104
+ state["step_output"] = error_msg
105
+ state["step_thoughts"] = res.thoughts
106
+ self._print_step(
107
+ state,
108
+ step_output=f"**Observation Reasoning Process**\n{reasoning}"
109
+ )
110
+ return state, token_usage
111
+ out, error, code = run_command([
112
+ "docker", "run",
113
+ "--name", "bioguider_demo",
114
+ docker_image_name
115
+ ], timeout=MAX_TIMEOUT)
116
+ run_command([
117
+ "docker", "rm", "-f",
118
+ "bioguider_demo"
119
+ ], timeout=MAX_TIMEOUT)
120
+ run_command([
121
+ "docker", "rmi", docker_image_name
122
+ ], timeout=MAX_TIMEOUT)
123
+ if code != 0:
124
+ system_prompt = self._build_system_prompt(
125
+ state,
126
+ "docker build successfully.",
127
+ error,
128
+ )
129
+ agent = CommonAgentTwoChainSteps(llm=self.llm)
130
+ res, _, token_usage, reasoning = agent.go(
131
+ system_prompt=system_prompt,
132
+ instruction_prompt="Now, let's begin observing.",
133
+ schema=DockerGenerationObserveResult,
134
+ )
135
+ state["step_dockerfile_content"] = read_file(dockerfile_path)
136
+ state["step_output"] = error
137
+ state["step_thoughts"] = res.thoughts
138
+ self._print_step(
139
+ state,
140
+ step_output=f"**Observation Reasoning Process**\n{reasoning}",
141
+ )
142
+ return state, token_usage
143
+
144
+ state["final_answer"] = read_file(dockerfile_path)
145
+ return state, token_usage
146
+
147
+ state["step_thoughts"] = "No Dockerfile is generated."
148
+ return state, token_usage
149
+
150
+
151
+
152
+
153
+
154
+
@@ -0,0 +1,158 @@
1
+
2
+ import os
3
+ from langchain_openai.chat_models.base import BaseChatOpenAI
4
+ from langchain.tools import BaseTool
5
+ from langchain_core.prompts import ChatPromptTemplate
6
+ from nanoid import generate
7
+
8
+ from bioguider.agents.agent_utils import (
9
+ convert_plan_to_string,
10
+ get_tool_names_and_descriptions,
11
+ PlanAgentResult,
12
+ PlanAgentResultJsonSchema,
13
+ )
14
+ from bioguider.agents.peo_common_step import PEOCommonStep
15
+ from bioguider.agents.common_agent_2step import CommonAgentTwoChainSteps, CommonAgentTwoSteps
16
+ from bioguider.agents.dockergeneration_task_utils import (
17
+ DockerGenerationWorkflowState,
18
+ prepare_provided_files_string,
19
+ )
20
+
21
+ DOCKERGENERATION_PLAN_SYSTEM_PROMPT = ChatPromptTemplate.from_template("""
22
+ You are an expert in software containerization and reproducibility engineering.
23
+ Your task is to generate a **Dockerfile** that prepares the environment and runs a simple get-started example based on the provided files from a GitHub repository.
24
+ ---
25
+
26
+ ### Repository File Structure
27
+ Below is the 2-level file structure of the repository (`f` = file, `d` = directory, `l` - symlink, `u` - unknown):
28
+ {repo_structure}
29
+
30
+ ### **Input Files:**
31
+
32
+ You are given the contents of the following files extracted from the repository:
33
+
34
+ {extracted_files}
35
+ ---
36
+
37
+ ### **Intermediate Dockerfile**
38
+ Here is the Dockerfile you generated before.
39
+ {intermediate_dockerfile_content}
40
+
41
+ ---
42
+
43
+ ### **Intermediate Error**
44
+ Here is the error when building or running the Dockerfile
45
+ {intermediate_error}
46
+
47
+ ## ** Intermediate Thoughts **
48
+ Here is the thoughts you need to take into consideration.
49
+ {intermediate_thoughts}
50
+ ---
51
+
52
+ ### **Function Tools**
53
+ You have access to the following function tools:
54
+ {tools}
55
+ ---
56
+
57
+ ### Instructions:
58
+ 1. We will iterate through multiple **Plan -> Execution -> Observation** loops as needed.
59
+ - Plan stage(current stage) will make a plan based on provided **tools**, **intermediate output** and **repo structure**
60
+ - Execution stage will execute the planned actions to generate Dockerfile
61
+ - Observation stage will observe the Dockerfile that is generated in execution step and provide advice in **intermediate thoughts**
62
+ 2. Your current task is to make a plan to achieve the goal.
63
+ You can start by `write_file_tool` to prepare script files, then use `generate_Dockerfile_tool` to generate **Dockerfile**
64
+ 3. When using `write_file_tool`, you must specify both the **file name** and **file content** as input.
65
+ - Use `write_file_tool` to create new files, such as a minimal demo script.
66
+ - You may also use it to **overwrite existing files** if **needed**.
67
+ - If no update, **do not** use `write_file_tool` to overwrite existed file.
68
+ - Always provide **complete and concrete file content**—do **not** include suggestions, placeholders, abstract descriptions, or part of content.
69
+ 4. You can use `extract_python_file_from_notebook_tool` to extract python code from python notebook and save to a python file to avoid running python notebook with jupyter.
70
+ 5. You may use the `python_repl` tool to execute Python code, but this should **also be avoided in the first step**.
71
+ 6. The Dockerfile will be placed at the root of the repository.
72
+ Therefore, in the Dockerfile, you can assume all repository files are accessible and can be copied as needed.
73
+ 7. If you are given **Intermediate Error** and **Intermediate Dockerfile**, you need to analyze them carefully, and try to fix them with new generated Dockerfile.
74
+ You need to provide concrete resolution in your reasoning process.
75
+ 8. When using `generate_Dockerfile_tool` to generate a Dockerfile, please use `demo-bioguider-{docker_id}.Dockerfile` as file name.
76
+ 9. Always use `generate_Dockerfile_tool` as the **final action step** in your plan to ensure the Dockerfile is generated at the end of the process.
77
+ ---
78
+
79
+ ### **Output Format**
80
+ Your plan should be returned as a sequence of step actions in the following format:
81
+
82
+ Step: <tool name> # Tool name must be one of {tool_names}
83
+ Step Input: <file or directory name>
84
+
85
+ Step: <tool name>
86
+ Step Input: <file or directory name>
87
+ ...
88
+ """)
89
+
90
+ class DockerGenerationPlanStep(PEOCommonStep):
91
+ def __init__(
92
+ self,
93
+ llm: BaseChatOpenAI,
94
+ repo_path: str,
95
+ repo_structure: str,
96
+ gitignore_path: str,
97
+ custom_tools: list[BaseTool] | None = None,
98
+ ):
99
+ super().__init__(llm)
100
+ self.step_name = "Dockerfile Generation Plan Step"
101
+ self.repo_path = repo_path
102
+ self.repo_structure = repo_structure
103
+ self.gitignore_path = gitignore_path
104
+ self.custom_tools = custom_tools
105
+
106
+ def _prepare_intermediate_steps(self, state: DockerGenerationWorkflowState):
107
+ _, intermediate_thoughts = super()._build_intermediate_analysis_and_thoughts(state)
108
+ intermediate_dockerfile_content = state["step_dockerfile_content"] if "step_dockerfile_content" in state else "N/A"
109
+ intermediate_error = state["step_output"] if "step_output" in state else "N/A"
110
+ intermediate_error = intermediate_error.replace("{", "(").replace("}", ")")
111
+
112
+ return intermediate_dockerfile_content, intermediate_error, intermediate_thoughts
113
+
114
+ def _prepare_system_prompt(self, state: DockerGenerationWorkflowState) -> str:
115
+ docker_id = generate('1234567890abcdefhijklmnopqrstuvwxyz', size=10)
116
+ tool_names, tools_desc = get_tool_names_and_descriptions(self.custom_tools)
117
+ provided_files = state["provided_files"]
118
+ str_provided_files = prepare_provided_files_string(self.repo_path, provided_files)
119
+
120
+ intermediate_dockerfile_content, intermediate_error, intermediate_thoughts = self._prepare_intermediate_steps(state)
121
+ system_prompt = DOCKERGENERATION_PLAN_SYSTEM_PROMPT.format(
122
+ repo_structure=self.repo_structure,
123
+ tools=tools_desc,
124
+ tool_names=tool_names,
125
+ extracted_files=str_provided_files,
126
+ intermediate_dockerfile_content=intermediate_dockerfile_content,
127
+ intermediate_error=intermediate_error,
128
+ intermediate_thoughts=intermediate_thoughts,
129
+ docker_id=docker_id,
130
+ )
131
+ self._print_step(
132
+ state,
133
+ step_output="**Intermediate Step Output**\n" + intermediate_error
134
+ )
135
+ self._print_step(
136
+ state,
137
+ step_output="**Intermediate Step Thoughts**\n" + intermediate_thoughts
138
+ )
139
+ return system_prompt
140
+
141
+ def _execute_directly(self, state: DockerGenerationWorkflowState):
142
+ system_prompt = self._prepare_system_prompt(state)
143
+ agent = CommonAgentTwoChainSteps(llm=self.llm)
144
+ res, _, token_usage, reasoning = agent.go(
145
+ system_prompt=system_prompt,
146
+ instruction_prompt="Now, let's begin to make a plan",
147
+ schema=PlanAgentResultJsonSchema,
148
+ )
149
+ res = PlanAgentResult(**res)
150
+ self._print_step(state, step_output=f"**Reasoning Process**\n{reasoning}")
151
+ self._print_step(state, step_output=f"**Plan**\n{str(res.actions)}")
152
+ state["plan_thoughts"] = reasoning
153
+ state["plan_actions"] = convert_plan_to_string(res)
154
+
155
+ return state, token_usage
156
+
157
+
158
+