bioguider 0.2.11__py3-none-any.whl → 0.2.13__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.

Potentially problematic release.


This version of bioguider might be problematic. Click here for more details.

@@ -13,7 +13,12 @@ class AgentTask(ABC):
13
13
  A class representing a step in an agent's process.
14
14
  """
15
15
 
16
- def __init__(self, llm: BaseChatOpenAI, step_callback: Callable | None = None):
16
+ def __init__(
17
+ self,
18
+ llm: BaseChatOpenAI,
19
+ step_callback: Callable | None = None,
20
+ summarized_files_db: SummarizedFilesDb | None = None,
21
+ ):
17
22
  """
18
23
  Initialize the AgentStep with a language model and a callback function.
19
24
 
@@ -23,7 +28,7 @@ class AgentTask(ABC):
23
28
  """
24
29
  self.llm = llm
25
30
  self.step_callback = step_callback
26
- self.summary_file_db = None
31
+ self.summarized_files_db = summarized_files_db
27
32
  self.graph: CompiledGraph | None = None
28
33
 
29
34
  def _print_step(
@@ -45,7 +50,7 @@ class AgentTask(ABC):
45
50
  token_usage=token_usage,
46
51
  )
47
52
 
48
- def compile(self, repo_path: str, gitignore_path: str, db: SummarizedFilesDb | None = None, **kwargs):
53
+ def compile(self, repo_path: str, gitignore_path: str, **kwargs):
49
54
  """
50
55
  Compile the agent step with the given repository and gitignore paths.
51
56
 
@@ -55,7 +60,6 @@ class AgentTask(ABC):
55
60
  **kwargs: derived class may pass more arguments to implmented _compile(), that is,
56
61
  what **kwargs is depends on derived class
57
62
  """
58
- self.summary_file_db = db
59
63
  self._compile(repo_path, gitignore_path, **kwargs)
60
64
 
61
65
  @abstractmethod
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import logging
2
3
  from typing import Callable
3
4
  from markdownify import markdownify as md
4
5
  from langchain_openai.chat_models.base import BaseChatOpenAI
@@ -7,6 +8,8 @@ from bioguider.utils.file_utils import get_file_type
7
8
  from bioguider.agents.agent_utils import read_directory, read_file, summarize_file
8
9
  from bioguider.rag.data_pipeline import count_tokens
9
10
 
11
+ logger = logging.getLogger(__name__)
12
+
10
13
  class agent_tool:
11
14
  def __init__(
12
15
  self,
@@ -53,19 +56,12 @@ Returns:
53
56
  class summarize_file_tool(agent_tool):
54
57
  """ Read a file and generate a summary according to a specified prompt.
55
58
 
56
- Arguments
57
- ----------
58
- file_path : str, required
59
- Path to the file to read.
60
- summarize_prompt : str, optional
61
- Instruction guiding the summarization focus (default is "N/A").
62
- Use this to emphasize specific aspects of the content.
59
+ Args:
60
+ file_path str: required. The file path to read.
61
+ summarize_prompt str: optional. A string instruction guiding the summarization focus (default is "N/A"). Use this to emphasize specific aspects of the content.
63
62
 
64
- Returns
65
- -------
66
- str or None
67
- A summarized version of the file content.
68
- Returns None if the file does not exist or cannot be read.
63
+ Returns:
64
+ str or None: A summarized version of the file content. Returns None if the file does not exist or cannot be read.
69
65
  """
70
66
  def __init__(
71
67
  self,
@@ -124,8 +120,15 @@ Returns
124
120
  if summarized_content is not None:
125
121
  return f"summarized content of file {file_path}: " + summarized_content
126
122
 
127
- file_content = read_file(abs_file_path)
128
- file_content = file_content.replace("{", "{{").replace("}", "}}")
123
+ try:
124
+ file_content = read_file(abs_file_path)
125
+ file_content = file_content.replace("{", "{{").replace("}", "}}")
126
+ except UnicodeDecodeError as e:
127
+ logger.error(str(e))
128
+ return f"{file_path} is a binary, can't be summarized."
129
+ except Exception as e:
130
+ logger.error(str(e))
131
+ return f"Failed to read {file_path}."
129
132
  summarized_content, token_usage = summarize_file(
130
133
  self.llm, abs_file_path, file_content, self.detailed_level,
131
134
  summary_instructions=self.summarize_instruction,
@@ -16,11 +16,12 @@ from langchain.tools import BaseTool
16
16
  from langchain.schema import AgentAction, AgentFinish
17
17
  from langchain.agents import AgentOutputParser
18
18
  from langgraph.prebuilt import create_react_agent
19
+ from langchain_community.callbacks.openai_info import OpenAICallbackHandler
19
20
  import logging
20
21
 
21
22
  from pydantic import BaseModel, Field
22
23
 
23
- from bioguider.utils.constants import DEFAULT_TOKEN_USAGE
24
+ from bioguider.utils.constants import DEFAULT_TOKEN_USAGE, MAX_FILE_LENGTH, MAX_SENTENCE_NUM
24
25
  from bioguider.utils.file_utils import get_file_type
25
26
  from ..utils.gitignore_checker import GitignoreChecker
26
27
  from ..database.summarized_file_db import SummarizedFilesDb
@@ -178,8 +179,7 @@ Here is the file content:
178
179
  Now, let's start to summarize.
179
180
  """)
180
181
 
