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,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