bioguider 0.2.19__py3-none-any.whl → 0.2.20__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.
- bioguider/agents/agent_utils.py +5 -3
- bioguider/agents/collection_execute_step.py +1 -1
- bioguider/agents/common_conversation.py +20 -2
- bioguider/agents/consistency_collection_execute_step.py +152 -0
- bioguider/agents/consistency_collection_observe_step.py +128 -0
- bioguider/agents/consistency_collection_plan_step.py +128 -0
- bioguider/agents/consistency_collection_task.py +109 -0
- bioguider/agents/consistency_collection_task_utils.py +137 -0
- bioguider/agents/evaluation_task.py +2 -2
- bioguider/agents/evaluation_userguide_prompts.py +162 -0
- bioguider/agents/evaluation_userguide_task.py +164 -0
- bioguider/agents/prompt_utils.py +11 -8
- bioguider/database/code_structure_db.py +489 -0
- bioguider/generation/__init__.py +39 -0
- bioguider/generation/change_planner.py +140 -0
- bioguider/generation/document_renderer.py +47 -0
- bioguider/generation/llm_cleaner.py +43 -0
- bioguider/generation/llm_content_generator.py +69 -0
- bioguider/generation/llm_injector.py +270 -0
- bioguider/generation/models.py +77 -0
- bioguider/generation/output_manager.py +54 -0
- bioguider/generation/repo_reader.py +37 -0
- bioguider/generation/report_loader.py +151 -0
- bioguider/generation/style_analyzer.py +36 -0
- bioguider/generation/suggestion_extractor.py +136 -0
- bioguider/generation/test_metrics.py +104 -0
- bioguider/managers/evaluation_manager.py +24 -0
- bioguider/managers/generation_manager.py +160 -0
- bioguider/managers/generation_test_manager.py +74 -0
- bioguider/utils/code_structure_builder.py +42 -0
- bioguider/utils/file_handler.py +65 -0
- {bioguider-0.2.19.dist-info → bioguider-0.2.20.dist-info}/METADATA +1 -1
- {bioguider-0.2.19.dist-info → bioguider-0.2.20.dist-info}/RECORD +35 -10
- {bioguider-0.2.19.dist-info → bioguider-0.2.20.dist-info}/LICENSE +0 -0
- {bioguider-0.2.19.dist-info → bioguider-0.2.20.dist-info}/WHEEL +0 -0
bioguider/agents/agent_utils.py
CHANGED
|
@@ -289,8 +289,9 @@ class CustomOutputParser(AgentOutputParser):
|
|
|
289
289
|
action_input = match.group(2)
|
|
290
290
|
# Return the action and action input
|
|
291
291
|
action_dict = None
|
|
292
|
-
|
|
293
|
-
action_input_replaced =
|
|
292
|
+
action_input_replaced = action_input.strip().strip(" ").strip('"').strip('`').strip()
|
|
293
|
+
action_input_replaced = action_input_replaced.replace("'", '"')
|
|
294
|
+
action_input_replaced = action_input_replaced.replace("`", '"')
|
|
294
295
|
try:
|
|
295
296
|
action_dict = json.loads(action_input_replaced)
|
|
296
297
|
except json.JSONDecodeError:
|
|
@@ -299,10 +300,11 @@ class CustomOutputParser(AgentOutputParser):
|
|
|
299
300
|
# try using ast to parse input string
|
|
300
301
|
import ast
|
|
301
302
|
try:
|
|
302
|
-
action_dict = ast.literal_eval(
|
|
303
|
+
action_dict = ast.literal_eval(action_input_replaced)
|
|
303
304
|
if not isinstance(action_dict, dict):
|
|
304
305
|
action_dict = None
|
|
305
306
|
except Exception as e:
|
|
307
|
+
logger.error(f"Error parsing action input: {action_input} -> {action_input_replaced}\n{e}")
|
|
306
308
|
pass
|
|
307
309
|
return AgentAction(
|
|
308
310
|
tool=action,
|
|
@@ -144,7 +144,7 @@ class CollectionExecuteStep(PEOCommonStep):
|
|
|
144
144
|
agent_executor = AgentExecutor(
|
|
145
145
|
agent=agent,
|
|
146
146
|
tools=self.custom_tools,
|
|
147
|
-
max_iterations=
|
|
147
|
+
max_iterations=30,
|
|
148
148
|
)
|
|
149
149
|
response = agent_executor.invoke(
|
|
150
150
|
input={"plan_actions": plan_actions, "input": "Now, let's begin."},
|
|
@@ -19,8 +19,26 @@ class CommonConversation:
|
|
|
19
19
|
callbacks=[callback_handler]
|
|
20
20
|
)
|
|
21
21
|
response = result.generations[0][0].text
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
# Try to normalize token usage across providers
|
|
23
|
+
token_usage = {}
|
|
24
|
+
try:
|
|
25
|
+
if hasattr(result, "llm_output") and result.llm_output is not None:
|
|
26
|
+
raw = result.llm_output.get("token_usage") or result.llm_output.get("usage")
|
|
27
|
+
if isinstance(raw, dict):
|
|
28
|
+
token_usage = {
|
|
29
|
+
"total_tokens": raw.get("total_tokens") or raw.get("total"),
|
|
30
|
+
"prompt_tokens": raw.get("prompt_tokens") or raw.get("prompt"),
|
|
31
|
+
"completion_tokens": raw.get("completion_tokens") or raw.get("completion"),
|
|
32
|
+
}
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
if not token_usage:
|
|
36
|
+
token_usage = {
|
|
37
|
+
"total_tokens": getattr(callback_handler, "total_tokens", 0),
|
|
38
|
+
"prompt_tokens": getattr(callback_handler, "prompt_tokens", 0),
|
|
39
|
+
"completion_tokens": getattr(callback_handler, "completion_tokens", 0),
|
|
40
|
+
}
|
|
41
|
+
return response, token_usage
|
|
24
42
|
|
|
25
43
|
def generate_with_schema(self, system_prompt: str, instruction_prompt: str, schema: any):
|
|
26
44
|
system_prompt = escape_braces(system_prompt)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from langchain_openai.chat_models.base import BaseChatOpenAI
|
|
4
|
+
from langchain.tools import BaseTool, StructuredTool
|
|
5
|
+
from langchain.agents import AgentExecutor, create_react_agent
|
|
6
|
+
from langchain_community.callbacks.openai_info import OpenAICallbackHandler
|
|
7
|
+
|
|
8
|
+
from bioguider.agents.consistency_collection_task_utils import ConsistencyCollectionWorkflowState
|
|
9
|
+
from bioguider.agents.consistency_collection_task_utils import ConsistencyCollectionWorkflowState
|
|
10
|
+
from bioguider.database.code_structure_db import CodeStructureDb
|
|
11
|
+
from bioguider.utils.constants import DEFAULT_TOKEN_USAGE
|
|
12
|
+
from bioguider.agents.agent_utils import CustomOutputParser, CustomPromptTemplate
|
|
13
|
+
from bioguider.agents.peo_common_step import (
|
|
14
|
+
PEOCommonStep,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
CONSISTENCY_EVAL_EXECUTION_SYSTEM_PROMPT = """You are an expert developer specializing in the biomedical domain.
|
|
20
|
+
|
|
21
|
+
You are given a **plan** and are expected to complete it using the available tools.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
### **Available Tools**
|
|
26
|
+
{tools}
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### **Your Task**
|
|
31
|
+
|
|
32
|
+
Your job is to **execute the given plan step by step**, using the tools available to you.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
### **Output Format (Strict Order Required)**
|
|
37
|
+
|
|
38
|
+
For **each step**, follow the **exact format** below and **do not change the order of the fields** under any circumstances:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
Thought: Describe what you are thinking or planning to do next.
|
|
42
|
+
Action: The tool you are going to use (must be one of: {tool_names})
|
|
43
|
+
Action Input: The input provided to the selected action
|
|
44
|
+
Observation: The result returned by the action
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### **Important Instructions**
|
|
50
|
+
1. You may repeat the **Thought → Action → Action Input → Observation** loop as needed.
|
|
51
|
+
2. Once all steps in the plan have been executed, output all the results using this format:
|
|
52
|
+
3. For each step, **only execute one tool**.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Thought: I have completed the plan.
|
|
56
|
+
Final Answer:
|
|
57
|
+
Action: <tool_name>
|
|
58
|
+
Action Input: <input>
|
|
59
|
+
Action Observation: <Observation1>
|
|
60
|
+
---
|
|
61
|
+
Action: <tool_name>
|
|
62
|
+
Action Input: <input>
|
|
63
|
+
Action Observation: <Observation2>
|
|
64
|
+
---
|
|
65
|
+
...
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### **Plan**
|
|
71
|
+
{plan_actions}
|
|
72
|
+
|
|
73
|
+
### **Actions Already Taken**
|
|
74
|
+
{agent_scratchpad}
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
{input}
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
class ConsistencyCollectionExecuteStep(PEOCommonStep):
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
llm: BaseChatOpenAI,
|
|
85
|
+
code_structure_db: CodeStructureDb,
|
|
86
|
+
custom_tools: list[BaseTool] | None = None,
|
|
87
|
+
):
|
|
88
|
+
self.llm = llm
|
|
89
|
+
self.code_structure_db = code_structure_db
|
|
90
|
+
self.step_name = "Consistency Collection Execute Step"
|
|
91
|
+
self.custom_tools = custom_tools if custom_tools is not None else []
|
|
92
|
+
|
|
93
|
+
def _execute_directly(self, state: ConsistencyCollectionWorkflowState):
|
|
94
|
+
plan_actions = state["plan_actions"]
|
|
95
|
+
prompt = CustomPromptTemplate(
|
|
96
|
+
template=CONSISTENCY_EVAL_EXECUTION_SYSTEM_PROMPT,
|
|
97
|
+
tools=self.custom_tools,
|
|
98
|
+
plan_actions=plan_actions,
|
|
99
|
+
input_variables=[
|
|
100
|
+
"tools", "tool_names", "agent_scratchpad",
|
|
101
|
+
"intermediate_steps", "plan_actions",
|
|
102
|
+
],
|
|
103
|
+
)
|
|
104
|
+
output_parser = CustomOutputParser()
|
|
105
|
+
agent = create_react_agent(
|
|
106
|
+
llm=self.llm,
|
|
107
|
+
tools=self.custom_tools,
|
|
108
|
+
prompt=prompt,
|
|
109
|
+
output_parser=output_parser,
|
|
110
|
+
stop_sequence=["\nObservation:"],
|
|
111
|
+
)
|
|
112
|
+
callback_handler = OpenAICallbackHandler()
|
|
113
|
+
agent_executor = AgentExecutor(
|
|
114
|
+
agent=agent,
|
|
115
|
+
tools=self.custom_tools,
|
|
116
|
+
max_iterations=30,
|
|
117
|
+
)
|
|
118
|
+
response = agent_executor.invoke(
|
|
119
|
+
input={
|
|
120
|
+
"plan_actions": plan_actions,
|
|
121
|
+
"input": "Now, let's begin."
|
|
122
|
+
},
|
|
123
|
+
config={
|
|
124
|
+
"callbacks": [callback_handler],
|
|
125
|
+
"recursion_limit": 20,
|
|
126
|
+
},
|
|
127
|
+
)
|
|
128
|
+
if "output" in response:
|
|
129
|
+
output = response["output"]
|
|
130
|
+
self._print_step(state, step_output=f"**Execute Output:** \n{output}")
|
|
131
|
+
if "**Final Answer**" in output:
|
|
132
|
+
final_answer = output.split("**Final Answer:**")[-1].strip().strip(":")
|
|
133
|
+
step_output = final_answer
|
|
134
|
+
elif "Final Answer" in output:
|
|
135
|
+
final_answer = output.split("Final Answer")[-1].strip().strip(":")
|
|
136
|
+
step_output = final_answer
|
|
137
|
+
else:
|
|
138
|
+
step_output = output
|
|
139
|
+
self._print_step(state, step_output=step_output)
|
|
140
|
+
state["step_output"] = step_output
|
|
141
|
+
else:
|
|
142
|
+
logger.error("No output found in the response.")
|
|
143
|
+
self._print_step(
|
|
144
|
+
state,
|
|
145
|
+
step_output="Error: No output found in the response.",
|
|
146
|
+
)
|
|
147
|
+
state["step_output"] = "Error: No output found in the response."
|
|
148
|
+
|
|
149
|
+
token_usage = vars(callback_handler)
|
|
150
|
+
token_usage = {**DEFAULT_TOKEN_USAGE, **token_usage}
|
|
151
|
+
|
|
152
|
+
return state, token_usage
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
from langchain_openai.chat_models.base import BaseChatOpenAI
|
|
4
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
5
|
+
from bioguider.agents.agent_utils import ObservationResult
|
|
6
|
+
from bioguider.agents.common_agent_2step import CommonAgentTwoSteps
|
|
7
|
+
from bioguider.agents.consistency_collection_task_utils import ConsistencyCollectionWorkflowState
|
|
8
|
+
from bioguider.agents.peo_common_step import PEOCommonStep
|
|
9
|
+
|
|
10
|
+
CONSISTENCY_EVAL_OBSERVE_SYSTEM_PROMPT = """You are an expert developer specializing in the biomedical domain.
|
|
11
|
+
|
|
12
|
+
### **Goal**
|
|
13
|
+
Your task is to collect the function, class, and method definitions and docstrings for a given user guide/API documentation.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
### **Intermediate Steps**
|
|
18
|
+
Here are the results from previous steps:
|
|
19
|
+
{intermediate_steps}
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### **Instructions**
|
|
24
|
+
1. Your goal is if you have enough information to evaluate the consistency of the user guide/API documentation.
|
|
25
|
+
2. Carefully review the **Goal**, **User Guide/API Documentation**, and **Intermediate Output**.
|
|
26
|
+
3. If you believe you have enough information to evaluate the consistency of the user guide/API documentation:
|
|
27
|
+
|
|
28
|
+
* Proceed with the following format:
|
|
29
|
+
|
|
30
|
+
* Provide your reasoning under **Analysis**
|
|
31
|
+
* Then give your final answer under **FinalAnswer**
|
|
32
|
+
* **FinalAnswer** format must exactly match this format:
|
|
33
|
+
**FinalAnswer**: {{"final_answer": "yes" or "no"}}
|
|
34
|
+
* Your answer **must exactly match the follwing format** (note: no JSON code block, no additional comments), **do not** make up anything:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
**Analysis**: your analysis here
|
|
38
|
+
**FinalAnswer**: {{"final_answer": "yes" or "no"}}
|
|
39
|
+
```
|
|
40
|
+
4. If you believe you do not have enough information to evaluate the consistency of the user guide/API documentation:
|
|
41
|
+
|
|
42
|
+
* Provide your reasoning under **Thoughts**:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
**Thoughts**: your thoughts here
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Be precise and support your reasoning with evidence from the input.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### **Notes**
|
|
53
|
+
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**.
|
|
54
|
+
If you find the current information insufficient, share your reasoning or thoughts instead—we’ll continue with the next round accordingly.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### **Input User Guide/API Documentation**
|
|
59
|
+
{user_guide_api_documentation}
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ConsistencyCollectionObserveStep(PEOCommonStep):
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
llm: BaseChatOpenAI,
|
|
70
|
+
):
|
|
71
|
+
super().__init__(llm=llm)
|
|
72
|
+
self.step_name = "Consistency Collection Observe Step"
|
|
73
|
+
|
|
74
|
+
def _build_prompt(self, state):
|
|
75
|
+
user_guide_api_documentation = state["user_guide_api_documentation"]
|
|
76
|
+
intermediate_steps = self._build_intermediate_steps(state)
|
|
77
|
+
prompt = ChatPromptTemplate.from_template(CONSISTENCY_EVAL_OBSERVE_SYSTEM_PROMPT)
|
|
78
|
+
return prompt.format(
|
|
79
|
+
user_guide_api_documentation=user_guide_api_documentation,
|
|
80
|
+
intermediate_steps=intermediate_steps,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def _collect_final_answer(self, state: ConsistencyCollectionWorkflowState):
|
|
84
|
+
if not ("final_answer" in state and state["final_answer"] is not None and
|
|
85
|
+
state["final_answer"].strip().lower() == "yes"):
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
final_result = ""
|
|
89
|
+
if "intermediate_steps" in state and state["intermediate_steps"] is not None:
|
|
90
|
+
for i in range(len(state["intermediate_steps"])):
|
|
91
|
+
final_result += state["intermediate_steps"][i]
|
|
92
|
+
final_result += "\n\n"
|
|
93
|
+
if "step_output" in state and state["step_output"] is not None:
|
|
94
|
+
final_result += state["step_output"]
|
|
95
|
+
final_result += "\n\n"
|
|
96
|
+
|
|
97
|
+
return final_result
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _execute_directly(self, state: ConsistencyCollectionWorkflowState):
|
|
101
|
+
system_prompt = self._build_prompt(state)
|
|
102
|
+
agent = CommonAgentTwoSteps(llm=self.llm)
|
|
103
|
+
res, _, token_usage, reasoning_process = agent.go(
|
|
104
|
+
system_prompt=system_prompt,
|
|
105
|
+
instruction_prompt="Now, let's begin the consistency collection observe step.",
|
|
106
|
+
schema=ObservationResult,
|
|
107
|
+
)
|
|
108
|
+
state["final_answer"] = res.FinalAnswer
|
|
109
|
+
analysis = res.Analysis
|
|
110
|
+
thoughts = res.Thoughts
|
|
111
|
+
state["step_analysis"] = analysis
|
|
112
|
+
state["step_thoughts"] = thoughts
|
|
113
|
+
state["step_count"] += 1
|
|
114
|
+
state["final_assembly_result"] = self._collect_final_answer(state)
|
|
115
|
+
self._print_step(
|
|
116
|
+
state,
|
|
117
|
+
step_output=f"**Observation Reasoning Process {state['step_count']}**\n{reasoning_process}"
|
|
118
|
+
)
|
|
119
|
+
self._print_step(
|
|
120
|
+
state,
|
|
121
|
+
step_output=f"Final Answer: {res.FinalAnswer if res.FinalAnswer else None}\nAnalysis: {analysis}\nThoughts: {thoughts}",
|
|
122
|
+
)
|
|
123
|
+
if state["final_assembly_result"] is not None:
|
|
124
|
+
self._print_step(
|
|
125
|
+
state,
|
|
126
|
+
step_output=f"Final Assembly Result: {state['final_assembly_result']}",
|
|
127
|
+
)
|
|
128
|
+
return state, token_usage
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from langchain_openai.chat_models.base import BaseChatOpenAI
|
|
2
|
+
|
|
3
|
+
from langchain.tools import BaseTool
|
|
4
|
+
from langchain_core.prompts import ChatPromptTemplate, StringPromptTemplate
|
|
5
|
+
from bioguider.agents.agent_utils import (
|
|
6
|
+
convert_plan_to_string,
|
|
7
|
+
get_tool_names_and_descriptions,
|
|
8
|
+
PlanAgentResultJsonSchema,
|
|
9
|
+
PlanAgentResult,
|
|
10
|
+
)
|
|
11
|
+
from bioguider.agents.common_agent_2step import CommonAgentTwoChainSteps, CommonAgentTwoSteps
|
|
12
|
+
from bioguider.agents.consistency_collection_task_utils import ConsistencyCollectionWorkflowState
|
|
13
|
+
from bioguider.agents.peo_common_step import PEOCommonStep
|
|
14
|
+
|
|
15
|
+
CONSISTANCE_EVAL_PLAN_SYSTEM_PROMPT = ChatPromptTemplate.from_template("""### **Goal**
|
|
16
|
+
You are an expert developer specializing in the biomedical domain.
|
|
17
|
+
Your task is to collect the function, class, and method definitions and docstrings for a given user guide/API documentation.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
### **Function Tools**
|
|
22
|
+
You have access to the following function tools:
|
|
23
|
+
{tools}
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
### **Intermediate Steps**
|
|
28
|
+
Here are the results from previous steps:
|
|
29
|
+
{intermediate_steps}
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### **Intermediate Thoughts**
|
|
34
|
+
- **Analysis**: {intermediate_analysis}
|
|
35
|
+
- **Thoughts**: {intermediate_thoughts}
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
### **Instructions**
|
|
40
|
+
1. We will iterate through multiple **Plan -> Execution -> Observation** loops as needed.
|
|
41
|
+
- All variables and tool outputs are **persisted across rounds**, so you can build on prior results.
|
|
42
|
+
- Develop your plan **incrementally**, and reflect on intermediate observations before proceeding.
|
|
43
|
+
- Limit each step to **one or two actions** — avoid trying to complete everything in a single step.
|
|
44
|
+
|
|
45
|
+
2. Your task is to evaluate the consistency of the user guide/API documentation.
|
|
46
|
+
|
|
47
|
+
3. You may use
|
|
48
|
+
- the `retrieve_function_definition_and_docstring` tool to get the function definition and docstring or,
|
|
49
|
+
- the `retrieve_class_definition_and_docstring` to get the class definition and docstring or,
|
|
50
|
+
- the `retrieve_class_and_method_definition_and_docstring` to get the class and method definition and docstring.
|
|
51
|
+
|
|
52
|
+
4. Your plan can only use the above tools, **do not** make up any tools not in the above tools list.
|
|
53
|
+
|
|
54
|
+
5. If no function, class, or method is found in the given user guide/API documentation, you should return "N/A" as an empty plan.
|
|
55
|
+
Our tools can only retrieve the **function**, **class**, **method** definition and docstring, **do not** make up any function, class, or method name.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### **Input User Guide/API Documentation**
|
|
59
|
+
{user_guide_api_documentation}
|
|
60
|
+
|
|
61
|
+
### **Output Format**
|
|
62
|
+
Your plan **must exactly match** a sequence of steps in the following format, **do not** make up anything:
|
|
63
|
+
|
|
64
|
+
Step: <tool name> # Tool name **must be one** of {tool_names}
|
|
65
|
+
Step Input: <function/class/method name>
|
|
66
|
+
Step Input: <file path, if not sure, just put "N/A">
|
|
67
|
+
|
|
68
|
+
Step: <tool name> # Tool name **must be one** of {tool_names}
|
|
69
|
+
Step Input: <function/class/method name>
|
|
70
|
+
Step Input: <file path, if not sure, just put "N/A">
|
|
71
|
+
...
|
|
72
|
+
|
|
73
|
+
...
|
|
74
|
+
""")
|
|
75
|
+
|
|
76
|
+
class ConsistencyCollectionPlanStep(PEOCommonStep):
|
|
77
|
+
"""
|
|
78
|
+
ConsistencyCollectionPlanStep is a step in the consistency collection plan process.
|
|
79
|
+
It is responsible for initializing the tools and compiling the step.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(
|
|
83
|
+
self,
|
|
84
|
+
llm: BaseChatOpenAI,
|
|
85
|
+
custom_tools: list[BaseTool] | None = None,
|
|
86
|
+
):
|
|
87
|
+
super().__init__(llm)
|
|
88
|
+
self.step_name = "Consistency Collection Plan Step"
|
|
89
|
+
self.custom_tools = custom_tools if custom_tools is not None else []
|
|
90
|
+
|
|
91
|
+
def _prepare_system_prompt(self, state: ConsistencyCollectionWorkflowState) -> str:
|
|
92
|
+
user_guide_api_documentation = state["user_guide_api_documentation"]
|
|
93
|
+
intermediate_steps = self._build_intermediate_steps(state)
|
|
94
|
+
step_analysis, step_thoughts = self._build_intermediate_analysis_and_thoughts(state)
|
|
95
|
+
tool_names, tools_desc = get_tool_names_and_descriptions(self.custom_tools)
|
|
96
|
+
system_prompt = CONSISTANCE_EVAL_PLAN_SYSTEM_PROMPT.format(
|
|
97
|
+
tools=tools_desc,
|
|
98
|
+
intermediate_steps=intermediate_steps,
|
|
99
|
+
intermediate_analysis=step_analysis,
|
|
100
|
+
intermediate_thoughts=step_thoughts,
|
|
101
|
+
tool_names=tool_names,
|
|
102
|
+
user_guide_api_documentation=user_guide_api_documentation,
|
|
103
|
+
)
|
|
104
|
+
self._print_step(
|
|
105
|
+
state,
|
|
106
|
+
step_output="**Intermediate Step Output**\n" + intermediate_steps
|
|
107
|
+
)
|
|
108
|
+
self._print_step(
|
|
109
|
+
state,
|
|
110
|
+
step_output="**Intermediate Step Analysis**\n{step_analysis}\n**Intermediate Step Thoughts**\n{step_thoughts}",
|
|
111
|
+
)
|
|
112
|
+
return system_prompt
|
|
113
|
+
|
|
114
|
+
def _execute_directly(self, state: ConsistencyCollectionWorkflowState):
|
|
115
|
+
system_prompt = self._prepare_system_prompt(state)
|
|
116
|
+
agent = CommonAgentTwoSteps(llm=self.llm)
|
|
117
|
+
res, _, token_usage, reasoning_process = agent.go(
|
|
118
|
+
system_prompt=system_prompt,
|
|
119
|
+
instruction_prompt="Now, let's begin the consistency collection plan step.",
|
|
120
|
+
schema=PlanAgentResultJsonSchema,
|
|
121
|
+
)
|
|
122
|
+
PEOCommonStep._reset_step_state(state)
|
|
123
|
+
res = PlanAgentResult(**res)
|
|
124
|
+
self._print_step(state, step_output=f"**Reasoning Process**\n{reasoning_process}")
|
|
125
|
+
self._print_step(state, step_output=f"**Plan**\n{str(res.actions)}")
|
|
126
|
+
state["plan_actions"] = convert_plan_to_string(res)
|
|
127
|
+
|
|
128
|
+
return state, token_usage
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Callable, Optional, TypedDict
|
|
3
|
+
from langchain.prompts import ChatPromptTemplate
|
|
4
|
+
from langchain_openai.chat_models.base import BaseChatOpenAI
|
|
5
|
+
from langchain_core.messages import AIMessage
|
|
6
|
+
from langchain.tools import StructuredTool
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
import logging
|
|
9
|
+
from langgraph.graph import StateGraph, START, END
|
|
10
|
+
|
|
11
|
+
from bioguider.agents.agent_task import AgentTask
|
|
12
|
+
from bioguider.agents.agent_tools import agent_tool
|
|
13
|
+
from bioguider.agents.agent_utils import read_file, summarize_file
|
|
14
|
+
from bioguider.agents.peo_common_step import PEOWorkflowState
|
|
15
|
+
from bioguider.agents.common_agent import CommonAgent
|
|
16
|
+
from bioguider.agents.common_agent_2step import CommonAgentTwoSteps
|
|
17
|
+
from bioguider.agents.consistency_collection_task_utils import (
|
|
18
|
+
ConsistencyCollectionWorkflowState,
|
|
19
|
+
retrieve_function_definition_and_docstring_tool,
|
|
20
|
+
retrieve_class_definition_and_docstring_tool,
|
|
21
|
+
retrieve_class_and_method_definition_and_docstring_tool,
|
|
22
|
+
retrieve_method_definition_and_docstring_tool,
|
|
23
|
+
)
|
|
24
|
+
from bioguider.agents.consistency_collection_plan_step import ConsistencyCollectionPlanStep
|
|
25
|
+
from bioguider.agents.consistency_collection_observe_step import ConsistencyCollectionObserveStep
|
|
26
|
+
from bioguider.agents.consistency_collection_execute_step import ConsistencyCollectionExecuteStep
|
|
27
|
+
from bioguider.database.code_structure_db import CodeStructureDb
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
class ConsistencyCollectionTask(AgentTask):
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
llm: BaseChatOpenAI,
|
|
35
|
+
code_structure_db: CodeStructureDb,
|
|
36
|
+
step_callback: Callable | None = None,
|
|
37
|
+
):
|
|
38
|
+
super().__init__(llm=llm, step_callback=step_callback)
|
|
39
|
+
self.llm = llm
|
|
40
|
+
self.code_structure_db = code_structure_db
|
|
41
|
+
|
|
42
|
+
func_tool = retrieve_function_definition_and_docstring_tool(llm=llm, code_structure_db=code_structure_db)
|
|
43
|
+
class_tool = retrieve_class_definition_and_docstring_tool(llm=llm, code_structure_db=code_structure_db)
|
|
44
|
+
class_and_method_tool = retrieve_class_and_method_definition_and_docstring_tool(llm=llm, code_structure_db=code_structure_db)
|
|
45
|
+
method_tool = retrieve_method_definition_and_docstring_tool(llm=llm, code_structure_db=code_structure_db)
|
|
46
|
+
self.tools = [func_tool, class_tool, class_and_method_tool, method_tool]
|
|
47
|
+
self.custom_tools = [
|
|
48
|
+
StructuredTool.from_function(
|
|
49
|
+
func_tool.run,
|
|
50
|
+
description=func_tool.__class__.__doc__,
|
|
51
|
+
name=func_tool.__class__.__name__,
|
|
52
|
+
),
|
|
53
|
+
StructuredTool.from_function(
|
|
54
|
+
class_tool.run,
|
|
55
|
+
description=class_tool.__class__.__doc__,
|
|
56
|
+
name=class_tool.__class__.__name__,
|
|
57
|
+
),
|
|
58
|
+
StructuredTool.from_function(
|
|
59
|
+
class_and_method_tool.run,
|
|
60
|
+
description=class_and_method_tool.__class__.__doc__,
|
|
61
|
+
name=class_and_method_tool.__class__.__name__,
|
|
62
|
+
),
|
|
63
|
+
StructuredTool.from_function(
|
|
64
|
+
method_tool.run,
|
|
65
|
+
description=method_tool.__class__.__doc__,
|
|
66
|
+
name=method_tool.__class__.__name__,
|
|
67
|
+
),
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
self.steps = [
|
|
71
|
+
ConsistencyCollectionPlanStep(llm=llm, custom_tools=self.custom_tools),
|
|
72
|
+
ConsistencyCollectionExecuteStep(llm=llm, code_structure_db=code_structure_db, custom_tools=self.custom_tools),
|
|
73
|
+
ConsistencyCollectionObserveStep(llm=llm)
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
def _compile(self, repo_path: str, gitignore_path: str):
|
|
77
|
+
def check_observe_step(state: ConsistencyCollectionWorkflowState):
|
|
78
|
+
if "final_answer" in state and state["final_answer"] is not None:
|
|
79
|
+
return END
|
|
80
|
+
return "plan_step"
|
|
81
|
+
def check_plan_step(state: ConsistencyCollectionWorkflowState):
|
|
82
|
+
if "plan_actions" in state and state["plan_actions"] is not None and len(state["plan_actions"]) > 0:
|
|
83
|
+
return "execute_step"
|
|
84
|
+
return END
|
|
85
|
+
|
|
86
|
+
graph = StateGraph(ConsistencyCollectionWorkflowState)
|
|
87
|
+
graph.add_node("plan_step", self.steps[0].execute)
|
|
88
|
+
graph.add_node("execute_step", self.steps[1].execute)
|
|
89
|
+
graph.add_node("observe_step", self.steps[2].execute)
|
|
90
|
+
graph.add_edge(START, "plan_step")
|
|
91
|
+
graph.add_conditional_edges("plan_step", check_plan_step, {"execute_step", END})
|
|
92
|
+
graph.add_edge("execute_step", "observe_step")
|
|
93
|
+
graph.add_conditional_edges("observe_step", check_observe_step, {"plan_step", END})
|
|
94
|
+
|
|
95
|
+
self.graph = graph.compile()
|
|
96
|
+
|
|
97
|
+
def collect(self, user_guide_api_documentation: str) -> tuple[bool, str | None]:
|
|
98
|
+
s = self._go_graph({
|
|
99
|
+
"user_guide_api_documentation": user_guide_api_documentation,
|
|
100
|
+
"step_count": 0,
|
|
101
|
+
})
|
|
102
|
+
# analyze the final assembly result
|
|
103
|
+
if "final_assembly_result" in s and s["final_assembly_result"] is not None:
|
|
104
|
+
self._print_step(step_name="Final Assembly Result")
|
|
105
|
+
self._print_step(step_output=s["final_assembly_result"])
|
|
106
|
+
return True, s["final_assembly_result"]
|
|
107
|
+
else:
|
|
108
|
+
return False, s["thoughts"] if "thoughts" in s else None
|
|
109
|
+
|