181
- MAX_FILE_LENGTH=20 *1024 # 20K
182
- MAX_SENTENCE_NUM=20
182
+
183
183
  def summarize_file(
184
184
  llm: BaseChatOpenAI,
185
185
  name: str,
@@ -379,6 +379,20 @@ def escape_braces(text: str) -> str:
379
379
  text = re.sub(r'(?<!{){(?!{)', '{{', text)
380
380
  return text
381
381
 
382
+ STRING_TO_OBJECT_SYSTEM_PROMPT = """
383
+ You are an expert to understand data. You will be provided a text, and your task is to extracted structured data from the provided text.
384
+
385
+ ---
386
+
387
+ ### **Instructions**
388
+ 1. If no structured data can be extracted, return None
389
+
390
+ ---
391
+
392
+ ### **Input Text**
393
+ {input_text}
394
+ """
395
+
382
396
  def try_parse_json_object(json_obj: str) -> dict | None:
383
397
  json_obj = json_obj.strip()
384
398
 
@@ -406,4 +420,26 @@ def try_parse_json_object(json_obj: str) -> dict | None:
406
420
  return None
407
421
  except Exception as e:
408
422
  logger.error(e)
409
- return None
423
+ return None
424
+
425
+ def try_parse_with_llm(llm: BaseChatOpenAI, input_text: str, schema: any):
426
+ system_prompt = ChatPromptTemplate.from_template(
427
+ STRING_TO_OBJECT_SYSTEM_PROMPT
428
+ ).format(input_text=input_text)
429
+ prompt = ChatPromptTemplate.from_messages([
430
+ ("system", system_prompt)
431
+ ])
432
+ agent = prompt | llm.with_structured_output(schema)
433
+ callback_handler = OpenAICallbackHandler()
434
+
435
+ try:
436
+ res = agent.invoke(
437
+ input={},
438
+ config={
439
+ "callbacks": [callback_handler],
440
+ },
441
+ )
442
+ return res, vars(callback_handler)
443
+ except Exception as e:
444
+ logger.error(e)
445
+ return None
@@ -5,7 +5,7 @@ from langchain_openai.chat_models.base import BaseChatOpenAI
5
5
  from langchain_core.prompts import ChatPromptTemplate
6
6
  from bioguider.agents.agent_utils import ObservationResult
7
7
  from bioguider.agents.collection_task_utils import CollectionWorkflowState
8
- from bioguider.agents.common_agent_2step import CommonAgentTwoSteps
8
+ from bioguider.agents.common_agent_2step import CommonAgentTwoChainSteps, CommonAgentTwoSteps
9
9
  from bioguider.agents.peo_common_step import PEOCommonStep
10
10
  from bioguider.agents.prompt_utils import COLLECTION_GOAL, COLLECTION_PROMPTS
11
11
 
@@ -34,11 +34,13 @@ Here is the 2-level file structure of the repository (`f` = file, `d` = director
34
34
 
35
35
  * Provide your reasoning under **Analysis**
36
36
  * Then list all relevant files and folders under **FinalAnswer**
37
+ * **FinalAnswer** format must exactly match this format:
38
+ **FinalAnswer**: {{"final_answer": [<file path>, <file path>, <file path>, ...]}}
37
39
  * Be sure to include the **full relative paths** with respect to the repository root.
38
- * Your answer **must follow this exact format** (note: no JSON code block, no additional comments):
40
+ * Your answer **must exactly match the follwing format** (note: no JSON code block, no additional comments), **do not** make up anything:
39
41
 
40
42
  ```
41
- **Analysis**: your analysis here
43
+ **Analysis**: your analysis here
42
44
  **FinalAnswer**: {{"final_answer": ["path/to/file1", "path/to/file2", ...]}}
43
45
  ```
44
46
  4. If you believe **more files still need to be collected**:
@@ -80,8 +82,8 @@ class CollectionObserveStep(PEOCommonStep):
80
82
  repo_structure = self.repo_structure
81
83
  intermediate_steps = self._build_intermediate_steps(state)
82
84
  prompt = ChatPromptTemplate.from_template(COLLECTION_OBSERVE_SYSTEM_PROMPT)
83
- important_instructions = "N/A" if "important_instructions" not in collection_item or len(collection_item["important_instructions"]) == 0 \
84
- else collection_item["important_instructions"]
85
+ important_instructions = "N/A" if "observe_important_instructions" not in collection_item or len(collection_item["observe_important_instructions"]) == 0 \
86
+ else collection_item["observe_important_instructions"]
85
87
  return prompt.format(
86
88
  goal_item_desc=goal_item_desc,
87
89
  related_file_description=collection_item["related_file_description"],
@@ -8,7 +8,7 @@ from bioguider.agents.agent_utils import (
8
8
  PlanAgentResultJsonSchema,
9
9
  PlanAgentResult,
10
10
  )
11
- from bioguider.agents.common_agent_2step import CommonAgentTwoSteps
11
+ from bioguider.agents.common_agent_2step import CommonAgentTwoChainSteps, CommonAgentTwoSteps
12
12
  from bioguider.agents.peo_common_step import PEOCommonStep
13
13
  from bioguider.agents.collection_task_utils import CollectionWorkflowState
14
14
  from bioguider.agents.prompt_utils import COLLECTION_GOAL, COLLECTION_PROMPTS
@@ -57,7 +57,9 @@ Here are the results from previous steps:
57
57
 
58
58
  3. You may use the `read_directory` tool to explore directory contents, but avoid using it in the first step unless necessary.
59
59
 
60
- 4. You may use the `python_repl` tool to execute Python code, but this should **also be avoided in the first step**.
60
+ 4. Your plan can only use the above tools, **do not** make up any tools not in the above tools list.
61
+
62
+ 5. Your planned step input file or input directory must come from the above repository files structure, **do not** make up file name or directory name.
61
63
 
62
64
  ---
63
65
 
@@ -65,12 +67,12 @@ Here are the results from previous steps:
65
67
  {important_instructions}
66
68
 
67
69
  ### **Output Format**
68
- Your plan should be returned as a sequence of steps in the following format:
70
+ Your plan **must exactly match** a sequence of steps in the following format, **do not** make up anything:
69
71
 
70
- Step: <tool name> # Tool name must be one of {tool_names}
72
+ Step: <tool name> # Tool name **must be one** of {tool_names}
71
73
  Step Input: <file or directory name>
72
74
 
73
- Step: <tool name>
75
+ Step: <tool name> # Tool name **must be one** of {tool_names}
74
76
  Step Input: <file or directory name>
75
77
  ...
76
78
  """)
@@ -105,8 +107,8 @@ class CollectionPlanStep(PEOCommonStep):
105
107
  step_analysis, step_thoughts = self._build_intermediate_analysis_and_thoughts(state)
106
108
  goal = ChatPromptTemplate.from_template(COLLECTION_GOAL).format(goal_item=collection_item["goal_item"])
107
109
  related_file_description = collection_item["related_file_description"]
108
- important_instructions="N/A" if "important_instructions" not in collection_item or len(collection_item["important_instructions"]) == 0 \
109
- else collection_item["important_instructions"]
110
+ important_instructions="N/A" if "plan_important_instructions" not in collection_item or len(collection_item["plan_important_instructions"]) == 0 \
111
+ else collection_item["plan_important_instructions"]
110
112
  tool_names, tools_desc = get_tool_names_and_descriptions(self.custom_tools)
111
113
  system_prompt = COLLECTION_PLAN_SYSTEM_PROMPT.format(
112
114
  goal=goal,
@@ -50,9 +50,12 @@ class CollectionTask(AgentTask):
50
50
  def __init__(
51
51
  self,
52
52
  llm: BaseChatOpenAI,
53
- step_callback: Callable | None = None
53
+ step_callback: Callable | None = None,
54
+ summarize_instruction: str | None = "N/A",
55
+ summarized_files_db: SummarizedFilesDb | None = None,
56
+ provided_files: list[str] | None = None,
54
57
  ):
55
- super().__init__(llm, step_callback)
58
+ super().__init__(llm, step_callback, summarized_files_db=summarized_files_db)
56
59
  self.repo_path: str | None = None
57
60
  self.gitignore_path: str | None = None
58
61
  self.repo_structure: str | None = None
@@ -60,6 +63,8 @@ class CollectionTask(AgentTask):
60
63
  self.steps: list[PEOCommonStep] = []
61
64
  self.tools: list[any] | None = None
62
65
  self.custom_tools: list[Tool] | None = None
66
+ self.summarize_instruction = summarize_instruction
67
+ self.provided_files = provided_files
63
68
 
64
69
  def _prepare_tools(self, related_file_goal_item_desc):
65
70
  tool_rd = read_directory_tool(repo_path=self.repo_path)
@@ -67,7 +72,8 @@ class CollectionTask(AgentTask):
67
72
  llm=self.llm,
68
73
  repo_path=self.repo_path,
69
74
  output_callback=self.step_callback,
70
- db=self.summary_file_db,
75
+ db=self.summarized_files_db,
76
+ summaize_instruction=self.summarize_instruction,
71
77
  )
72
78
  tool_rf = read_file_tool(repo_path=self.repo_path)
73
79
  tool_cf = check_file_related_tool(
@@ -75,6 +81,8 @@ class CollectionTask(AgentTask):
75
81
  repo_path=self.repo_path,
76
82
  goal_item_desc=related_file_goal_item_desc,
77
83
  output_callback=self.step_callback,
84
+ summarize_instruction=self.summarize_instruction,
85
+ summarized_files_db=self.summarized_files_db,
78
86
  )
79
87
  self.tools = [tool_rd, tool_sum, tool_rf, tool_cf]
80
88
  self.custom_tools = [
@@ -99,13 +107,15 @@ class CollectionTask(AgentTask):
99
107
  description=tool_cf.__class__.__doc__,
100
108
  ),
101
109
  ]
102
- self.custom_tools.append(CustomPythonAstREPLTool())
110
+ # self.custom_tools.append(CustomPythonAstREPLTool())
103
111
 
104
112
  def _initialize(self):
105
113
  # initialize the 2-level file structure of the repo
106
114
  if not os.path.exists(self.repo_path):
107
115
  raise ValueError(f"Repository path {self.repo_path} does not exist.")
108
- files = read_directory(self.repo_path, os.path.join(self.repo_path, ".gitignore"))
116
+ files = self.provided_files
117
+ if files is None:
118
+ files = read_directory(self.repo_path, os.path.join(self.repo_path, ".gitignore"))
109
119
  file_pairs = [(f, get_file_type(os.path.join(self.repo_path, f)).value) for f in files]
110
120
  self.repo_structure = ""
111
121
  for f, f_type in file_pairs:
@@ -4,13 +4,17 @@ from langchain.prompts import ChatPromptTemplate
4
4
  from langchain_openai.chat_models.base import BaseChatOpenAI
5
5
  from langchain_core.messages import AIMessage
6
6
  from pydantic import BaseModel, Field
7
+ import logging
7
8
 
8
9
  from bioguider.agents.agent_tools import agent_tool
9
10
  from bioguider.agents.agent_utils import read_file, summarize_file
10
11
  from bioguider.agents.peo_common_step import PEOWorkflowState
11
12
  from bioguider.agents.common_agent import CommonAgent
12
13
  from bioguider.agents.common_agent_2step import CommonAgentTwoSteps
14
+ from bioguider.database.summarized_file_db import SummarizedFilesDb
15
+ from bioguider.utils.constants import MAX_FILE_LENGTH
13
16
 
17
+ logger = logging.getLogger(__name__)
14
18
 
15
19
  class CollectionWorkflowState(TypedDict):
16
20
  llm: Optional[BaseChatOpenAI]
@@ -46,20 +50,22 @@ Does this file appear to contain related information?
46
50
 
47
51
  ---
48
52
 
49
- ### **Output Format:**
50
- Respond with a single word: "Yes" or "No" to indicate whether the file is related to the goal item.
51
- Do not include any additional text, explanation, or formatting.
53
+ ### **Output Format:**
54
+ Respond with exactly two parts:
55
+ 1. A single word: Yes or No (indicating if the file meets the goal criteria)
56
+ 2. One brief explanatory sentence.
57
+ For example: Yes. This file is a compiled binary file, so, it is related to the compiled standalone file (goal item).
52
58
  """)
53
59
 
54
60
  class CheckFileRelatedResult(BaseModel):
55
- is_related: bool = Field(description="True if the file is related to the goal item, False otherwise.")
61
+ is_related: str = Field(description="A string conclusion specify if the provided file is related. The string value contains two parts:\n 1. A single word: Yes or No (indicating if the file meets the goal criteria).\n 2. One brief explanatory sentence.")
56
62
 
57
63
  class check_file_related_tool(agent_tool):
58
64
  """ Check if the file is related to the goal item
59
65
  Args:
60
66
  file_path str: file path
61
67
  Returns:
62
- bool: True if the file is related to the goal item, False otherwise.
68
+ str: A string conclusion. The string conclusion contains two parts:\n 1. A single word: Yes or No (indicating if the file meets the goal criteria).\n 2. One brief explanatory sentence.
63
69
  """
64
70
  def __init__(
65
71
  self,
@@ -67,23 +73,51 @@ Returns:
67
73
  repo_path: str,
68
74
  goal_item_desc: str,
69
75
  output_callback: Callable | None = None,
76
+ summarize_instruction: str | None = None,
77
+ summarize_level: int | None = 6,
78
+ summarized_files_db: SummarizedFilesDb | None = None,
70
79
  ):
71
80
  super().__init__(llm=llm, output_callback=output_callback)
72
81
  self.repo_path = repo_path
73
82
  self.goal_item_desc = goal_item_desc
83
+ self.summarize_instruction = summarize_instruction \
84
+ if summarize_instruction is not None else "N/A"
85
+ self.summarize_level = summarize_level
86
+ self.summarized_files_db = summarized_files_db
74
87
 
75
88
  def run(self, file_path: str) -> str:
76
89
  if not self.repo_path in file_path:
77
90
  file_path = os.path.join(self.repo_path, file_path)
78
91
  if not os.path.isfile(file_path):
79
92
  return "Can't read file"
80
- file_content = read_file(file_path)
81
- if file_content is None:
93
+
94
+ check_prompts = None
95
+ try:
96
+ file_content = read_file(file_path)
97
+ except UnicodeDecodeError as e:
98
+ logger.error(str(e))
99
+ check_prompts = "Can't summarize binary file, please decide according to file name and extension."
100
+ except Exception as e:
101
+ logger.error(str(e))
102
+ check_prompts = "Failed to summarize file, please decide according to file name and extension."
103
+ if check_prompts is None and file_content is None:
82
104
  return "Failed to read file"
83
- summarized_content, token_usage = summarize_file(self.llm, file_path, file_content, 6)
84
- if summarized_content is None:
85
- return "Failed to summarize file"
86
- self._print_token_usage(token_usage)
105
+ if check_prompts is not None:
106
+ summarized_content = check_prompts
107
+ else:
108
+ if len(file_content) > MAX_FILE_LENGTH:
109
+ file_content = file_content[:MAX_FILE_LENGTH]
110
+ summarized_content, token_usage = summarize_file(
111
+ llm=self.llm,
112
+ name=file_path,
113
+ content=file_content,
114
+ level=self.summarize_level,
115
+ summary_instructions=self.summarize_instruction,
116
+ db=self.summarized_files_db,
117
+ )
118
+ if summarized_content is None:
119
+ return "Failed to summarize file"
120
+ self._print_token_usage(token_usage)
87
121
 
88
122
  prompt = CHECK_FILE_RELATED_USER_PROMPT.format(
89
123
  goal_item_desc=self.goal_item_desc,
@@ -102,8 +136,5 @@ Returns:
102
136
 
103
137
  self._print_step_output(step_output=reasoning)
104
138
  self._print_token_usage(token_usage)
105
- if out:
106
- return "Yes, the file is related to the goal item."
107
- else:
108
- return "No, the file **is not** related to the goal item."
139
+ return res.is_related
109
140
 
@@ -47,7 +47,7 @@ class DockerGenerationTask(AgentTask):
47
47
  def __init__(
48
48
  self,
49
49
  llm,
50
- step_callback = None
50
+ step_callback = None,
51
51
  ):
52
52
  super().__init__(llm, step_callback)
53
53
  self.repo_path: str | None = None
@@ -9,7 +9,8 @@ from pydantic import BaseModel, Field
9
9
  from markdownify import markdownify as md
10
10
 
11
11
  from bioguider.agents.agent_utils import read_file
12
- from bioguider.agents.prompt_utils import EVALUATION_INSTRUCTION
12
+ from bioguider.agents.collection_task import CollectionTask
13
+ from bioguider.agents.prompt_utils import EVALUATION_INSTRUCTION, CollectionGoalItemEnum
13
14
  from bioguider.utils.constants import DEFAULT_TOKEN_USAGE, ProjectMetadata
14
15
  from bioguider.rag.data_pipeline import count_tokens
15
16
  from .common_agent_2step import CommonAgentTwoSteps, CommonAgentTwoChainSteps
@@ -29,17 +30,20 @@ Your task is to analyze the provided files related to installation and generate
29
30
 
30
31
  ### **Evaluation Criteria**
31
32
 
32
- 1. **Installation Available**: Is the installation documents accessible and present?
33
+ 1. **Installation Available**: Is the installation section in document (like README.md or INSTALLATION)?
33
34
  * Output: `Yes` or `No`
34
35
 
35
- 2. **Installation Tutorial**: Is the installation tutorial provided?
36
+ 2. **Installation Tutorial**: Is the step-by-step installation tutorial provided?
36
37
  * Ouput: `Yes` or `No`
37
38
 
38
39
  3. **Number of required Dependencies Installation**: The number of dependencies that are required to install
39
40
  * Output: Number
40
41
  * Suggest specific improvements if necessary, such as missing dependencies
41
42
 
42
- 4. **Overall Score**: Give an overall quality rating of the Installation information.
43
+ 4. **Compatible Operating System**: Is the compatible operating system described?
44
+ * Output: `Yes` or `No`
45
+
46
+ 5. **Overall Score**: Give an overall quality rating of the Installation information.
43
47
  * Output: `Poor`, `Fair`, `Good`, or `Excellent`
44
48
 
45
49
  ---
@@ -53,6 +57,7 @@ Your final report must **exactly match** the following format. Do not add or omi
53
57
  **Dependency:**
54
58
  * number: [Number]
55
59
  * suggestions: <suggestion to improve **dependency information** like missing dependencies
60
+ **Compatible Operating System:** [Yes / No]
56
61
  **Overall Score:** [Poor / Fair / Good / Excellent]
57
62
 
58
63
  ---
@@ -113,6 +118,7 @@ class StructuredEvaluationInstallationResult(BaseModel):
113
118
  install_tutorial: Optional[bool]=Field(description="A boolean value. Is the installation tutorial provided?")
114
119
  dependency_number: Optional[int]=Field(description="A number. It is the number of dependencies that are required to install.")
115
120
  dependency_suggestions: Optional[str]=Field(description="A string value. It is the specific improvements if necessary, such as missing dependencies")
121
+ compatible_os: Optional[bool]=Field(description="A boolean value. Is compatible operating system described?")
116
122
  overall_score: Optional[str]=Field(description="A overall scroll for the installation quality, could be `Poor`, `Fair`, `Good`, or `Excellent`")
117
123
 
118
124
  class EvaluationInstallationResult(BaseModel):
@@ -163,8 +169,9 @@ class EvaluationInstallationTask(EvaluationTask):
163
169
  gitignore_path,
164
170
  meta_data = None,
165
171
  step_callback = None,
172
+ summarized_files_db = None,
166
173
  ):
167
- super().__init__(llm, repo_path, gitignore_path, meta_data, step_callback)
174
+ super().__init__(llm, repo_path, gitignore_path, meta_data, step_callback, summarized_files_db)
168
175
  self.evaluation_name = "Installation Evaluation"
169
176
 
170
177
 
@@ -235,7 +242,7 @@ class EvaluationInstallationTask(EvaluationTask):
235
242
  }
236
243
  return evaluation, token_usage
237
244
 
238
- def _evaluate(self, files: list[str] | None = None) -> tuple[dict | None, dict]:
245
+ def _evaluate(self, files: list[str] | None = None) -> tuple[dict | None, dict, list[str]]:
239
246
  evaluation, token_usage = self._free_evaluate(files)
240
247
  structured_evaluation, structured_token_usage = self._structured_evaluate(files)
241
248
 
@@ -245,5 +252,20 @@ class EvaluationInstallationTask(EvaluationTask):
245
252
  }
