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.
- bioguider/__init__.py +0 -0
- bioguider/agents/__init__.py +0 -0
- bioguider/agents/agent_task.py +92 -0
- bioguider/agents/agent_tools.py +176 -0
- bioguider/agents/agent_utils.py +504 -0
- bioguider/agents/collection_execute_step.py +182 -0
- bioguider/agents/collection_observe_step.py +125 -0
- bioguider/agents/collection_plan_step.py +156 -0
- bioguider/agents/collection_task.py +184 -0
- bioguider/agents/collection_task_utils.py +142 -0
- bioguider/agents/common_agent.py +137 -0
- bioguider/agents/common_agent_2step.py +215 -0
- bioguider/agents/common_conversation.py +61 -0
- bioguider/agents/common_step.py +85 -0
- bioguider/agents/consistency_collection_step.py +102 -0
- bioguider/agents/consistency_evaluation_task.py +57 -0
- bioguider/agents/consistency_evaluation_task_utils.py +14 -0
- bioguider/agents/consistency_observe_step.py +110 -0
- bioguider/agents/consistency_query_step.py +77 -0
- bioguider/agents/dockergeneration_execute_step.py +186 -0
- bioguider/agents/dockergeneration_observe_step.py +154 -0
- bioguider/agents/dockergeneration_plan_step.py +158 -0
- bioguider/agents/dockergeneration_task.py +158 -0
- bioguider/agents/dockergeneration_task_utils.py +220 -0
- bioguider/agents/evaluation_installation_task.py +270 -0
- bioguider/agents/evaluation_readme_task.py +767 -0
- bioguider/agents/evaluation_submission_requirements_task.py +172 -0
- bioguider/agents/evaluation_task.py +206 -0
- bioguider/agents/evaluation_tutorial_task.py +169 -0
- bioguider/agents/evaluation_tutorial_task_prompts.py +187 -0
- bioguider/agents/evaluation_userguide_prompts.py +179 -0
- bioguider/agents/evaluation_userguide_task.py +154 -0
- bioguider/agents/evaluation_utils.py +127 -0
- bioguider/agents/identification_execute_step.py +181 -0
- bioguider/agents/identification_observe_step.py +104 -0
- bioguider/agents/identification_plan_step.py +140 -0
- bioguider/agents/identification_task.py +270 -0
- bioguider/agents/identification_task_utils.py +22 -0
- bioguider/agents/peo_common_step.py +64 -0
- bioguider/agents/prompt_utils.py +253 -0
- bioguider/agents/python_ast_repl_tool.py +69 -0
- bioguider/agents/rag_collection_task.py +130 -0
- bioguider/conversation.py +67 -0
- bioguider/database/code_structure_db.py +500 -0
- bioguider/database/summarized_file_db.py +146 -0
- bioguider/generation/__init__.py +39 -0
- bioguider/generation/benchmark_metrics.py +610 -0
- bioguider/generation/change_planner.py +189 -0
- bioguider/generation/document_renderer.py +157 -0
- bioguider/generation/llm_cleaner.py +67 -0
- bioguider/generation/llm_content_generator.py +1128 -0
- bioguider/generation/llm_injector.py +809 -0
- bioguider/generation/models.py +85 -0
- bioguider/generation/output_manager.py +74 -0
- bioguider/generation/repo_reader.py +37 -0
- bioguider/generation/report_loader.py +166 -0
- bioguider/generation/style_analyzer.py +36 -0
- bioguider/generation/suggestion_extractor.py +436 -0
- bioguider/generation/test_metrics.py +189 -0
- bioguider/managers/benchmark_manager.py +785 -0
- bioguider/managers/evaluation_manager.py +215 -0
- bioguider/managers/generation_manager.py +686 -0
- bioguider/managers/generation_test_manager.py +107 -0
- bioguider/managers/generation_test_manager_v2.py +525 -0
- bioguider/rag/__init__.py +0 -0
- bioguider/rag/config.py +117 -0
- bioguider/rag/data_pipeline.py +651 -0
- bioguider/rag/embedder.py +24 -0
- bioguider/rag/rag.py +138 -0
- bioguider/settings.py +103 -0
- bioguider/utils/code_structure_builder.py +59 -0
- bioguider/utils/constants.py +135 -0
- bioguider/utils/default.gitignore +140 -0
- bioguider/utils/file_utils.py +215 -0
- bioguider/utils/gitignore_checker.py +175 -0
- bioguider/utils/notebook_utils.py +117 -0
- bioguider/utils/pyphen_utils.py +73 -0
- bioguider/utils/python_file_handler.py +65 -0
- bioguider/utils/r_file_handler.py +551 -0
- bioguider/utils/utils.py +163 -0
- bioguider-0.2.52.dist-info/LICENSE +21 -0
- bioguider-0.2.52.dist-info/METADATA +51 -0
- bioguider-0.2.52.dist-info/RECORD +84 -0
- bioguider-0.2.52.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
from typing import Callable, List, Optional, TypedDict, Union
|
|
6
|
+
from langchain_core.prompts import ChatPromptTemplate, StringPromptTemplate
|
|
7
|
+
from langchain_core.messages import SystemMessage, HumanMessage
|
|
8
|
+
from langchain_openai.chat_models.base import BaseChatOpenAI
|
|
9
|
+
from langchain.tools import StructuredTool, Tool, tool, BaseTool
|
|
10
|
+
from langchain.agents import (
|
|
11
|
+
initialize_agent,
|
|
12
|
+
AgentType,
|
|
13
|
+
AgentOutputParser,
|
|
14
|
+
create_react_agent,
|
|
15
|
+
AgentExecutor,
|
|
16
|
+
)
|
|
17
|
+
from langchain.schema import (
|
|
18
|
+
AgentFinish,
|
|
19
|
+
AgentAction,
|
|
20
|
+
)
|
|
21
|
+
from langgraph.graph import StateGraph, START, END
|
|
22
|
+
|
|
23
|
+
from bioguider.database.summarized_file_db import SummarizedFilesDb
|
|
24
|
+
from bioguider.agents.peo_common_step import PEOCommonStep
|
|
25
|
+
from bioguider.utils.file_utils import get_file_type
|
|
26
|
+
from bioguider.agents.agent_utils import read_directory, read_file
|
|
27
|
+
from bioguider.agents.collection_task_utils import (
|
|
28
|
+
RELATED_FILE_GOAL_ITEM,
|
|
29
|
+
CollectionWorkflowState,
|
|
30
|
+
check_file_related_tool,
|
|
31
|
+
)
|
|
32
|
+
from bioguider.agents.common_agent import CommonAgent
|
|
33
|
+
from bioguider.agents.dockergeneration_task_utils import (
|
|
34
|
+
generate_Dockerfile_tool,
|
|
35
|
+
prepare_provided_files_string,
|
|
36
|
+
write_file_tool,
|
|
37
|
+
extract_python_file_from_notebook_tool,
|
|
38
|
+
)
|
|
39
|
+
from bioguider.agents.python_ast_repl_tool import CustomPythonAstREPLTool
|
|
40
|
+
from bioguider.agents.dockergeneration_plan_step import DockerGenerationPlanStep
|
|
41
|
+
from bioguider.agents.dockergeneration_execute_step import DockerGenerationExecuteStep
|
|
42
|
+
from bioguider.agents.dockergeneration_observe_step import DockerGenerationObserveStep
|
|
43
|
+
from bioguider.agents.dockergeneration_task_utils import DockerGenerationWorkflowState
|
|
44
|
+
from bioguider.agents.agent_task import AgentTask
|
|
45
|
+
|
|
46
|
+
class DockerGenerationTask(AgentTask):
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
llm,
|
|
50
|
+
step_callback = None,
|
|
51
|
+
):
|
|
52
|
+
super().__init__(llm, step_callback)
|
|
53
|
+
self.repo_path: str | None = None
|
|
54
|
+
self.gitignore_path: str | None = None
|
|
55
|
+
self.repo_structure: str | None = None
|
|
56
|
+
self.steps: list[PEOCommonStep] = []
|
|
57
|
+
self.tools: list[any] | None = None
|
|
58
|
+
self.provided_files: list[str] | None = None
|
|
59
|
+
|
|
60
|
+
def _initialize(self):
|
|
61
|
+
# initialize the 2-level file structure of the repo
|
|
62
|
+
if not os.path.exists(self.repo_path):
|
|
63
|
+
raise ValueError(f"Repository path {self.repo_path} does not exist.")
|
|
64
|
+
files = read_directory(self.repo_path, os.path.join(self.repo_path, ".gitignore"))
|
|
65
|
+
file_pairs = [(f, get_file_type(os.path.join(self.repo_path, f)).value) for f in files]
|
|
66
|
+
self.repo_structure = ""
|
|
67
|
+
for f, f_type in file_pairs:
|
|
68
|
+
self.repo_structure += f"{f} - {f_type}\n"
|
|
69
|
+
|
|
70
|
+
# initialize extracted files string
|
|
71
|
+
if self.provided_files is not None:
|
|
72
|
+
self.str_extracted_files = prepare_provided_files_string(
|
|
73
|
+
self.repo_path, self.provided_files
|
|
74
|
+
)
|
|
75
|
+
write_tool = write_file_tool(self.repo_path)
|
|
76
|
+
generate_tool = generate_Dockerfile_tool(
|
|
77
|
+
llm=self.llm,
|
|
78
|
+
repo_path=self.repo_path,
|
|
79
|
+
extracted_files=self.str_extracted_files,
|
|
80
|
+
repo_structure=self.repo_structure,
|
|
81
|
+
output_callback=self.step_callback,
|
|
82
|
+
)
|
|
83
|
+
extract_tool = extract_python_file_from_notebook_tool(
|
|
84
|
+
repo_path=self.repo_path,
|
|
85
|
+
)
|
|
86
|
+
self.tools = [
|
|
87
|
+
write_tool, generate_tool, extract_tool,
|
|
88
|
+
]
|
|
89
|
+
self.custom_tools = [
|
|
90
|
+
StructuredTool.from_function(
|
|
91
|
+
write_tool.run,
|
|
92
|
+
description=write_tool.__class__.__doc__,
|
|
93
|
+
name=write_tool.__class__.__name__,
|
|
94
|
+
),
|
|
95
|
+
Tool(
|
|
96
|
+
func=generate_tool.run,
|
|
97
|
+
description=generate_tool.__class__.__doc__,
|
|
98
|
+
name=generate_tool.__class__.__name__,
|
|
99
|
+
),
|
|
100
|
+
StructuredTool.from_function(
|
|
101
|
+
extract_tool.run,
|
|
102
|
+
description=extract_tool.__class__.__doc__,
|
|
103
|
+
name=extract_tool.__class__.__name__,
|
|
104
|
+
)
|
|
105
|
+
]
|
|
106
|
+
self.custom_tools.append(CustomPythonAstREPLTool())
|
|
107
|
+
plan_step = DockerGenerationPlanStep(
|
|
108
|
+
llm=self.llm,
|
|
109
|
+
repo_path=self.repo_path,
|
|
110
|
+
repo_structure=self.repo_structure,
|
|
111
|
+
gitignore_path=self.gitignore_path,
|
|
112
|
+
custom_tools=self.custom_tools,
|
|
113
|
+
)
|
|
114
|
+
execute_step = DockerGenerationExecuteStep(
|
|
115
|
+
llm=self.llm,
|
|
116
|
+
repo_path=self.repo_path,
|
|
117
|
+
repo_structure=self.repo_structure,
|
|
118
|
+
gitignore_path=self.gitignore_path,
|
|
119
|
+
custom_tools=self.custom_tools,
|
|
120
|
+
)
|
|
121
|
+
observe_step = DockerGenerationObserveStep(
|
|
122
|
+
llm=self.llm,
|
|
123
|
+
repo_path=self.repo_path,
|
|
124
|
+
)
|
|
125
|
+
self.steps = [
|
|
126
|
+
plan_step, execute_step, observe_step,
|
|
127
|
+
]
|
|
128
|
+
# pass generate_Dockerfile_tool to execute step
|
|
129
|
+
execute_step.set_generate_Dockerfile_tool(generate_tool)
|
|
130
|
+
|
|
131
|
+
def _compile(self, repo_path, gitignore_path, **kwargs):
|
|
132
|
+
self.repo_path = repo_path
|
|
133
|
+
self.gitignore_path = gitignore_path
|
|
134
|
+
self.provided_files = kwargs.get("provided_files")
|
|
135
|
+
self._initialize()
|
|
136
|
+
|
|
137
|
+
def check_observe_step(state: DockerGenerationWorkflowState):
|
|
138
|
+
if "final_answer" in state and state["final_answer"] is not None:
|
|
139
|
+
self._print_step(step_name="Final Answer")
|
|
140
|
+
self._print_step(step_output=state["final_answer"])
|
|
141
|
+
return END
|
|
142
|
+
return "plan_step"
|
|
143
|
+
|
|
144
|
+
graph = StateGraph(DockerGenerationWorkflowState)
|
|
145
|
+
graph.add_node("plan_step", self.steps[0].execute)
|
|
146
|
+
graph.add_node("execute_step", self.steps[1].execute)
|
|
147
|
+
graph.add_node("observe_step", self.steps[2].execute)
|
|
148
|
+
graph.add_edge(START, "plan_step")
|
|
149
|
+
graph.add_edge("plan_step", "execute_step")
|
|
150
|
+
graph.add_edge("execute_step", "observe_step")
|
|
151
|
+
graph.add_conditional_edges("observe_step", check_observe_step, {"plan_step", END})
|
|
152
|
+
|
|
153
|
+
self.graph = graph.compile()
|
|
154
|
+
|
|
155
|
+
def generate(self):
|
|
156
|
+
s = self._go_graph({"provided_files": self.provided_files})
|
|
157
|
+
return s
|
|
158
|
+
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Callable, Optional, TypedDict
|
|
5
|
+
from langchain_openai.chat_models.base import BaseChatOpenAI
|
|
6
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
from bioguider.agents.agent_tools import agent_tool
|
|
10
|
+
from bioguider.agents.agent_utils import read_file, write_file
|
|
11
|
+
from bioguider.agents.common_agent_2step import CommonAgentTwoSteps
|
|
12
|
+
from bioguider.utils.file_utils import extract_code_from_notebook
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
class DockerGenerationPlanResult(BaseModel):
|
|
17
|
+
Dockerfile: str = Field(description="Dockerfile content")
|
|
18
|
+
|
|
19
|
+
class DockerGenerationWorkflowState(TypedDict):
|
|
20
|
+
llm: Optional[BaseChatOpenAI]
|
|
21
|
+
step_output_callback: Optional[Callable]
|
|
22
|
+
provided_files: Optional[list[str]]
|
|
23
|
+
intermediate_steps: Optional[str]
|
|
24
|
+
step_dockerfile_content: Optional[str]
|
|
25
|
+
step_output: Optional[str]
|
|
26
|
+
step_thoughts: Optional[str]
|
|
27
|
+
plan_thoughts: Optional[str]
|
|
28
|
+
plan_actions: Optional[str]
|
|
29
|
+
dockerfile: Optional[str]
|
|
30
|
+
final_answer: Optional[str]
|
|
31
|
+
|
|
32
|
+
def extract_dockergeneration_related_content(filename: str):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
DOCKERGENERATION_SYSTEM_PROMPT = ChatPromptTemplate.from_template("""
|
|
36
|
+
You are an expert in software containerization and reproducibility engineering.
|
|
37
|
+
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.
|
|
38
|
+
---
|
|
39
|
+
### Repository File Structure
|
|
40
|
+
Below is the 2-level file structure of the repository (`f` = file, `d` = directory, `l` - symlink, `u` - unknown):
|
|
41
|
+
{repo_structure}
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
### **Input Files:**
|
|
45
|
+
|
|
46
|
+
You are given the contents of the following files extracted from the repository:
|
|
47
|
+
|
|
48
|
+
{extracted_files}
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### **plan thoughts**
|
|
52
|
+
Here is the plan thoughts, you are in **generate_Dockerfile_tool** action:
|
|
53
|
+
{plan_thoughts}
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### **Intermediate Output**
|
|
58
|
+
Here is the Dockerfile you generate before.
|
|
59
|
+
{step_dockerfile_content}
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### **Intermediate Error**
|
|
64
|
+
Here is the error occurred in building or running the above generated Dockerfile:
|
|
65
|
+
{step_error}
|
|
66
|
+
|
|
67
|
+
### **Requirements:**
|
|
68
|
+
1. **Environment Setup**
|
|
69
|
+
* When generating the Dockerfile, prioritize using the base image provided in the repository. If no base image is specified, select an appropriate one based on the project's context.
|
|
70
|
+
* Use the relevant installation and configuration details from the input files (e.g., `requirements.txt`, `environment.yml`, `setup.py`, etc.).
|
|
71
|
+
* Choose an appropriate base image (e.g., `python:3.10`, `r-base`, etc.) based on the language and setup instructions.
|
|
72
|
+
2. **Dependency Installation**
|
|
73
|
+
* Include all commands necessary to install packages, tools, or dependencies as specified in the input files.
|
|
74
|
+
* Make sure to always install common system utilities and development tools such as gcc, g++, build-essential, curl, wget, and similar essential packages.
|
|
75
|
+
3. **Running a Get-Started Example**
|
|
76
|
+
* Identify a minimal executable script or command (e.g., `python example.py`, `Rscript demo.R`, `jupyter nbconvert --execute`) that demonstrates the basic functionality of the repository.
|
|
77
|
+
4. **Keep the Dockerfile Minimal and Reproducible**
|
|
78
|
+
* Use best practices such as specifying exact versions where possible, minimizing layers, and using `COPY`, `WORKDIR`, and `CMD` appropriately.
|
|
79
|
+
5. The Dockerfile will be placed at the root of the repository.
|
|
80
|
+
Therefore, in the Dockerfile, you can assume all repository files are accessible and can be copied as needed.
|
|
81
|
+
6. If the **Intermediate Output** and **Intermediate Error** are provided, you need to analyze them carefully, and try to fix them in the generated Dockerfile.
|
|
82
|
+
---
|
|
83
|
+
### **Output Format:**
|
|
84
|
+
Return only the Dockerfile content enclosed in triple backticks:
|
|
85
|
+
```dockerfile
|
|
86
|
+
# Dockerfile
|
|
87
|
+
<your generated Dockerfile content here>
|
|
88
|
+
```
|
|
89
|
+
Do not include any explanation, comments, or extra output outside the code block.
|
|
90
|
+
""")
|
|
91
|
+
|
|
92
|
+
class generate_Dockerfile_tool(agent_tool):
|
|
93
|
+
""" Generate Dockerfile for provided repository
|
|
94
|
+
Args:
|
|
95
|
+
output_path str: the output path to save Dockerfile
|
|
96
|
+
Returns:
|
|
97
|
+
boolean: if Dockerfile is saved successfully
|
|
98
|
+
"""
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
llm: BaseChatOpenAI,
|
|
102
|
+
repo_path: str,
|
|
103
|
+
extracted_files: str,
|
|
104
|
+
repo_structure: str,
|
|
105
|
+
output_callback: Callable | None = None,
|
|
106
|
+
):
|
|
107
|
+
super().__init__(llm, output_callback=output_callback)
|
|
108
|
+
self.repo_path = repo_path
|
|
109
|
+
self.repo_struture = repo_structure
|
|
110
|
+
self.extracted_files = extracted_files
|
|
111
|
+
self.plan_thoughts = None
|
|
112
|
+
self.step_error: str = None
|
|
113
|
+
self.step_dockerfile_content: str = None
|
|
114
|
+
|
|
115
|
+
def set_intermediate_output(self, plan_thoughts: str, step_error: str, step_dockerfile_content: str):
|
|
116
|
+
plan_thoughts = plan_thoughts.replace("{", "(").replace("}", ")")
|
|
117
|
+
step_error = step_error.replace("{", "(").replace("}", ")")
|
|
118
|
+
self.plan_thoughts = plan_thoughts
|
|
119
|
+
self.step_error = step_error
|
|
120
|
+
self.step_dockerfile_content = step_dockerfile_content
|
|
121
|
+
|
|
122
|
+
def run(self, output_path: str):
|
|
123
|
+
agent = CommonAgentTwoSteps(llm=self.llm)
|
|
124
|
+
system_prompt = DOCKERGENERATION_SYSTEM_PROMPT.format(
|
|
125
|
+
repo_structure = self.repo_struture,
|
|
126
|
+
extracted_files = self.extracted_files,
|
|
127
|
+
plan_thoughts=self.plan_thoughts,
|
|
128
|
+
step_error=self.step_error,
|
|
129
|
+
step_dockerfile_content=self.step_dockerfile_content
|
|
130
|
+
)
|
|
131
|
+
res, _, token_usage, reasoning = agent.go(
|
|
132
|
+
system_prompt=system_prompt,
|
|
133
|
+
instruction_prompt="Now, let's start to generate Dockerfile.",
|
|
134
|
+
schema=DockerGenerationPlanResult,
|
|
135
|
+
)
|
|
136
|
+
res: DockerGenerationPlanResult = res
|
|
137
|
+
self._print_step_output(step_output=reasoning)
|
|
138
|
+
self._print_token_usage(token_usage)
|
|
139
|
+
if self.repo_path not in output_path:
|
|
140
|
+
output_path = os.path.join(self.repo_path, output_path)
|
|
141
|
+
content = res.Dockerfile
|
|
142
|
+
if content.startswith("```dockerfile"):
|
|
143
|
+
content = content[13:]
|
|
144
|
+
content = content.strip().strip("```")
|
|
145
|
+
write_file(output_path, content)
|
|
146
|
+
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
class write_file_tool():
|
|
150
|
+
"""write file tool
|
|
151
|
+
Args:
|
|
152
|
+
file_name str: a string specifies file path that will be written to.
|
|
153
|
+
file_content str: a string speifies file content.
|
|
154
|
+
Returns:
|
|
155
|
+
bool, True if it is succeeded to write to file, otherwise False
|
|
156
|
+
"""
|
|
157
|
+
def __init__(self, repo_path: str):
|
|
158
|
+
self.repo_path = repo_path
|
|
159
|
+
|
|
160
|
+
def run(self, file_name: str, file_content: str):
|
|
161
|
+
if file_name is None or file_content is None:
|
|
162
|
+
return False
|
|
163
|
+
file_name = file_name
|
|
164
|
+
content = file_content
|
|
165
|
+
file_name = file_name.strip()
|
|
166
|
+
if self.repo_path is not None and self.repo_path not in file_name:
|
|
167
|
+
file_name = os.path.join(self.repo_path, file_name)
|
|
168
|
+
try:
|
|
169
|
+
with open(file_name, "w") as fobj:
|
|
170
|
+
fobj.write(content)
|
|
171
|
+
return True
|
|
172
|
+
except Exception as e:
|
|
173
|
+
logger.error(e)
|
|
174
|
+
return False
|
|
175
|
+
|
|
176
|
+
class extract_python_file_from_notebook_tool:
|
|
177
|
+
"""extract code in a notebook to a python file
|
|
178
|
+
Args:
|
|
179
|
+
notebook_path str: a string speicifies notebook path to extract.
|
|
180
|
+
output_path str: a string specifies output python file path.
|
|
181
|
+
Returns:
|
|
182
|
+
bool True if it is succeeded to extract to python file, otherwise False
|
|
183
|
+
"""
|
|
184
|
+
def __init__(self, repo_path: str):
|
|
185
|
+
self.repo_path = repo_path
|
|
186
|
+
|
|
187
|
+
def run(self, notebook_path: str, output_path: str):
|
|
188
|
+
# notebook_path = notebook_path_and_output_path[0]
|
|
189
|
+
# output_path = notebook_path_and_output_path[1]
|
|
190
|
+
if notebook_path is None or output_path is None:
|
|
191
|
+
return False
|
|
192
|
+
if self.repo_path not in notebook_path:
|
|
193
|
+
notebook_path = os.path.join(self.repo_path, notebook_path)
|
|
194
|
+
if self.repo_path not in output_path:
|
|
195
|
+
output_path = os.path.join(self.repo_path, output_path)
|
|
196
|
+
content = extract_code_from_notebook(notebook_path)
|
|
197
|
+
try:
|
|
198
|
+
with open(output_path, "w") as fobj:
|
|
199
|
+
fobj.write(content)
|
|
200
|
+
return True
|
|
201
|
+
except FileNotFoundError as e:
|
|
202
|
+
logger.error(str(e))
|
|
203
|
+
return f"False, {output_path} does not exist."
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def prepare_provided_files_string(repo_path: str, provided_files: list[str]):
|
|
207
|
+
if provided_files is None or len(provided_files) == 0:
|
|
208
|
+
return "N/A"
|
|
209
|
+
str_provided_files = ""
|
|
210
|
+
for fn in provided_files:
|
|
211
|
+
file_path = os.path.join(repo_path, fn)
|
|
212
|
+
if fn.endswith(".ipynb"): # python notebook
|
|
213
|
+
content = extract_code_from_notebook(file_path)
|
|
214
|
+
else:
|
|
215
|
+
content = read_file(file_path)
|
|
216
|
+
content = content.replace("{", "{{").replace("}", "}}")
|
|
217
|
+
str_provided_files += f"""**{fn}**:\n{content}\n"""
|
|
218
|
+
|
|
219
|
+
return str_provided_files
|
|
220
|
+
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import logging
|
|
4
|
+
from langchain.prompts import ChatPromptTemplate
|
|
5
|
+
from markdownify import markdownify as md
|
|
6
|
+
|
|
7
|
+
from bioguider.agents.agent_utils import read_file
|
|
8
|
+
from bioguider.agents.collection_task import CollectionTask
|
|
9
|
+
from bioguider.agents.prompt_utils import EVALUATION_INSTRUCTION, CollectionGoalItemEnum
|
|
10
|
+
from bioguider.utils.constants import (
|
|
11
|
+
DEFAULT_TOKEN_USAGE,
|
|
12
|
+
ProjectMetadata,
|
|
13
|
+
StructuredEvaluationInstallationResult,
|
|
14
|
+
FreeEvaluationInstallationResult,
|
|
15
|
+
EvaluationInstallationResult,
|
|
16
|
+
)
|
|
17
|
+
from bioguider.rag.data_pipeline import count_tokens
|
|
18
|
+
from .evaluation_utils import run_llm_evaluation
|
|
19
|
+
|
|
20
|
+
from .evaluation_task import EvaluationTask
|
|
21
|
+
from bioguider.utils.utils import get_overall_score, increase_token_usage
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
STRUCTURED_EVALUATION_INSTALLATION_SYSTEM_PROMPT = """
|
|
27
|
+
You are an expert in evaluating the quality of installation information in software repositories.
|
|
28
|
+
Your task is to analyze the provided files related to installation and generate a structured quality assessment based on the following criteria.
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### **Evaluation Criteria**
|
|
32
|
+
|
|
33
|
+
1. **Installation Available**: Is the installation section in document (like README.md or INSTALLATION)?
|
|
34
|
+
* Output: `Yes` or `No`
|
|
35
|
+
|
|
36
|
+
2. **Installation Tutorial**: Is the step-by-step installation tutorial provided?
|
|
37
|
+
* Ouput: `Yes` or `No`
|
|
38
|
+
|
|
39
|
+
3. **Number of required Dependencies Installation**: The number of dependencies that are required to install
|
|
40
|
+
* Output: Number
|
|
41
|
+
* Suggest specific improvements if necessary, such as missing dependencies
|
|
42
|
+
|
|
43
|
+
4. **Compatible Operating System**: Is the compatible operating system described?
|
|
44
|
+
* Output: `Yes` or `No`
|
|
45
|
+
|
|
46
|
+
5. **Hardware Requirements**: Is the hardware requirements described?
|
|
47
|
+
* Output: `Yes` or `No`
|
|
48
|
+
|
|
49
|
+
6. **Overall Score**: Give an overall quality rating of the Installation information.
|
|
50
|
+
* Output: a number between 0 and 100 representing the overall quality rating.
|
|
51
|
+
* **Grade Level**:
|
|
52
|
+
- **85-100**: The installation information is exceptionally clear, polished, and engaging. It reads smoothly, with minimal effort required from the reader.
|
|
53
|
+
- **65-84**: The installation information is clear and easy to understand, with a natural flow and minimal jargon.
|
|
54
|
+
- **45-64**: The installation information is somewhat clear, but could benefit from more polish and consistency.
|
|
55
|
+
- **0-44**: The installation information is difficult to understand, with unclear language, jargon, or overly complex sentences.
|
|
56
|
+
|
|
57
|
+
### Installation Files Provided:
|
|
58
|
+
{installation_files_content}
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
FREE_EVALUATION_INSTALLATION_SYSTEM_PROMPT = """
|
|
64
|
+
You are an expert in evaluating the quality of **installation instructions** in software repositories.
|
|
65
|
+
Your task is to analyze the provided content of installation-related files and generate a **comprehensive, structured quality report**.
|
|
66
|
+
You will be given:
|
|
67
|
+
1. The content of installation-related files.
|
|
68
|
+
2. A structured evaluation of the installation-related files and its reasoning process.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
### **Instructions**
|
|
73
|
+
1. Based on the provided structured evaluation and its reasoning process, generate a free evaluation of the installation-related files.
|
|
74
|
+
2. Focus on the explanation of assessment in structured evaluation and how to improve the installation-related files based on the structured evaluation and its reasoning process.
|
|
75
|
+
* For each suggestion to improve the installation-related files, you **must provide some examples** of the original text snippet and the improving comments.
|
|
76
|
+
3. For each item in the structured evaluation, provide a detailed assessment followed by specific, actionable comments for improvement.
|
|
77
|
+
4. Your improvement suggestions must also include the original text snippet and the improving comments.
|
|
78
|
+
5. Your improvement suggestions must also include suggestions to improve readability.
|
|
79
|
+
6. If you think the it is good enough, you can say so.
|
|
80
|
+
7. In each section output, please first give a detailed explanation of the assessment, and then provide the detailed suggestion for improvement. If you think the it is good enough, you can say so.
|
|
81
|
+
The following is an example of the output format:
|
|
82
|
+
**Ease of Access:**
|
|
83
|
+
Detailed explanation of the assessment. Such as: The INSTALLATION file is present in the repository. The content of the file has been shared completely and is accessible. This confirms the availability of the installation documentation for evaluation. There's no issue with availability.
|
|
84
|
+
Detailed suggestion for improvement. Such as: No need to improve accessibility of the installation documentation.
|
|
85
|
+
**Clarity of Dependency Specification:**
|
|
86
|
+
Detailed explanation of the assessment. Such as: The installation section provides a clear list of dependencies. No need to improve.
|
|
87
|
+
Detailed suggestion for improvement. Such as: No need to improve.
|
|
88
|
+
**Hardware Requirements:**
|
|
89
|
+
Detailed explanation of the assessment. Such as: No hardware requirements are provided in the installation section.
|
|
90
|
+
Detailed suggestion for improvement. Such as: Add a section to describe the hardware requirements for the installation.
|
|
91
|
+
**Installation Guide:**
|
|
92
|
+
Detailed explanation of the assessment. Such as: The installation guide is present in the INSTALLATION file, but lacks a clear and concise guide for the installation process.
|
|
93
|
+
Detailed suggestion for improvement. Such as:
|
|
94
|
+
- Add a clear and concise guide for the installation process.
|
|
95
|
+
- Improve the readability of the installation guide, like:
|
|
96
|
+
- <original text snippet> - <improving comments>
|
|
97
|
+
- <original text snippet> - <improving comments>
|
|
98
|
+
- ...
|
|
99
|
+
**Compatible Operating System:**
|
|
100
|
+
Detailed explanation of the assessment. Such as: No compatible operating system is provided in the installation section.
|
|
101
|
+
Detailed suggestion for improvement. Such as:
|
|
102
|
+
- Add a section to describe the compatible operating systems for the installation.
|
|
103
|
+
- For example, Ubuntu 22.04 LTS+, CentOS 7+, Mac OS X 10.15+, Windows 10+ are supported.
|
|
104
|
+
- ...
|
|
105
|
+
**Overall Score:**
|
|
106
|
+
Detailed explanation of the assessment. Such as: The installation section provides a clear list of installation steps. No need to improve.
|
|
107
|
+
Detailed suggestion for improvement. Such as: Add a section to describe the compatible operating systems for the installation.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### **Output Format**
|
|
112
|
+
Your output must **exactly match** the following format. Do not add or omit any sections. In the output, your detailed assessment and detailed suggestion must include the original text snippet and the improving comments, and include the analysis and explanation.
|
|
113
|
+
|
|
114
|
+
**FinalAnswer**
|
|
115
|
+
**Ease of Access:**
|
|
116
|
+
<Your assessment and suggestion here>
|
|
117
|
+
**Clarity of Dependency Specification:**
|
|
118
|
+
<Your assessment and suggestion here>
|
|
119
|
+
**Hardware Requirements:**
|
|
120
|
+
<Your assessment and suggestion here>
|
|
121
|
+
**Installation Guide:**
|
|
122
|
+
<Your assessment and suggestion here>
|
|
123
|
+
**Compatible Operating System:**
|
|
124
|
+
<Your assessment and suggestion here>
|
|
125
|
+
**Overall Score:**
|
|
126
|
+
<Your assessment and suggestion here>
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### **Structured Evaluation and Reasoning Process**
|
|
131
|
+
{structured_evaluation_and_reasoning_process}
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### Installation Files Provided:
|
|
136
|
+
{installation_files_content}
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
class EvaluationInstallationTask(EvaluationTask):
|
|
141
|
+
def __init__(
|
|
142
|
+
self,
|
|
143
|
+
llm,
|
|
144
|
+
repo_path,
|
|
145
|
+
gitignore_path,
|
|
146
|
+
meta_data = None,
|
|
147
|
+
step_callback = None,
|
|
148
|
+
summarized_files_db = None,
|
|
149
|
+
collected_files: list[str] | None = None,
|
|
150
|
+
):
|
|
151
|
+
super().__init__(llm, repo_path, gitignore_path, meta_data, step_callback, summarized_files_db)
|
|
152
|
+
self.evaluation_name = "Installation Evaluation"
|
|
153
|
+
self.collected_files = collected_files
|
|
154
|
+
|
|
155
|
+
def _collect_install_files_content(self, files: list[str] | None=None) -> str:
|
|
156
|
+
if files is None or len(files) == 0:
|
|
157
|
+
return "N/A"
|
|
158
|
+
files_content = ""
|
|
159
|
+
MAX_TOKENS = os.environ.get("OPENAI_MAX_INPUT_TOKENS", 102400)
|
|
160
|
+
for f in files:
|
|
161
|
+
if f.endswith(".html") or f.endswith(".htm"):
|
|
162
|
+
html = read_file(os.path.join(self.repo_path, f))
|
|
163
|
+
content = md(html, escape_underscores=False)
|
|
164
|
+
else:
|
|
165
|
+
content = read_file(os.path.join(self.repo_path, f))
|
|
166
|
+
if count_tokens(content) > int(MAX_TOKENS):
|
|
167
|
+
content = content[:100000]
|
|
168
|
+
files_content += f"""
|
|
169
|
+
{f} content:
|
|
170
|
+
{content}
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
return files_content
|
|
174
|
+
|
|
175
|
+
def _structured_evaluate(self, files: list[str] | None = None) -> tuple[dict|None, dict]:
|
|
176
|
+
if files is None or len(files) == 0:
|
|
177
|
+
return None, {**DEFAULT_TOKEN_USAGE}
|
|
178
|
+
|
|
179
|
+
files_content = self._collect_install_files_content(files)
|
|
180
|
+
system_prompt = ChatPromptTemplate.from_template(
|
|
181
|
+
STRUCTURED_EVALUATION_INSTALLATION_SYSTEM_PROMPT,
|
|
182
|
+
).format(
|
|
183
|
+
installation_files_content=files_content,
|
|
184
|
+
)
|
|
185
|
+
res, token_usage, reasoning_process = run_llm_evaluation(
|
|
186
|
+
llm=self.llm,
|
|
187
|
+
system_prompt=system_prompt,
|
|
188
|
+
instruction_prompt=EVALUATION_INSTRUCTION,
|
|
189
|
+
schema=StructuredEvaluationInstallationResult,
|
|
190
|
+
chain=True,
|
|
191
|
+
)
|
|
192
|
+
res: StructuredEvaluationInstallationResult = res
|
|
193
|
+
res.overall_score = get_overall_score([
|
|
194
|
+
res.install_available,
|
|
195
|
+
res.install_tutorial,
|
|
196
|
+
res.compatible_os,
|
|
197
|
+
res.hardware_requirements,
|
|
198
|
+
], [3, 3, 1, 1])
|
|
199
|
+
res.dependency_number = 0 if res.dependency_number is None else res.dependency_number
|
|
200
|
+
self.print_step(step_output=reasoning_process)
|
|
201
|
+
self.print_step(token_usage=token_usage)
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
"evaluation": res,
|
|
205
|
+
"reasoning_process": reasoning_process,
|
|
206
|
+
}, token_usage
|
|
207
|
+
|
|
208
|
+
def _free_evaluate(
|
|
209
|
+
self,
|
|
210
|
+
files: list[str] | None=None,
|
|
211
|
+
structured_evaluation_and_reasoning_process: str | None=None,
|
|
212
|
+
) -> tuple[dict|None, dict]:
|
|
213
|
+
if files is None or len(files) == 0:
|
|
214
|
+
return None, {**DEFAULT_TOKEN_USAGE}
|
|
215
|
+
|
|
216
|
+
structured_evaluation_and_reasoning_process = structured_evaluation_and_reasoning_process or "N/A"
|
|
217
|
+
files_content = self._collect_install_files_content(files)
|
|
218
|
+
system_prompt = ChatPromptTemplate.from_template(FREE_EVALUATION_INSTALLATION_SYSTEM_PROMPT).format(
|
|
219
|
+
installation_files_content=files_content,
|
|
220
|
+
structured_evaluation_and_reasoning_process=structured_evaluation_and_reasoning_process,
|
|
221
|
+
)
|
|
222
|
+
res, token_usage, reasoning_process = run_llm_evaluation(
|
|
223
|
+
llm=self.llm,
|
|
224
|
+
system_prompt=system_prompt,
|
|
225
|
+
instruction_prompt=EVALUATION_INSTRUCTION,
|
|
226
|
+
schema=FreeEvaluationInstallationResult,
|
|
227
|
+
)
|
|
228
|
+
self.print_step(step_output=reasoning_process)
|
|
229
|
+
self.print_step(token_usage=token_usage)
|
|
230
|
+
evaluation = {
|
|
231
|
+
"evaluation": res,
|
|
232
|
+
"reasoning_process": reasoning_process,
|
|
233
|
+
}
|
|
234
|
+
return evaluation, token_usage
|
|
235
|
+
|
|
236
|
+
def _evaluate(self, files: list[str] | None = None) -> tuple[EvaluationInstallationResult | None, dict, list[str]]:
|
|
237
|
+
total_token_usage = {**DEFAULT_TOKEN_USAGE}
|
|
238
|
+
|
|
239
|
+
structured_evaluation, structured_token_usage = self._structured_evaluate(files)
|
|
240
|
+
total_token_usage = increase_token_usage(total_token_usage, structured_token_usage)
|
|
241
|
+
evaluation, token_usage = self._free_evaluate(files, structured_evaluation["reasoning_process"])
|
|
242
|
+
total_token_usage = increase_token_usage(total_token_usage, token_usage)
|
|
243
|
+
|
|
244
|
+
combined_evaluation = EvaluationInstallationResult(
|
|
245
|
+
structured_evaluation=structured_evaluation["evaluation"],
|
|
246
|
+
free_evaluation=evaluation["evaluation"],
|
|
247
|
+
structured_reasoning_process=structured_evaluation["reasoning_process"],
|
|
248
|
+
free_reasoning_process=evaluation["reasoning_process"],
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return combined_evaluation, total_token_usage, files
|
|
252
|
+
|
|
253
|
+
def _collect_files(self):
|
|
254
|
+
if self.collected_files is not None:
|
|
255
|
+
return self.collected_files
|
|
256
|
+
|
|
257
|
+
task = CollectionTask(
|
|
258
|
+
llm=self.llm,
|
|
259
|
+
step_callback=self.step_callback,
|
|
260
|
+
)
|
|
261
|
+
task.compile(
|
|
262
|
+
repo_path=self.repo_path,
|
|
263
|
+
gitignore_path=Path(self.repo_path, ".gitignore"),
|
|
264
|
+
db=self.summarized_files_db,
|
|
265
|
+
goal_item=CollectionGoalItemEnum.Installation.name,
|
|
266
|
+
)
|
|
267
|
+
files = task.collect()
|
|
268
|
+
if files is None:
|
|
269
|
+
return []
|
|
270
|
+
return files
|