246
253
  total_token_usage = increase_token_usage(token_usage, structured_token_usage)
247
254
 
248
- return combined_evaluation, total_token_usage
249
-
255
+ return combined_evaluation, total_token_usage, files
256
+
257
+ def _collect_files(self):
258
+ task = CollectionTask(
259
+ llm=self.llm,
260
+ step_callback=self.step_callback,
261
+ )
262
+ task.compile(
263
+ repo_path=self.repo_path,
264
+ gitignore_path=Path(self.repo_path, ".gitignore"),
265
+ db=self.summarized_files_db,
266
+ goal_item=CollectionGoalItemEnum.Installation.name,
267
+ )
268
+ files = task.collect()
269
+ if files is None:
270
+ return []
271
+ return files
@@ -7,6 +7,7 @@ from langchain_openai.chat_models.base import BaseChatOpenAI
7
7
  from pydantic import BaseModel, Field
8
8
 
9
9
  from bioguider.agents.prompt_utils import EVALUATION_INSTRUCTION
10
+ from bioguider.utils.gitignore_checker import GitignoreChecker
10
11
 
11
12
  from ..utils.pyphen_utils import PyphenReadability
12
13
  from bioguider.agents.agent_utils import increase_token_usage, read_file, summarize_file
@@ -84,6 +85,8 @@ Your final report must **exactly match** the following format. Do not add or omi
84
85
  **License Information Included:**
85
86
  * score: [Yes / No]
86
87
  * suggestions: <suggestions to improve **License Information**>
88
+ ** Code contributor / Author information included
89
+ * score: [Yes / No]
87
90
  **Overall Score:** [Poor / Fair / Good / Excellent]
88
91
 
89
92
  ---
@@ -257,6 +260,7 @@ class StructuredEvaluationREADMEResult(BaseModel):
257
260
  dependency_suggestions: Optional[str]=Field(description="Suggestions if dependencies are not clearly stated")
258
261
  license_score: Optional[bool]=Field(description="A boolean value, Are contributor or maintainer details provided?")
259
262
  license_suggestions: Optional[str]=Field(description="Suggestions to improve license information")
263
+ contributor_author_score: Optional[bool]=Field(description="A boolean value. are contributors or author included?")
260
264
  overall_score: str=Field(description="A overall scroll for the README quality, could be `Poor`, `Fair`, `Good`, or `Excellent`")
261
265
 
262
266
  class EvaluationREADMEResult(BaseModel):
@@ -300,9 +304,10 @@ class EvaluationREADMETask(EvaluationTask):
300
304
  repo_path: str,
301
305
  gitignore_path: str,
302
306
  meta_data: ProjectMetadata | None = None,
303
- step_callback: Callable | None = None
307
+ step_callback: Callable | None = None,
308
+ summarized_files_db = None,
304
309
  ):
305
- super().__init__(llm, repo_path, gitignore_path, meta_data, step_callback)
310
+ super().__init__(llm, repo_path, gitignore_path, meta_data, step_callback, summarized_files_db)
306
311
  self.evaluation_name = "README Evaluation"
307
312
 
308
313
  def _structured_evaluate(self, free_readme_evaluations: dict[str, dict]):
@@ -354,6 +359,7 @@ class EvaluationREADMETask(EvaluationTask):
354
359
  dependency_suggestions="No dependency provided",
355
360
  license_score=False,
356
361
  license_suggestions="No license information",
362
+ contributor_author_score=False,
357
363
  overall_score="Poor",
358
364
  ),
359
365
  "structured_reasoning_process": f"{readme_file} is an empty file.",
@@ -451,7 +457,7 @@ class EvaluationREADMETask(EvaluationTask):
451
457
  total_token_usage = increase_token_usage(total_token_usage, token_usage)
452
458
  return readme_evaluations, total_token_usage
453
459
 
454
- def _evaluate(self, files: list[str]) -> tuple[dict, dict]:
460
+ def _evaluate(self, files: list[str]) -> tuple[dict, dict, list[str]]:
455
461
  free_readme_evaluations, free_token_usage = self._free_evaluate(files)
456
462
  structured_readme_evaluations, structured_token_usage = self._structured_evaluate(free_readme_evaluations)
457
463
 
@@ -468,6 +474,26 @@ class EvaluationREADMETask(EvaluationTask):
468
474
 
469
475
  total_token_usage = increase_token_usage(free_token_usage, structured_token_usage)
470
476
 
471
- return combined_evaluations, total_token_usage
477
+ return combined_evaluations, total_token_usage, files
472
478
 
479
+ def _collect_files(self):
480
+ """
481
+ Search for a README file in the repository directory.
482
+ """
483
+ possible_readme_files = [
484
+ "readme.md",
485
+ "readme.rst",
486
+ "readme.txt",
487
+ "readme",
488
+ ]
489
+ repo_path = self.repo_path
490
+ gitignore_path = Path(repo_path, ".gitignore")
491
+ gitignore_checker = GitignoreChecker(
492
+ directory=repo_path, gitignore_path=gitignore_path
493
+ )
494
+ found_readme_files = gitignore_checker.check_files_and_folders(
495
+ check_file_cb=lambda root_dir, relative_path: Path(relative_path).name.lower() in possible_readme_files,
496
+ )
497
+
498
+ return found_readme_files
473
499
 
@@ -0,0 +1,153 @@
1
+
2
+ from typing import Optional
3
+ from pydantic import BaseModel, Field
4
+ from bioguider.agents.agent_utils import try_parse_json_object, try_parse_with_llm
5
+ from bioguider.agents.evaluation_task import EvaluationTask
6
+ from bioguider.agents.collection_task import CollectionTask
7
+ from bioguider.agents.identification_task import IdentificationTask
8
+ from bioguider.agents.prompt_utils import CollectionGoalItemEnum
9
+ from bioguider.agents.evaluation_installation_task import StructuredEvaluationInstallationResult
10
+ from bioguider.utils.constants import DEFAULT_TOKEN_USAGE
11
+
12
+ DEMO_INSTRUCTION_GOAL = """
13
+ 1. Identify if it provides the instructions to run on provided data
14
+ 2. Identify if it provides the instructions to run on custom data
15
+ 3. Identify if it provides the expected output
16
+ """
17
+
18
+ DEMO_INSTRUCTION_FINAL_ANSWER = \
19
+ '{{"run_on_data_instruction": <True or False>, "run_on_custom_instruction": <True or False>, "expected_output_description": <True Or False>}}'
20
+
21
+ class DemoInstructionsResult(BaseModel):
22
+ run_on_data_instruction: Optional[bool] = Field(description="A boolean value. Does it provide instructions on how to run on provided data?")
23
+ run_on_custom_instruction: Optional[bool] = Field(description="A boolean value. Does it provide instructions on how to run on custom data?")
24
+ expected_output_description: Optional[bool] = Field(description="A boolean value. Does it provide the description of expected output?")
25
+
26
+ class EvaluationSubmissionRequirementsTask(EvaluationTask):
27
+ def __init__(
28
+ self,
29
+ llm,
30
+ repo_path,
31
+ gitignore_path,
32
+ meta_data = None,
33
+ step_callback = None,
34
+ summarized_files_db = None,
35
+ readme_files_evaluation: dict | None = None,
36
+ installation_evaluation: dict | None = None,
37
+ installation_files: list[str] | None = None
38
+ ):
39
+ super().__init__(llm, repo_path, gitignore_path, meta_data, step_callback, summarized_files_db)
40
+ self.evaluation_name = "Submission Requirements Evaluation"
41
+ self.readme_files_evaluation = readme_files_evaluation
42
+ self.installation_evaluation = installation_evaluation
43
+ self.installation_files = installation_files
44
+
45
+ def _collect_software_package_content(self):
46
+ collection_task = CollectionTask(
47
+ llm = self.llm,
48
+ step_callback=self.step_callback,
49
+ summarize_instruction="We are collecting compiled standalone files, source code files and example data files.",
50
+ summarized_files_db=self.summarized_files_db,
51
+ )
52
+ collection_task.compile(
53
+ repo_path=self.repo_path,
54
+ gitignore_path=self.gitignore_path,
55
+ db=self.summarized_files_db,
56
+ goal_item=CollectionGoalItemEnum.SoftwarePackageContent.name,
57
+ )
58
+ files = collection_task.collect()
59
+
60
+ return files
61
+
62
+ def _evaluate_software_package_content(self):
63
+ files = self._collect_software_package_content()
64
+ if len(files) == 3:
65
+ return {
66
+ "compiled_standalone_software": files[0].strip().lower() != "n/a",
67
+ "source_code": files[1].strip().lower() != "n/a",
68
+ "demo_dataset": files[2].strip().lower() != "n/a",
69
+ }, files
70
+ else:
71
+ return {
72
+ "compiled_standalone_software": False,
73
+ "source_code": False,
74
+ "demo_dataset": False,
75
+ }, files
76
+
77
+ def _evaluatie_demo_instructions(self):
78
+ readme_files = [f for f in self.readme_files_evaluation.keys() \
79
+ if self.readme_files_evaluation[f]["evaluation"]["project_level"]]
80
+ installation_files = self.installation_files if self.installation_files is not None else []
81
+ provided_files = readme_files + installation_files
82
+ provided_files = provided_files if len(provided_files) > 0 else None
83
+ identify_task = IdentificationTask(
84
+ llm=self.llm,
85
+ step_callback=self.step_callback,
86
+ summarized_files_db=self.summarized_files_db,
87
+ provided_files=provided_files
88
+ )
89
+ identify_task.compile(
90
+ repo_path=self.repo_path,
91
+ gitignore_path=self.gitignore_path,
92
+ )
93
+ final_answer = identify_task.identify_customize_goal(
94
+ goal="demo instructions",
95
+ final_answer_example=DEMO_INSTRUCTION_FINAL_ANSWER,
96
+ )
97
+ final_answer = final_answer["final_answer"] \
98
+ if final_answer is not None and "final_answer" in final_answer else final_answer
99
+ parsed_obj = self._parse_demo_instruction_result(final_answer)
100
+ return parsed_obj, provided_files
101
+
102
+ def _parse_demo_instruction_result(self, result: str | dict):
103
+ if isinstance(result, dict):
104
+ return result
105
+ obj = try_parse_json_object(result)
106
+ if obj is None:
107
+ obj, token_usage = try_parse_with_llm(
108
+ llm=self.llm,
109
+ input_text=result,
110
+ schema=DemoInstructionsResult,
111
+ )
112
+ obj = vars(obj) if obj is not None else obj
113
+ self.print_step(token_usage=token_usage)
114
+ self.print_step(step_output=str(obj))
115
+
116
+ return obj
117
+
118
+
119
+ def _combine_evaluation(
120
+ self,
121
+ software_evaluation: dict,
122
+ demo_evaluation: dict,
123
+ ):
124
+ readme_files = [f for f in self.readme_files_evaluation.keys() \
125
+ if self.readme_files_evaluation[f]["evaluation"]["project_level"]]
126
+ structured_install_evaluation: StructuredEvaluationInstallationResult = \
127
+ self.installation_evaluation["structured_evaluation"]
128
+ software_dependency = structured_install_evaluation.dependency_number > 0
129
+ install_tutorial = structured_install_evaluation.install_tutorial
130
+ license = any([self.readme_files_evaluation[f]["structured_evaluation"].license_score for f in readme_files])
131
+ return {
132
+ **software_evaluation,
133
+ **demo_evaluation,
134
+ "complete_readme": len(readme_files) > 0,
135
+ "software_dependency": software_dependency,
136
+ "install_tutorial": install_tutorial,
137
+ "license": license,
138
+ }
139
+
140
+ def _evaluate(self, files):
141
+
142
+ software_evaluation, software_files = self._evaluate_software_package_content()
143
+ demo_evaluation, demo_files = self._evaluatie_demo_instructions()
144
+ files = list(set(software_files + demo_files))
145
+
146
+ return self._combine_evaluation(software_evaluation, demo_evaluation), {**DEFAULT_TOKEN_USAGE}, files
147
+
148
+
149
+ def _collect_files(self):
150
+ return []
151
+
152
+
153
+
@@ -9,6 +9,7 @@ from langchain_openai.chat_models.base import BaseChatOpenAI
9
9
 
10
10
  from bioguider.agents.agent_utils import read_file
11
11
  from bioguider.agents.prompt_utils import EVALUATION_INSTRUCTION
12
+ from bioguider.database.summarized_file_db import SummarizedFilesDb
12
13
  from bioguider.utils.constants import DEFAULT_TOKEN_USAGE, ProjectMetadata
13
14
  from .common_agent import CommonConversation
14
15
  from ..utils.pyphen_utils import PyphenReadability
@@ -158,7 +159,8 @@ class EvaluationTask(ABC):
158
159
  repo_path: str,
159
160
  gitignore_path: str,
160
161
  meta_data: ProjectMetadata | None = None,
161
- step_callback: Callable | None = None
162
+ step_callback: Callable | None = None,
163
+ summarized_files_db: SummarizedFilesDb | None=None,
162
164
  ):
163
165
  self.evaluation_name = ""
164
166
  self.llm = llm
@@ -166,6 +168,8 @@ class EvaluationTask(ABC):
166
168
  self.gitignore_path = gitignore_path
167
169
  self.step_callback = step_callback
168
170
  self.metadata = meta_data
171
+ self.summarized_files_db = summarized_files_db
172
+
169
173
  def print_step(
170
174
  self,
171
175
  step_name: str | None = None,
@@ -180,11 +184,12 @@ class EvaluationTask(ABC):
180
184
  token_usage=token_usage,
181
185
  )
182
186
 
183
- def evaluate(self, files: list[str] | None = None) -> dict:
187
+ def evaluate(self) -> dict:
184
188
  self._enter_evaluation()
185
- evaluations, token_usage = self._evaluate(files)
189
+ files = self._collect_files()
190
+ evaluations, token_usage, files = self._evaluate(files)
186
191
  self._leave_evaluation(token_usage)
187
- return evaluations
192
+ return evaluations, files
188
193
 
189
194
  def _enter_evaluation(self):
190
195
  self.print_step(step_name=self.evaluation_name)
@@ -196,6 +201,10 @@ class EvaluationTask(ABC):
196
201
  def _evaluate(self, files: list[str]) -> tuple[dict, dict]:
197
202
  pass
198
203
 
204
+ @abstractmethod
205
+ def _collect_files(self) -> list[str]:
206
+ pass
207
+
199
208
 
200
209
  EVALUATION_TUTORIAL_SYSTEM_PROMPT="""
201
210
  You are an expert in software documentation and developer education.
@@ -262,9 +271,10 @@ class EvaluationTutorialTask(EvaluationTask):
262
271
  repo_path: str,
263
272
  gitignore_path: str,
264
273
  meta_data: ProjectMetadata | None = None,
265
- step_callback: Callable | None = None
274
+ step_callback: Callable | None = None,
275
+ summarized_files_db = None,
266
276
  ):
267
- super().__init__(llm, repo_path, gitignore_path, meta_data, step_callback)
277
+ super().__init__(llm, repo_path, gitignore_path, meta_data, step_callback, summarized_files_db)
268
278
  self.evaluation_name = "Tutorial Evaluation"
269
279
 
270
280
  def _evaluate(self, files: list[str]) -> tuple[dict, dict]:
@@ -300,4 +310,7 @@ class EvaluationTutorialTask(EvaluationTask):
300
310
  self.print_step(step_output=response)
301
311
  evaluations[file] = response
302
312
  return evaluations, token_usage
313
+
314
+ def _collect_files(self):
315
+ return []
303
316
 
@@ -34,6 +34,9 @@ Carefully review the **Goal**, **Repository File Structure**, and **Intermediate
34
34
  ```
35
35
  Be precise and support your reasoning with evidence from the input.
36
36
 
37
+ ### **Important Instructions**
38
+ {important_instructions}
39
+
37
40
  ### Notes
38
41
  We are collecting information over multiple rounds, your thoughts and the output of this step will be persisted, so please **do not rush to provide a Final Answer**.
39
42
  If you find the current information insufficient, share your reasoning or thoughts instead—we’ll continue with the next round accordingly.
@@ -58,6 +61,8 @@ class IdentificationObserveStep(PEOCommonStep):
58
61
 
59
62
  def _prepare_system_prompt(self, state: IdentificationWorkflowState):
60
63
  goal = state["goal"]
64
+ important_instructions = "N/A" \
65
+ if not "observe_instructions" in state else state["observe_instructions"]
61
66
  final_answer_example = state["final_answer_example"]
62
67
  intermediate_output = self._build_intermediate_steps(state)
63
68
  prompt = ChatPromptTemplate.from_template(IDENTIFICATION_OBSERVATION_SYSTEM_PROMPT)
@@ -67,11 +72,12 @@ class IdentificationObserveStep(PEOCommonStep):
67
72
  repo_structure=self.repo_structure,
68
73
  intermediate_output=intermediate_output,
69
74
  final_answer_example=final_answer_example,
75
+ important_instructions=important_instructions,
70
76
  )
71
77
 
72
78
  def _execute_directly(self, state: IdentificationWorkflowState):
73
79
  system_prompt = self._prepare_system_prompt(state)
74
- agent = CommonAgentTwoChainSteps(llm=self.llm)
80
+ agent = CommonAgentTwoSteps(llm=self.llm)
75
81
  res, _, token_usage, reasoning_process = agent.go(
76
82
  system_prompt=system_prompt,
77
83
  instruction_prompt="Now, let's begin.",
@@ -36,6 +36,9 @@ meaning that states and variables will persisted through multiple rounds of plan
36
36
  developing your collection plan incrementally and reflect on the intermediate observations at each round, instead of coding up
37
37
  everything in one go. Be sure to take only one or two actions in each step.
38
38
 
39
+ ### **Important Instructions**
40
+ {important_instructions}
41
+
39
42
  ### **Output**
40
43
  You plan should follow this format:
41
44
  Step: tool name, should be one of {tool_names}
@@ -81,6 +84,7 @@ class IdentificationPlanStep(PEOCommonStep):
81
84
 
82
85
  def _prepare_system_prompt(self, state: IdentificationWorkflowState) -> str:
83
86
  goal = state["goal"]
87
+ important_instructions = "N/A" if not "plan_instructions" in state else state["plan_instructions"]
84
88
  repo_structure = self.repo_structure
85
89
  intermdediate_steps = self._build_intermediate_steps(state)
86
90
  step_analysis, step_thoughts = self._build_intermediate_analysis_and_thoughts(state)
@@ -101,6 +105,7 @@ class IdentificationPlanStep(PEOCommonStep):
101
105
  intermediate_analysis=step_analysis,
102
106
  intermediate_thoughts=step_thoughts,
103
107
  tool_names=tool_names,
108
+ important_instructions=important_instructions,
104
109
  )
105
110
 
106
111
  def _convert_to_plan_actions_text(self, actions: list[dict]) -> str:
@@ -113,7 +118,7 @@ class IdentificationPlanStep(PEOCommonStep):
113
118
 
114
119
  def _execute_directly(self, state: IdentificationWorkflowState):
115
120
  system_prompt = self._prepare_system_prompt(state)
116
- agent = CommonAgentTwoChainSteps(llm=self.llm)
121
+ agent = CommonAgentTwoSteps(llm=self.llm)
117
122
  res, _, token_usage, reasoning_process = agent.go(
118
123
  system_prompt=system_prompt,
119
124
  instruction_prompt="Now, let's begin.",
@@ -64,14 +64,17 @@ class IdentificationTask(AgentTask):
64
64
  self,
65
65
  llm: BaseChatOpenAI,
66
66
  step_callback: Callable | None=None,
67
+ summarized_files_db: SummarizedFilesDb | None = None,
68
+ provided_files: list[str] | None = None,
67
69
  ):
68
- super().__init__(llm=llm, step_callback=step_callback)
70
+ super().__init__(llm=llm, step_callback=step_callback, summarized_files_db=summarized_files_db)
69
71
  self.repo_path: str | None = None
70
72
  self.gitignore_path: str | None = None
71
73
  self.repo_structure: str | None = None
72
74
  self.tools = []
73
75
  self.custom_tools = []
74
76
  self.steps: list[PEOCommonStep] = []
77
+ self.provided_files = provided_files
75
78
 
76
79
  def _prepare_tools(self):
77
80
  tool_rd = read_directory_tool(repo_path=self.repo_path)
@@ -79,7 +82,7 @@ class IdentificationTask(AgentTask):
79
82
  llm=self.llm,
80
83
  repo_path=self.repo_path,
81
84
  output_callback=self.step_callback,
82
- db=self.summary_file_db,
85
+ db=self.summarized_files_db,
83
86
  )
84
87
  tool_rf = read_file_tool(repo_path=self.repo_path)
85
88
 
@@ -106,7 +109,9 @@ class IdentificationTask(AgentTask):
106
109
  def _initialize(self):
107
110
  if not os.path.exists(self.repo_path):
108
111
  raise ValueError(f"Repository path {self.repo_path} does not exist.")
109
- files = read_directory(self.repo_path, os.path.join(self.repo_path, ".gitignore"))
112
+ files = self.provided_files
113
+ if files is None:
114
+ files = read_directory(self.repo_path, os.path.join(self.repo_path, ".gitignore"))
110
115
  file_pairs = [(f, get_file_type(os.path.join(self.repo_path, f)).value) for f in files]
111
116
  self.repo_structure = ""
112
117
  for f, f_type in file_pairs:
@@ -188,7 +193,21 @@ class IdentificationTask(AgentTask):
188
193
  meta_data = s["final_answer"] if "final_answer" in s else "unknown type"
189
194
  return self._parse_meta_data(meta_data)
190
195
 
191
-
196
+ def identify_customize_goal(
197
+ self,
198
+ goal: str,
199
+ final_answer_example: str,
200
+ plan_instructions: str = "N/A",
201
+ observe_instructions: str = "N/A",
202
+ ):
203
+ s = self._go_graph({
204
+ "goal": goal,
205
+ "final_answer_example": final_answer_example,
206
+ "plan_instructions": plan_instructions,
207
+ "observe_instructions": observe_instructions,
208
+ })
209
+ return s["final_answer"] if "final_answer" in s else None
210
+
192
211
  def _parse_project_type(self, proj_type_obj: str) -> ProjectTypeEnum:
193
212
  proj_type_obj = proj_type_obj.strip()
194
213
  the_obj = try_parse_json_object(proj_type_obj)
@@ -10,6 +10,8 @@ class IdentificationWorkflowState(TypedDict):
10
10
 
11
11
  plan_actions: Optional[str]
12
12
  plan_reasoning: Optional[str]
13
+ plan_instructions: Optional[str]
14
+ observe_instructions: Optional[str]
13
15
  intermediate_steps: Optional[list[str]]
14
16
  final_answer: Optional[str]
15
17
  final_answer_example: Optional[str]
@@ -91,6 +91,7 @@ class CollectionGoalItemEnum(Enum):
91
91
  Installation = "Installation"
92
92
  License = "License"
93
93
  Contributing = "Contributing"
94
+ SoftwarePackageContent = "SoftwarePackageContent"
94
95
 
95
96
 
96
97
 
@@ -147,12 +148,18 @@ If **any one** of these is present, the document should be classified as a Docke
147
148
  * Examples: `example.py`, `main.py`, `demo.R`, `notebooks/get_started.ipynb`, etc.
148
149
  * These should be runnable with minimal configuration.""",
149
150
 
150
- "important_instructions": """- Only include minimal code examples that demonstrate basic functionality.
151
+ "plan_important_instructions": """- Only include minimal code examples that demonstrate basic functionality.
151
152
  If multiple example files are found, select only the simplest and most lightweight one that is sufficient to verify the repository works.
152
153
  - Give priority to analyzing files whose names include **"install"** or **"Dockerfile"**, as these are most likely to be useful for generating our Dockerfile
153
154
  - The total number of collected files should **not exceed 5**.
154
155
  - Make sure to include **only one code example**, selecting the most minimal and representative one.
155
- """
156
+ """,
157
+ "observe_important_instructions": """- Only include minimal code examples that demonstrate basic functionality.
158
+ If multiple example files are found, select only the simplest and most lightweight one that is sufficient to verify the repository works.
159
+ - Give priority to analyzing files whose names include **"install"** or **"Dockerfile"**, as these are most likely to be useful for generating our Dockerfile
160
+ - The total number of collected files should **not exceed 5**.
161
+ - Make sure to include **only one code example**, selecting the most minimal and representative one.
162
+ """,
156
163
  },
157
164
  "Installation": {
158
165
  "goal_item": "Installation Instructions",
@@ -163,11 +170,16 @@ If **any one** of these is present, the document should be classified as Install
163
170
  - Configuration steps required to get the software running.
164
171
  - Troubleshooting tips related to installation issues.
165
172
  - You can include directory names if all files in the directory are relevant to the goal item.""",
166
- "important_instructions": """- Give priority to analyzing README file that contain installation instructions and the files whose names include **"install"** or **"setup"**.
173
+ "plan_important_instructions": """ - Give priority to analyzing README file that contain installation instructions and the files whose names include **"install"** or **"setup"**.
167
174
  - If multiple files are found, select the most comprehensive one that covers the installation process.
168
175
  - The total number of collected files should **not exceed 3**.
169
176
  - Identify and select **no more than three** installation instruction files — choose the most comprehensive and representative ones.
170
- """
177
+ """,
178
+ "observe_important_instructions": """ - Give priority to analyzing README file that contain installation instructions and the files whose names include **"install"** or **"setup"**.
179
+ - If multiple files are found, select the most comprehensive one that covers the installation process.
180
+ - The total number of collected files should **not exceed 3**.
181
+ - Identify and select **no more than three** installation instruction files — choose the most comprehensive and representative ones.
182
+ """,
171
183
  },
172
184
  "License": {
173
185
  "goal_item": "License Information",
@@ -187,6 +199,34 @@ If **any one** of these is present, the document should be classified as Contrib
187
199
  - Any file that contains instructions for developers on how to contribute to the project, including coding standards, testing procedures, and submission processes.
188
200
  - You can include directory names if all files in the directory are relevant to the goal item.""",
189
201
  },
202
+ "SoftwarePackageContent": {
203
+ "goal_item": "Software Package Content",
204
+ "related_file_description": """A file qualifies as **Software Package Content** if it meets **at least one** of the following elements.
205
+ - A compiled binary file that may be qualified as a compiled standalone software, please carefully analyze a binary file and its file name to identify if it is a compiled standalone software
206
+ - A source code file, like a file whose extension is `.py`, `.R`, `.ipynb`, `.ts`, or `.js`.
207
+ - An example data which is used to demonstrate usage or for tutorial. Image file should not be considered as example data.
208
+ """,
209
+ "plan_important_instructions": """ - A comiled standalone software file is non-textual and appears to be in an executable format (e.g., `.exe`, `.dll`, `.so`, `.bin`, `.elf`).
210
+ - A comiled standalone software file **is not a script or compiled library**, that is, It is not a wrapper script (e.g., shell, Python, Python notebook or Rmd) nor a dynamic/shared library meant for linking.
211
+ - When identifying source code file, prioritize analyzing the file's **extension** and **file name** and try to avoid reading file, using check_file_related_tool or summarizing file content.
212
+ - When identifying example data, prioritize analyzing the file's **extension** (like .dat, .csv, .fastq, and so on) and **file name** (like example_data.txt, example.dat, and so on). If extension/name is ambiguous, use summarizing file content to decide.
213
+ - **Note**: You **only need to detect** whether at least **one** compiled standalone software file, **one** source code file and **one** example data file exist — no need to list all such files.
214
+ """,
215
+ "observe_important_instructions": """ - A comiled standalone software file is non-textual and appears to be in an executable format (e.g., `.exe`, `.dll`, `.so`, `.bin`, `.elf`).
216
+ - A comiled standalone software file **is not a script or compiled library**, that is, It is not a wrapper script (e.g., shell, Python, Python notebook or Rmd) nor a dynamic/shared library meant for linking.
217
+ - When identifying source code file, prioritize analyzing the file's **extension** and **file name** and try to avoid reading file, using check_file_related_tool or summarizing file content.
218
+ - When identifying example data, prioritize analyzing the file's **extension** (like .dat, .csv, .fastq, and so on) and **file name** (like example_data.txt, example.dat, and so on). If extension/name is ambiguous, use summarizing file content to decide.
219
+ - **Note**: You **only need to detect** whether at least **one** compiled standalone software file, **one** source code file and **one** example data file exist — no need to list all such files.
220
+ - **Final answer format**: If you believe **all relevant files** have been collected:
221
+ Your final answer **must exactly match** the following format:
222
+ **FinalAnswer:** {{"final_answer": [<N/A or a compiled filename>, <N/A or a source file name>, <N/A or a example data file name>]}}
223
+ For each category, return a single file name or `"N/A"` if none found. And the return array must exactly follow this order: [<A comiled standalone software file name>, <A source code file name>, <A example data file name>]
224
+ For example, **FinalAnswer:** {{"final_answer": ["N/A", "app.py", "example.csv"]}} indicates:
225
+ * No compiled standalone software found
226
+ * `app.py` found as source code
227
+ * `example.csv` found as example data
228
+ """,
229
+ },
190
230
  }
191
231
 
192
232
 
@@ -11,6 +11,7 @@ from ..utils.file_utils import parse_repo_url
11
11
  from ..database.summarized_file_db import SummarizedFilesDb
12
12
  from ..agents.evaluation_readme_task import EvaluationREADMETask
13
13
  from ..agents.evaluation_installation_task import EvaluationInstallationTask
14
+ from ..agents.evaluation_submission_requirements_task import EvaluationSubmissionRequirementsTask
14
15
  from ..agents.collection_task import CollectionTask
15
16
 
16
17
  class EvaluationManager:
@@ -65,69 +66,60 @@ class EvaluationManager:
65
66
  gitignore_path=Path(self.rag.repo_dir, ".gitignore"),
66
67
  meta_data=self.project_metadata,
67
68
  step_callback=self.step_callback,
69
+ summarized_files_db=self.summary_file_db,
68
70
  )
69
- readme_files = self._find_readme_files()
70
- results = task.evaluate(readme_files)
71
+ # readme_files = self._find_readme_files()
72
+ results, readme_files = task.evaluate()
71
73
  return results, readme_files
72
74
 
73
75
  def evaluate_tutorial(self):
74
- task = CollectionTask(
75
- llm=self.llm,
76
- step_callback=self.step_callback,
77
- )
78
- task.compile(
79
- repo_path=self.rag.repo_dir,
80
- gitignore_path=Path(self.rag.repo_dir, ".gitignore"),
81
- db=self.summary_file_db,
82
- goal_item=CollectionGoalItemEnum.Tutorial.name,
83
- )
84
- s = task.collect()
85
- if s is None or 'final_answer' not in s:
86
- return None
76
+ pass
77
+ # task = CollectionTask(
78
+ # llm=self.llm,
79
+ # step_callback=self.step_callback,
80
+ # )
81
+ # task.compile(
82
+ # repo_path=self.rag.repo_dir,
83
+ # gitignore_path=Path(self.rag.repo_dir, ".gitignore"),
84
+ # db=self.summary_file_db,
85
+ # goal_item=CollectionGoalItemEnum.Tutorial.name,
86
+ # )
87
+ # s = task.collect()
88
+ # if s is None or 'final_answer' not in s:
89
+ # return None
87
90
 
88
91
  def evaluate_installation(self):
89
- task = CollectionTask(
92
+ evaluation_task = EvaluationInstallationTask(
90
93
  llm=self.llm,
91
- step_callback=self.step_callback,
92
- )
93
- task.compile(
94
94
  repo_path=self.rag.repo_dir,
95
95
  gitignore_path=Path(self.rag.repo_dir, ".gitignore"),
96
- db=self.summary_file_db,
97
- goal_item=CollectionGoalItemEnum.Installation.name,
96
+ meta_data=self.project_metadata,
97
+ step_callback=self.step_callback,
98
98
  )
99
- files = task.collect()
100
- if files is None or len(files) == 0:
101
- return None
102
- evaluation_task = EvaluationInstallationTask(
99
+ evaluation, files = evaluation_task.evaluate()
100
+ return evaluation, files
101
+
102
+ def evaluate_submission_requirements(
103
+ self,
104
+ readme_files_evaluation: dict | None = None,
105
+ installation_files: list[str] | None = None,
106
+ installation_evaluation: dict[str] | None = None,
107
+ ):
108
+ evaluation_task = EvaluationSubmissionRequirementsTask(
103
109
  llm=self.llm,
104
110
  repo_path=self.rag.repo_dir,
105
111
  gitignore_path=Path(self.rag.repo_dir, ".gitignore"),
106
112
  meta_data=self.project_metadata,
107
113
  step_callback=self.step_callback,
114
+ summarized_files_db=self.summary_file_db,
115
+ readme_files_evaluation=readme_files_evaluation,
116
+ installation_files=installation_files,
117
+ installation_evaluation=installation_evaluation,
108
118
  )
109
- evaluation = evaluation_task.evaluate(files)
119
+ evaluation, files = evaluation_task.evaluate()
120
+
110
121
  return evaluation, files
111
122
 
112
- def _find_readme_files(self) -> list[str]:
113
- """
114
- Search for a README file in the repository directory.
115
- """
116
- possible_readme_files = [
117
- "readme.md",
118
- "readme.rst",
119
- "readme.txt",
120
- "readme",
121
- ]
122
- repo_path = self.rag.repo_dir
123
- gitignore_path = Path(repo_path, ".gitignore")
124
- gitignore_checker = GitignoreChecker(
125
- directory=repo_path, gitignore_path=gitignore_path
126
- )
127
- found_readme_files = gitignore_checker.check_files_and_folders(
128
- check_file_cb=lambda root_dir, relative_path: Path(relative_path).name.lower() in possible_readme_files,
129
- )
130
-
131
- return found_readme_files
123
+
132
124
 
133
125
 
@@ -38,3 +38,5 @@ class ProjectMetadata:
38
38
  self.description = description
39
39
  self.license = license
40
40
 
41
+ MAX_FILE_LENGTH=10 *1024 # 10K
42
+ MAX_SENTENCE_NUM=20
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bioguider
3
- Version: 0.2.11
3
+ Version: 0.2.13
4
4
  Summary: An AI-Powered package to help biomedical developers to generate clear documentation
5
5
  License: MIT
6
6
  Author: Cankun Wang
@@ -1,49 +1,50 @@
1
1
  bioguider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  bioguider/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- bioguider/agents/agent_task.py,sha256=FrWCq_mG-Oo745qcZT3Lai4rd8hQ5IGK3jMNe1vFQrs,2820
4
- bioguider/agents/agent_tools.py,sha256=YWF44vGjTzK0H9dxfdZyJ5K2H4z2j1bz-Q0bVw1UoE8,7014
5
- bioguider/agents/agent_utils.py,sha256=FxZsssGapnHe0zruopfuBcctkEHL0zaA9So7dJvvtAg,13671
3
+ bioguider/agents/agent_task.py,sha256=B_QGkoA96GdrYSX29TQ_tct_y8z4zS4l5Cn0-eph88k,2863
4
+ bioguider/agents/agent_tools.py,sha256=r21wHV6a-Ic2T0dk4YzA-_d7PodHPM3GzRxJqv-llSw,7286
5
+ bioguider/agents/agent_utils.py,sha256=FFFhZvL_THvZ2Y7QwFmi3jsUd1i7PYi9tPJNMTVUsgY,14702
6
6
  bioguider/agents/collection_execute_step.py,sha256=Ev4BLjjmBdsc52M1zrq7QK8g7fsffDkSxu-jN2rvedw,5614
7
- bioguider/agents/collection_observe_step.py,sha256=iNeV6f16Emk1LMStSR4FXBPZ6Sc0eTjwxEfmoeegV-U,4554
8
- bioguider/agents/collection_plan_step.py,sha256=mx-_5Y3pqKDPBaMMyFElKlpq1GWN7g03ZplnlTr9ppE,5699
9
- bioguider/agents/collection_task.py,sha256=CLtPOqhlOgAfysMX2WYiGs3_O9W7qp3kh0wck6COiac,7304
10
- bioguider/agents/collection_task_utils.py,sha256=WRzzpMV6r8aY0FlX_zroHbLDXyrmvS48OSiBr_fIq2Q,3677
7
+ bioguider/agents/collection_observe_step.py,sha256=N_P5NadFa0usO0M9cXXJvKJoZofwcDW0cPSfnwvPEO4,4786
8
+ bioguider/agents/collection_plan_step.py,sha256=Nn0f8AOkEDCDtnhaqE7yCQoi7PVpsHmiUcsIqC0T0dQ,5956
9
+ bioguider/agents/collection_task.py,sha256=ZLUxDgh8OkY5INMfC283RdxEYCZJTksoLDNEMfKg-3s,7865
10
+ bioguider/agents/collection_task_utils.py,sha256=CmOh3HZEocuLj5VZCkLbD6P8O5tzyuyFa8Ykd-1GPGE,5356
11
11
  bioguider/agents/common_agent.py,sha256=eGs8m8bjO0dmW6lDIen7DQNdWdHD7j8Udf3XhL1k6vI,5242
12
12
  bioguider/agents/common_agent_2step.py,sha256=Vton0RKtmMyEgIIFhnBk4CFU_hynX0LvwREcZ9kvMxQ,7918
13
13
  bioguider/agents/common_step.py,sha256=GdOCbmj1pwh4etg-futVFYVDQuoUG89DnIrw-B6QbzM,2594
14
14
  bioguider/agents/dockergeneration_execute_step.py,sha256=F92jDlkc6KjAvTkX7q1FsCYP8J15SCaNgmwh3YPqfDo,6500
15
15
  bioguider/agents/dockergeneration_observe_step.py,sha256=93PO_Y4YyUShVTKRt0nErcjb-xYTcwcZCj7TgniS9t4,6098
16
16
  bioguider/agents/dockergeneration_plan_step.py,sha256=SB8tQM9PkIKsD2o1DFD7bedcxz6r6hSy8n_EVK60Fz0,7235
17
- bioguider/agents/dockergeneration_task.py,sha256=ezsweVHJsFpOyOI6rYMt1DZ3PE19dcq4J3Lm-d0IA8M,6220
17
+ bioguider/agents/dockergeneration_task.py,sha256=mYmorLKnJ-Jku3Qq_Y_kcSTsbYIo3RiVdD0puxqXY5Q,6221
18
18
  bioguider/agents/dockergeneration_task_utils.py,sha256=v7emqrJlVW-A5ZdLmPSdiaMSKCR8uzy9UYzx_1cgzyo,9041
19
- bioguider/agents/evaluation_installation_task.py,sha256=Lxgp4GZo6zZjSLV1bibnIbO03wH5klcLexALU8c5-lo,10195
20
- bioguider/agents/evaluation_readme_task.py,sha256=QqCnTwPy4r_WnmdNlY5CkLRi94NSWLFt-cpk4urynR0,21492
21
- bioguider/agents/evaluation_task.py,sha256=e-yJWhty9hvlvWaMYRoSoZs6Sjq9eLzBxtJstYaEIKY,12261
19
+ bioguider/agents/evaluation_installation_task.py,sha256=9AVE5PJB69aoDX0WMkye0UkYstSU2CsjzVB3eaPrLQo,11128
20
+ bioguider/agents/evaluation_readme_task.py,sha256=_v7ESqMurOg4UXCGqc1zmaVscBx3QbznrUdAKQH9Zws,22597
21
+ bioguider/agents/evaluation_submission_requirements_task.py,sha256=rFCI6bTl64kRiUkEwbh6Ef1LV-YqrgJhGkHaaqE_Pp8,6647
22
+ bioguider/agents/evaluation_task.py,sha256=4VZ7l8oqcUsgJ2YY6s6mkcJu25DgA1qLXv2kVUm2SgI,12654
22
23
  bioguider/agents/identification_execute_step.py,sha256=w3IjL8f2WiHCyiLjVSoySnIAXpi1-hK1DLKCnXbAN2Y,5587
23
- bioguider/agents/identification_observe_step.py,sha256=j4Fniv86jljkClTFc-p3pA39_zxhGJLPS9K7jNpxhJ0,3750
24
- bioguider/agents/identification_plan_step.py,sha256=p0BKziXdB4ph4D_T9FU5bH8CbHD5Gv0YuszMds_xh-Y,5224
25
- bioguider/agents/identification_task.py,sha256=qJ46FdmctibXIzO4C2wBwXR7VLHUksBtFiILH2eIHB4,9277
26
- bioguider/agents/identification_task_utils.py,sha256=5gevknha9hJiiQN5L7Yp9-pyhAlbR_j31aGRK5j0D_w,522
24
+ bioguider/agents/identification_observe_step.py,sha256=U-iWDR1AZIUpthEswtMbMkPK4YAbAv2SrvBJAqdKyZo,3988
25
+ bioguider/agents/identification_plan_step.py,sha256=owsTK1NZIuiZL7QPVknJyp9TBRK-mhnuf2RwK4YzaxU,5442
26
+ bioguider/agents/identification_task.py,sha256=hVhgExCv1OPMgOYOGRRJyR3-uiG-VR-OgrvWT6vLn9M,10031
27
+ bioguider/agents/identification_task_utils.py,sha256=nWRK3kCyiglw7576idiDGXEzUBBInkz_w9OsK6OJv2E,599
27
28
  bioguider/agents/peo_common_step.py,sha256=iw2c1h7X11WJzSE2tSRg0UAoXH0QOlQDxW9CCzSVMOY,2677
28
- bioguider/agents/prompt_utils.py,sha256=qjHEvqyHLazGAc3PEx_-QN3rCy2-WYnC3mbUigwPtEM,12530
29
+ bioguider/agents/prompt_utils.py,sha256=M-KUqGPhtOyFlDN1yNNZdOxTOPFjACF5VhBFW7gXgSc,17151
29
30
  bioguider/agents/python_ast_repl_tool.py,sha256=o7-4P1h8jS8ikhGSA4CI_OWQ2a0Eg5tEdmuAp_qrO-0,2519
30
31
  bioguider/agents/rag_collection_task.py,sha256=r_jPAMjQcC7dIydKxX77UuMqjJ3MiVKswNZ-yNw7yx8,5199
31
32
  bioguider/conversation.py,sha256=DIvk_d7pz_guuORByK1eaaF09FAK-8shcNTrbSUHz9Y,1779
32
33
  bioguider/database/summarized_file_db.py,sha256=tDSi2iCvm2-lrx0rBJo0C11gYl9FswsDZTG2-Yhu5cE,4646
33
- bioguider/managers/evaluation_manager.py,sha256=czLjC3Cl6Xb1R2-sKFUHUNVle_G3O-g66x0-LISdP_w,4917
34
+ bioguider/managers/evaluation_manager.py,sha256=O8mxrAGllDIkcQVsrRrqxH0eyJHwtoSauWrPe_F7qqU,4778
34
35
  bioguider/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
36
  bioguider/rag/config.py,sha256=5g4IqTzgyfZfax9Af9CTkXShgItPOt4_9TEMSekCPik,4602
36
37
  bioguider/rag/data_pipeline.py,sha256=bkJ2IUCgPx_OL2uZtPd6cIBor2VFZEIfGd5kVlmiPjw,27292
37
38
  bioguider/rag/embedder.py,sha256=jofR8hOj3Aj2IyBQ9y6FeAc84tgq5agbIfCGyFxYpJ8,650
38
39
  bioguider/rag/rag.py,sha256=JFPwrJlKDSyd3U3Gce_NSxI5343eNUbqPG9Fs5Pfoq0,4696
39
40
  bioguider/settings.py,sha256=BD_iz9aYarxmWUl0XaKl4-D4oTXMhFzljsXLNn2phis,3143
40
- bioguider/utils/constants.py,sha256=_xMAhwE3py2RR0pIimnb2qfucXdnTj4ZNeKGACouh2w,932
41
+ bioguider/utils/constants.py,sha256=4PbzF-s49M0nNtGsLjxQ9-APaJqNAjCQrE0wunSvPqw,982
41
42
  bioguider/utils/default.gitignore,sha256=XjPdyO2KV8z8iyuqluaNR_70tBQftMpyKL8HboVNyeI,1605
42
43
  bioguider/utils/file_utils.py,sha256=9VfAHsz1UkFPtzAmvWZvPl1TMaKIYNjNlLgsfB8tNjg,3683
43
44
  bioguider/utils/gitignore_checker.py,sha256=pOYUwsS9D5014LxcZb0cj3s2CAYaD2uF_pYJpaNKcho,6532
44
45
  bioguider/utils/pyphen_utils.py,sha256=cdZc3qphkvMDeL5NiZ8Xou13M_uVNP7ifJ-FwxO-0BE,2680
45
46
  bioguider/utils/utils.py,sha256=YP3HXgU_rvYDWkEcTzWGiYZw-mlfVrqGhUGSc0_4Pms,900
46
- bioguider-0.2.11.dist-info/LICENSE,sha256=qzkvZcKwwA5DuSuhXMOm2LcO6BdEr4V7jwFZVL2-jL4,1065
47
- bioguider-0.2.11.dist-info/METADATA,sha256=Vy7ZfE7701TohPSZoK9H_VoEhvw7pLyPvvvBkhs8RTY,1868
48
- bioguider-0.2.11.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
49
- bioguider-0.2.11.dist-info/RECORD,,
47
+ bioguider-0.2.13.dist-info/LICENSE,sha256=qzkvZcKwwA5DuSuhXMOm2LcO6BdEr4V7jwFZVL2-jL4,1065
48
+ bioguider-0.2.13.dist-info/METADATA,sha256=HpVwAdlrLxjQ5JVIm-prHV33vHyxPHi3IID4x8l-l_c,1868
49
+ bioguider-0.2.13.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
50
+ bioguider-0.2.13.dist-info/RECORD,,