pdd-cli 0.0.90__py3-none-any.whl → 0.0.121__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.
- pdd/__init__.py +38 -6
- pdd/agentic_bug.py +323 -0
- pdd/agentic_bug_orchestrator.py +506 -0
- pdd/agentic_change.py +231 -0
- pdd/agentic_change_orchestrator.py +537 -0
- pdd/agentic_common.py +533 -770
- pdd/agentic_crash.py +2 -1
- pdd/agentic_e2e_fix.py +319 -0
- pdd/agentic_e2e_fix_orchestrator.py +582 -0
- pdd/agentic_fix.py +118 -3
- pdd/agentic_update.py +27 -9
- pdd/agentic_verify.py +3 -2
- pdd/architecture_sync.py +565 -0
- pdd/auth_service.py +210 -0
- pdd/auto_deps_main.py +63 -53
- pdd/auto_include.py +236 -3
- pdd/auto_update.py +125 -47
- pdd/bug_main.py +195 -23
- pdd/cmd_test_main.py +345 -197
- pdd/code_generator.py +4 -2
- pdd/code_generator_main.py +118 -32
- pdd/commands/__init__.py +6 -0
- pdd/commands/analysis.py +113 -48
- pdd/commands/auth.py +309 -0
- pdd/commands/connect.py +358 -0
- pdd/commands/fix.py +155 -114
- pdd/commands/generate.py +5 -0
- pdd/commands/maintenance.py +3 -2
- pdd/commands/misc.py +8 -0
- pdd/commands/modify.py +225 -163
- pdd/commands/sessions.py +284 -0
- pdd/commands/utility.py +12 -7
- pdd/construct_paths.py +334 -32
- pdd/context_generator_main.py +167 -170
- pdd/continue_generation.py +6 -3
- pdd/core/__init__.py +33 -0
- pdd/core/cli.py +44 -7
- pdd/core/cloud.py +237 -0
- pdd/core/dump.py +68 -20
- pdd/core/errors.py +4 -0
- pdd/core/remote_session.py +61 -0
- pdd/crash_main.py +219 -23
- pdd/data/llm_model.csv +4 -4
- pdd/docs/prompting_guide.md +864 -0
- pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
- pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
- pdd/fix_code_loop.py +208 -34
- pdd/fix_code_module_errors.py +6 -2
- pdd/fix_error_loop.py +291 -38
- pdd/fix_main.py +208 -6
- pdd/fix_verification_errors_loop.py +235 -26
- pdd/fix_verification_main.py +269 -83
- pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
- pdd/frontend/dist/assets/index-CUWd8al1.js +450 -0
- pdd/frontend/dist/index.html +376 -0
- pdd/frontend/dist/logo.svg +33 -0
- pdd/generate_output_paths.py +46 -5
- pdd/generate_test.py +212 -151
- pdd/get_comment.py +19 -44
- pdd/get_extension.py +8 -9
- pdd/get_jwt_token.py +309 -20
- pdd/get_language.py +8 -7
- pdd/get_run_command.py +7 -5
- pdd/insert_includes.py +2 -1
- pdd/llm_invoke.py +531 -97
- pdd/load_prompt_template.py +15 -34
- pdd/operation_log.py +342 -0
- pdd/path_resolution.py +140 -0
- pdd/postprocess.py +122 -97
- pdd/preprocess.py +68 -12
- pdd/preprocess_main.py +33 -1
- pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
- pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
- pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
- pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
- pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
- pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
- pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
- pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
- pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
- pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
- pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
- pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
- pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +140 -0
- pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
- pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
- pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
- pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
- pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
- pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
- pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
- pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
- pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
- pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
- pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
- pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
- pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
- pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
- pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
- pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
- pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
- pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
- pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
- pdd/prompts/agentic_update_LLM.prompt +192 -338
- pdd/prompts/auto_include_LLM.prompt +22 -0
- pdd/prompts/change_LLM.prompt +3093 -1
- pdd/prompts/detect_change_LLM.prompt +571 -14
- pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
- pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
- pdd/prompts/generate_test_LLM.prompt +19 -1
- pdd/prompts/generate_test_from_example_LLM.prompt +366 -0
- pdd/prompts/insert_includes_LLM.prompt +262 -252
- pdd/prompts/prompt_code_diff_LLM.prompt +123 -0
- pdd/prompts/prompt_diff_LLM.prompt +82 -0
- pdd/remote_session.py +876 -0
- pdd/server/__init__.py +52 -0
- pdd/server/app.py +335 -0
- pdd/server/click_executor.py +587 -0
- pdd/server/executor.py +338 -0
- pdd/server/jobs.py +661 -0
- pdd/server/models.py +241 -0
- pdd/server/routes/__init__.py +31 -0
- pdd/server/routes/architecture.py +451 -0
- pdd/server/routes/auth.py +364 -0
- pdd/server/routes/commands.py +929 -0
- pdd/server/routes/config.py +42 -0
- pdd/server/routes/files.py +603 -0
- pdd/server/routes/prompts.py +1347 -0
- pdd/server/routes/websocket.py +473 -0
- pdd/server/security.py +243 -0
- pdd/server/terminal_spawner.py +217 -0
- pdd/server/token_counter.py +222 -0
- pdd/summarize_directory.py +236 -237
- pdd/sync_animation.py +8 -4
- pdd/sync_determine_operation.py +329 -47
- pdd/sync_main.py +272 -28
- pdd/sync_orchestration.py +289 -211
- pdd/sync_order.py +304 -0
- pdd/template_expander.py +161 -0
- pdd/templates/architecture/architecture_json.prompt +41 -46
- pdd/trace.py +1 -1
- pdd/track_cost.py +0 -13
- pdd/unfinished_prompt.py +2 -1
- pdd/update_main.py +68 -26
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/METADATA +15 -10
- pdd_cli-0.0.121.dist-info/RECORD +229 -0
- pdd_cli-0.0.90.dist-info/RECORD +0 -153
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/WHEEL +0 -0
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/entry_points.txt +0 -0
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/licenses/LICENSE +0 -0
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/top_level.txt +0 -0
pdd/prompts/change_LLM.prompt
CHANGED
|
@@ -15,7 +15,3099 @@
|
|
|
15
15
|
</inputs_outputs_definitions>
|
|
16
16
|
|
|
17
17
|
<change_prompt_examples>
|
|
18
|
-
|
|
18
|
+
<examples>
|
|
19
|
+
<example>
|
|
20
|
+
<example_number>5</example_number>
|
|
21
|
+
<example_input_prompt>```
|
|
22
|
+
% You are an expert Python Software Engineer. Your goal is to write a Python function, "split", that will split a prompt into a sub_prompt and modified_prompt with no loss of functionality. All output to the console will be pretty printed using the Python Rich library.
|
|
23
|
+
|
|
24
|
+
% Here are the inputs and outputs of the function:
|
|
25
|
+
Inputs:
|
|
26
|
+
- 'input_prompt': A string containing the prompt that will be split into a sub_prompt and modified_prompt.
|
|
27
|
+
- 'input_code': A string containing the code that was generated from the input_prompt.
|
|
28
|
+
- 'example_code': A string containing the code example of how the code generated from the sub_prompt would be used by the code generated from the modified_prompt.
|
|
29
|
+
- 'strength': A float value representing the strength parameter for the LLM model, used to influence the model's behavior.
|
|
30
|
+
- 'temperature': A float value representing the temperature parameter for the LLM model, used to control the randomness of the model's output.
|
|
31
|
+
Outputs:
|
|
32
|
+
- 'sub_prompt': A string containing the sub_prompt that was split from the input_prompt.
|
|
33
|
+
- 'modified_prompt': A string containing the modified prompt from input_prompt split from the sub_prompt.
|
|
34
|
+
- 'total_cost': A float value representing the total cost of running the function.
|
|
35
|
+
|
|
36
|
+
% Here is an example how to preprocess the prompt from a file: ```
|
|
37
|
+
from preprocess import preprocess
|
|
38
|
+
from rich.console import Console
|
|
39
|
+
console = Console()
|
|
40
|
+
prompt = """
|
|
41
|
+
<prompt>
|
|
42
|
+
Copyright 2025 Prompt Driven, Inc.
|
|
43
|
+
|
|
44
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
45
|
+
|
|
46
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
47
|
+
|
|
48
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
49
|
+
Hello World
|
|
50
|
+
|
|
51
|
+
<pdd>This is a comment</pdd>
|
|
52
|
+
``` <file_to_include.txt>```
|
|
53
|
+
</prompt>
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
recursive = False
|
|
57
|
+
double_curly_brackets = True
|
|
58
|
+
|
|
59
|
+
processed = preprocess(prompt, recursive, double_curly_brackets)
|
|
60
|
+
console.print("[bold white]Processed Prompt:[/bold white]")
|
|
61
|
+
console.print(processed)
|
|
62
|
+
|
|
63
|
+
# load prompts/change_LLM.prompt
|
|
64
|
+
with open('prompts/xml/change_LLM.prompt', 'r') as file:
|
|
65
|
+
change_LLM_prompt = file.read()
|
|
66
|
+
|
|
67
|
+
# call preprocess on change_LLM_prompt
|
|
68
|
+
processed = preprocess(change_LLM_prompt, recursive, False)
|
|
69
|
+
console.print("[bold white]Processed change_LLM Prompt:[/bold white]")
|
|
70
|
+
console.print(processed)
|
|
71
|
+
|
|
72
|
+
# write the processed prompt to a file
|
|
73
|
+
with open('prompts/xml/change_LLM_processed.prompt', 'w') as file:
|
|
74
|
+
file.write(processed)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
% Example usage of the Langchain LCEL program: ```
|
|
78
|
+
import os
|
|
79
|
+
from langchain_core.prompts import PromptTemplate
|
|
80
|
+
from langchain_community.cache import SQLiteCache
|
|
81
|
+
from langchain.globals import set_llm_cache
|
|
82
|
+
from langchain_core.output_parsers import JsonOutputParser
|
|
83
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
84
|
+
from langchain.output_parsers import RetryOutputParser
|
|
85
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
86
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
87
|
+
from langchain_core.runnables import RunnablePassthrough, ConfigurableField
|
|
88
|
+
|
|
89
|
+
from langchain_fireworks import Fireworks
|
|
90
|
+
from langchain_anthropic import ChatAnthropic
|
|
91
|
+
from langchain_openai import ChatOpenAI
|
|
92
|
+
from langchain_openai import OpenAI
|
|
93
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
94
|
+
from langchain_groq import ChatGroq
|
|
95
|
+
from langchain_together import Together
|
|
96
|
+
|
|
97
|
+
# Define a base output parser (e.g., PydanticOutputParser)
|
|
98
|
+
from pydantic import BaseModel, Field
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Setup cache to save money and increase speeds
|
|
102
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# Create the LCEL template. Make note of the variable {{topic}} which will be filled in later.
|
|
106
|
+
prompt_template = PromptTemplate.from_template("Tell me a joke about {{topic}}")
|
|
107
|
+
|
|
108
|
+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
|
109
|
+
# Combine with a model and parser to output a string
|
|
110
|
+
chain = prompt_template |llm| StrOutputParser()
|
|
111
|
+
|
|
112
|
+
# Run the template. Notice that the input is a dictionary with a single key "topic" which feeds it into the above prompt template. This is needed because the prompt template has a variable {{topic}} which needs to be filled in when invoked.
|
|
113
|
+
result = chain.invoke({{"topic": "cats"}})
|
|
114
|
+
print(result)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# Define your desired data structure.
|
|
118
|
+
class Joke(BaseModel):
|
|
119
|
+
setup: str = Field(description="question to set up a joke")
|
|
120
|
+
punchline: str = Field(description="answer to resolve the joke")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
llm = ChatGoogleGenerativeAI(model="gemini-pro", temperature=0)
|
|
124
|
+
# Set up a parser
|
|
125
|
+
parser = JsonOutputParser(pydantic_object=Joke)
|
|
126
|
+
|
|
127
|
+
# Create a prompt template
|
|
128
|
+
prompt = PromptTemplate(
|
|
129
|
+
template="Answer the user query.\n{{format_instructions}}\n{{query}}\n",
|
|
130
|
+
input_variables=["query"],
|
|
131
|
+
partial_variables={{"format_instructions": parser.get_format_instructions()}},
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Chain the components
|
|
135
|
+
chain = prompt | llm | parser
|
|
136
|
+
|
|
137
|
+
# Invoke the chain with a query
|
|
138
|
+
result = chain.invoke({{"query": "Tell me a joke."}})
|
|
139
|
+
print(result)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# Get DEEKSEEK_API_KEY environmental variable
|
|
143
|
+
deepseek_api_key = os.getenv('DEEKSEEK_API_KEY')
|
|
144
|
+
|
|
145
|
+
# Ensure the API key is retrieved successfully
|
|
146
|
+
if deepseek_api_key is None:
|
|
147
|
+
raise ValueError("DEEKSEEK_API_KEY environment variable is not set")
|
|
148
|
+
|
|
149
|
+
llm = ChatOpenAI(
|
|
150
|
+
model='deepseek-chat',
|
|
151
|
+
openai_api_key=deepseek_api_key,
|
|
152
|
+
openai_api_base='https://api.deepseek.com',
|
|
153
|
+
temperature=0
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Chain the components
|
|
157
|
+
chain = prompt | llm | parser
|
|
158
|
+
|
|
159
|
+
# Invoke the chain with a query
|
|
160
|
+
result = chain.invoke({{"query": "Write joke about the sky"}})
|
|
161
|
+
print("deepseek",result)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
llm = Fireworks(
|
|
165
|
+
model="accounts/fireworks/models/mixtral-8x7b-instruct",
|
|
166
|
+
temperature=0)
|
|
167
|
+
# Chain the components
|
|
168
|
+
chain = prompt | llm | parser
|
|
169
|
+
|
|
170
|
+
# Invoke the chain with a query
|
|
171
|
+
result = chain.invoke({{"query": "Tell me a joke about the president"}})
|
|
172
|
+
print("fireworks",result)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
prompt = ChatPromptTemplate.from_template(
|
|
179
|
+
"Tell me a short joke about {{topic}}"
|
|
180
|
+
)
|
|
181
|
+
chat_openai = ChatOpenAI(model="gpt-3.5-turbo")
|
|
182
|
+
openai = OpenAI(model="gpt-3.5-turbo-instruct")
|
|
183
|
+
anthropic = ChatAnthropic(model="claude-2")
|
|
184
|
+
model = (
|
|
185
|
+
chat_openai
|
|
186
|
+
.with_fallbacks([anthropic])
|
|
187
|
+
.configurable_alternatives(
|
|
188
|
+
ConfigurableField(id="model"),
|
|
189
|
+
default_key="chat_openai",
|
|
190
|
+
openai=openai,
|
|
191
|
+
anthropic=anthropic,
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
chain = (
|
|
196
|
+
{{"topic": RunnablePassthrough()}}
|
|
197
|
+
| prompt
|
|
198
|
+
| model
|
|
199
|
+
| StrOutputParser()
|
|
200
|
+
)
|
|
201
|
+
result = chain.invoke({{"topic": "Tell me a joke about the president"}})
|
|
202
|
+
print("config alt:",result)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
llm = ChatGroq(temperature=0, model_name="mixtral-8x7b-32768")
|
|
207
|
+
system = "You are a helpful assistant."
|
|
208
|
+
human = "{{text}}"
|
|
209
|
+
prompt = ChatPromptTemplate.from_messages([("system", system), ("human", human)])
|
|
210
|
+
|
|
211
|
+
chain = prompt | llm | StrOutputParser()
|
|
212
|
+
print(chain.invoke({{"text": "Explain the importance of low latency LLMs."}}))
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
llm = Together(
|
|
216
|
+
model="meta-llama/Llama-3-70b-chat-hf",
|
|
217
|
+
)
|
|
218
|
+
chain = prompt | llm | StrOutputParser()
|
|
219
|
+
print(chain.invoke({{"text": "Explain the importance of together.ai."}}))
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
% Example of selecting a Langchain LLM using llm_selector: ```
|
|
224
|
+
import os
|
|
225
|
+
from llm_selector import llm_selector
|
|
226
|
+
|
|
227
|
+
def main() -> None:
|
|
228
|
+
"""
|
|
229
|
+
Main function to demonstrate the usage of the llm_selector function.
|
|
230
|
+
Sets environment variables, defines parameters, and calls the function.
|
|
231
|
+
"""
|
|
232
|
+
# Set environment variables (for demonstration purposes)
|
|
233
|
+
os.environ['PDD_MODEL_DEFAULT'] = 'gpt-4o-mini' # Default model
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# Define desired strength and temperature
|
|
237
|
+
strength: float = .3 # Desired strength of the model (0.0 to 1.0)
|
|
238
|
+
temperature: float = 0 # Temperature for the model (0.0 to 1.0)
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
# Call the llm_selector function
|
|
242
|
+
llm, token_counter, input_cost, output_cost, model_name = llm_selector(strength, temperature)
|
|
243
|
+
|
|
244
|
+
# Output the selected model details
|
|
245
|
+
print(f"Selected LLM: {{model_name}}")
|
|
246
|
+
print(f"Input Cost: {{input_cost}}")
|
|
247
|
+
print(f"Output Cost: {{output_cost}}")
|
|
248
|
+
except Exception as e:
|
|
249
|
+
print(f"An error occurred: {{e}}")
|
|
250
|
+
|
|
251
|
+
if __name__ == "__main__":
|
|
252
|
+
main()
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
% Example of using tiktoken: ```
|
|
256
|
+
import tiktoken
|
|
257
|
+
encoding = tiktoken.get_encoding("cl100k_base") # or another encoding name
|
|
258
|
+
token_count = len(encoding.encode(preprocessed_prompt))
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
% Steps to be followed by the function:
|
|
262
|
+
1. Load the '$PDD_PATH/prompts/split_LLM.prompt' and '$PDD_PATH/prompts/extract_prompt_split_LLM.prompt' files.
|
|
263
|
+
2. Preprocess the split_LLM prompt using the preprocess function from the preprocess module.
|
|
264
|
+
2. Create a Langchain LCEL template from the processed split_LLM prompt to return a string output.
|
|
265
|
+
3. Use the llm_selector function for the LLM model.
|
|
266
|
+
4. Run the input through the model using Langchain LCEL:
|
|
267
|
+
- a. Pass the following string parameters to the prompt during invocation: 'input_prompt', 'input_code', 'example_code'.
|
|
268
|
+
- b. Calculate the input and output token count using tiktoken and pretty print the running message, including the token count and estimated cost. The cost from llm_selector is in dollars per million tokens.
|
|
269
|
+
5. Create a Langchain LCEL template from the extract_prompt_split_LLM prompt that outputs JSON:
|
|
270
|
+
- a. Pass the following string parameters to the prompt during invocation: 'llm_output' (this string is from Step 4).
|
|
271
|
+
- b. Calculate input and ouput token count using tiktoken and pretty print the running message with the token count and cost.
|
|
272
|
+
- c. get 'sub_prompt' and 'modified_prompt' from the JSON output.
|
|
273
|
+
6. Pretty print the extracted sub_prompt and modified_prompt using Rich Markdown function. Include token counts and costs.
|
|
274
|
+
7. Return the 'sub_prompt' and 'modified_prompt' strings and the total_cost.
|
|
275
|
+
|
|
276
|
+
% Ensure the function handles edge cases, such as missing inputs or model errors, and provide clear error messages.
|
|
277
|
+
```</example_input_prompt>
|
|
278
|
+
<example_input_code>```
|
|
279
|
+
#To implement the `split` function as described, we need to follow a structured approach using the Langchain library, the `llm_selector` function, and the `tiktoken` library for token counting. Additionally, we'll use the `rich` library for pretty printing. Below is a step-by-step implementation of the function:
|
|
280
|
+
#
|
|
281
|
+
#```python
|
|
282
|
+
import os
|
|
283
|
+
import json
|
|
284
|
+
from rich import print as rprint
|
|
285
|
+
from rich.markdown import Markdown
|
|
286
|
+
from langchain_core.prompts import PromptTemplate
|
|
287
|
+
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
|
|
288
|
+
from langchain_openai import ChatOpenAI
|
|
289
|
+
from llm_selector import llm_selector
|
|
290
|
+
import tiktoken
|
|
291
|
+
|
|
292
|
+
# Define the function
|
|
293
|
+
def split(input_prompt, input_code, example_code, strength, temperature):
|
|
294
|
+
# Step 1: Load the prompt files
|
|
295
|
+
pdd_path = os.getenv('PDD_PATH')
|
|
296
|
+
if not pdd_path:
|
|
297
|
+
raise ValueError("PDD_PATH environment variable is not set")
|
|
298
|
+
|
|
299
|
+
split_llm_prompt_path = os.path.join(pdd_path, 'prompts/xml', 'split_xml_LLM.prompt')
|
|
300
|
+
extract_prompt_split_llm_prompt_path = os.path.join(pdd_path, 'prompts', 'extract_prompt_split_LLM.prompt')
|
|
301
|
+
|
|
302
|
+
with open(split_llm_prompt_path, 'r') as file:
|
|
303
|
+
split_llm_prompt = file.read()
|
|
304
|
+
|
|
305
|
+
with open(extract_prompt_split_llm_prompt_path, 'r') as file:
|
|
306
|
+
extract_prompt_split_llm_prompt = file.read()
|
|
307
|
+
|
|
308
|
+
# Step 2: Create Langchain LCEL template for split_LLM
|
|
309
|
+
split_prompt_template = PromptTemplate.from_template(split_llm_prompt)
|
|
310
|
+
|
|
311
|
+
# Step 3: Use llm_selector to get the LLM model
|
|
312
|
+
llm, input_cost, output_cost = llm_selector(strength, temperature)
|
|
313
|
+
|
|
314
|
+
# Step 4: Run the input through the model using Langchain LCEL
|
|
315
|
+
chain = split_prompt_template | llm | StrOutputParser()
|
|
316
|
+
input_data = {{
|
|
317
|
+
"input_prompt": input_prompt,
|
|
318
|
+
"input_code": input_code,
|
|
319
|
+
"example_code": example_code
|
|
320
|
+
}}
|
|
321
|
+
llm_output = chain.invoke(input_data)
|
|
322
|
+
|
|
323
|
+
# Calculate token count and cost
|
|
324
|
+
encoding = tiktoken.get_encoding("cl100k_base")
|
|
325
|
+
input_tokens = len(encoding.encode(json.dumps(input_data)))
|
|
326
|
+
output_tokens = len(encoding.encode(llm_output))
|
|
327
|
+
total_cost = (input_tokens * input_cost + output_tokens * output_cost) / 1_000_000
|
|
328
|
+
|
|
329
|
+
rprint(f"[bold green]Running model...[/bold green]")
|
|
330
|
+
rprint(f"Input Tokens: {{input_tokens}}, Output Tokens: {{output_tokens}}, Estimated Cost: ${{total_cost:.6f}}")
|
|
331
|
+
|
|
332
|
+
# Step 5: Create Langchain LCEL template for extract_prompt_split_LLM
|
|
333
|
+
extract_prompt_template = PromptTemplate.from_template(extract_prompt_split_llm_prompt)
|
|
334
|
+
parser = JsonOutputParser()
|
|
335
|
+
|
|
336
|
+
chain = extract_prompt_template | llm | parser
|
|
337
|
+
result = chain.invoke({{"llm_output": llm_output}})
|
|
338
|
+
|
|
339
|
+
# Calculate token count and cost for extraction
|
|
340
|
+
extract_input_tokens = len(encoding.encode(llm_output))
|
|
341
|
+
extract_output_tokens = len(encoding.encode(json.dumps(result)))
|
|
342
|
+
extract_cost = (extract_input_tokens * input_cost + extract_output_tokens * output_cost) / 1_000_000
|
|
343
|
+
|
|
344
|
+
rprint(f"[bold green]Extracting prompts...[/bold green]")
|
|
345
|
+
rprint(f"Input Tokens: {{extract_input_tokens}}, Output Tokens: {{extract_output_tokens}}, Estimated Cost: ${{extract_cost:.6f}}")
|
|
346
|
+
|
|
347
|
+
# Step 6: Pretty print the extracted sub_prompt and modified_prompt
|
|
348
|
+
sub_prompt = result.get('sub_prompt', '')
|
|
349
|
+
modified_prompt = result.get('modified_prompt', '')
|
|
350
|
+
|
|
351
|
+
rprint(Markdown(f"**Sub Prompt:**\n{{sub_prompt}}"))
|
|
352
|
+
rprint(Markdown(f"**Modified Prompt:**\n{{modified_prompt}}"))
|
|
353
|
+
|
|
354
|
+
# Step 7: Return the sub_prompt, modified_prompt, and total_cost
|
|
355
|
+
return sub_prompt, modified_prompt, total_cost + extract_cost
|
|
356
|
+
|
|
357
|
+
# Example usage
|
|
358
|
+
# sub_prompt, modified_prompt, total_cost = split("input_prompt", "input_code", "example_code", 0.5, 0.7)
|
|
359
|
+
# rprint(f"Sub Prompt: {{sub_prompt}}, Modified Prompt: {{modified_prompt}}, Total Cost: {{total_cost}}")
|
|
360
|
+
#```
|
|
361
|
+
#
|
|
362
|
+
#### Explanation:
|
|
363
|
+
#1. **Environment Variables**: The function checks for the `PDD_PATH` environment variable to locate the prompt files.
|
|
364
|
+
#2. **Prompt Loading**: It reads the prompt templates from the specified files.
|
|
365
|
+
#3. **Langchain Setup**: It sets up the Langchain LCEL templates and chains them with the selected LLM model.
|
|
366
|
+
#4. **Token Counting**: It uses `tiktoken` to count tokens and calculate costs based on the input and output token counts.
|
|
367
|
+
#5. **Rich Printing**: It uses the `rich` library to pretty print the process and results.
|
|
368
|
+
#6. **Error Handling**: The function raises an error if the necessary environment variables are not set.
|
|
369
|
+
#
|
|
370
|
+
#This implementation assumes that the `llm_selector` function and the `tiktoken` library are correctly set up and available in your environment. Adjust paths and configurations as necessary for your specific setup.
|
|
371
|
+
```</example_input_code>
|
|
372
|
+
<example_change_prompt>```
|
|
373
|
+
Use token_counter from llm_selector instead of tiktoken to count tokens in the prompt.
|
|
374
|
+
```</example_change_prompt>
|
|
375
|
+
<example_modified_prompt>```
|
|
376
|
+
% You are an expert Python Software Engineer. Your goal is to write a Python function, "split", that will split a prompt into a sub_prompt and modified_prompt with no loss of functionality. All output to the console will be pretty printed using the Python Rich library.
|
|
377
|
+
|
|
378
|
+
% Here are the inputs and outputs of the function:
|
|
379
|
+
Inputs:
|
|
380
|
+
- 'input_prompt': A string containing the prompt that will be split into a sub_prompt and modified_prompt.
|
|
381
|
+
- 'input_code': A string containing the code that was generated from the input_prompt.
|
|
382
|
+
- 'example_code': A string containing the code example of how the code generated from the sub_prompt would be used by the code generated from the modified_prompt.
|
|
383
|
+
- 'strength': A float value representing the strength parameter for the LLM model, used to influence the model's behavior.
|
|
384
|
+
- 'temperature': A float value representing the temperature parameter for the LLM model, used to control the randomness of the model's output.
|
|
385
|
+
Outputs:
|
|
386
|
+
- 'sub_prompt': A string containing the sub_prompt that was split from the input_prompt.
|
|
387
|
+
- 'modified_prompt': A string containing the modified prompt from input_prompt split from the sub_prompt.
|
|
388
|
+
- 'total_cost': A float value representing the total cost of running the function.
|
|
389
|
+
|
|
390
|
+
% Here is an example how to preprocess the prompt from a file: ```
|
|
391
|
+
from preprocess import preprocess
|
|
392
|
+
from rich.console import Console
|
|
393
|
+
console = Console()
|
|
394
|
+
prompt = """
|
|
395
|
+
<prompt>
|
|
396
|
+
<include>LICENSE</include>
|
|
397
|
+
<shell>echo Hello World</shell>
|
|
398
|
+
<pdd>This is a comment</pdd>
|
|
399
|
+
``` <file_to_include.txt>```
|
|
400
|
+
</prompt>
|
|
401
|
+
"""
|
|
402
|
+
|
|
403
|
+
recursive = False
|
|
404
|
+
double_curly_brackets = True
|
|
405
|
+
|
|
406
|
+
processed = preprocess(prompt, recursive, double_curly_brackets)
|
|
407
|
+
console.print("[bold white]Processed Prompt:[/bold white]")
|
|
408
|
+
console.print(processed)
|
|
409
|
+
|
|
410
|
+
# load prompts/change_LLM.prompt
|
|
411
|
+
with open('prompts/xml/change_LLM.prompt', 'r') as file:
|
|
412
|
+
change_LLM_prompt = file.read()
|
|
413
|
+
|
|
414
|
+
# call preprocess on change_LLM_prompt
|
|
415
|
+
processed = preprocess(change_LLM_prompt, recursive, False)
|
|
416
|
+
console.print("[bold white]Processed change_LLM Prompt:[/bold white]")
|
|
417
|
+
console.print(processed)
|
|
418
|
+
|
|
419
|
+
# write the processed prompt to a file
|
|
420
|
+
with open('prompts/xml/change_LLM_processed.prompt', 'w') as file:
|
|
421
|
+
file.write(processed)
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
% Example usage of the Langchain LCEL program: ```
|
|
425
|
+
import os
|
|
426
|
+
from langchain_core.prompts import PromptTemplate
|
|
427
|
+
from langchain_community.cache import SQLiteCache
|
|
428
|
+
from langchain.globals import set_llm_cache
|
|
429
|
+
from langchain_core.output_parsers import JsonOutputParser
|
|
430
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
431
|
+
from langchain.output_parsers import RetryOutputParser
|
|
432
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
433
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
434
|
+
from langchain_core.runnables import RunnablePassthrough, ConfigurableField
|
|
435
|
+
|
|
436
|
+
from langchain_fireworks import Fireworks
|
|
437
|
+
from langchain_anthropic import ChatAnthropic
|
|
438
|
+
from langchain_openai import ChatOpenAI
|
|
439
|
+
from langchain_openai import OpenAI
|
|
440
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
441
|
+
from langchain_groq import ChatGroq
|
|
442
|
+
from langchain_together import Together
|
|
443
|
+
|
|
444
|
+
# Define a base output parser (e.g., PydanticOutputParser)
|
|
445
|
+
from pydantic import BaseModel, Field
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
# Setup cache to save money and increase speeds
|
|
449
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
# Create the LCEL template. Make note of the variable {{topic}} which will be filled in later.
|
|
453
|
+
prompt_template = PromptTemplate.from_template("Tell me a joke about {{topic}}")
|
|
454
|
+
|
|
455
|
+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
|
456
|
+
# Combine with a model and parser to output a string
|
|
457
|
+
chain = prompt_template |llm| StrOutputParser()
|
|
458
|
+
|
|
459
|
+
# Run the template. Notice that the input is a dictionary with a single key "topic" which feeds it into the above prompt template. This is needed because the prompt template has a variable {{topic}} which needs to be filled in when invoked.
|
|
460
|
+
result = chain.invoke({{"topic": "cats"}})
|
|
461
|
+
print(result)
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
# Define your desired data structure.
|
|
465
|
+
class Joke(BaseModel):
|
|
466
|
+
setup: str = Field(description="question to set up a joke")
|
|
467
|
+
punchline: str = Field(description="answer to resolve the joke")
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
llm = ChatGoogleGenerativeAI(model="gemini-pro", temperature=0)
|
|
471
|
+
# Set up a parser
|
|
472
|
+
parser = JsonOutputParser(pydantic_object=Joke)
|
|
473
|
+
|
|
474
|
+
# Create a prompt template
|
|
475
|
+
prompt = PromptTemplate(
|
|
476
|
+
template="Answer the user query.\n{{format_instructions}}\n{{query}}\n",
|
|
477
|
+
input_variables=["query"],
|
|
478
|
+
partial_variables={{"format_instructions": parser.get_format_instructions()}},
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
# Chain the components
|
|
482
|
+
chain = prompt | llm | parser
|
|
483
|
+
|
|
484
|
+
# Invoke the chain with a query
|
|
485
|
+
result = chain.invoke({{"query": "Tell me a joke."}})
|
|
486
|
+
print(result)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
# Get DEEKSEEK_API_KEY environmental variable
|
|
490
|
+
deepseek_api_key = os.getenv('DEEKSEEK_API_KEY')
|
|
491
|
+
|
|
492
|
+
# Ensure the API key is retrieved successfully
|
|
493
|
+
if deepseek_api_key is None:
|
|
494
|
+
raise ValueError("DEEKSEEK_API_KEY environment variable is not set")
|
|
495
|
+
|
|
496
|
+
llm = ChatOpenAI(
|
|
497
|
+
model='deepseek-chat',
|
|
498
|
+
openai_api_key=deepseek_api_key,
|
|
499
|
+
openai_api_base='https://api.deepseek.com',
|
|
500
|
+
temperature=0
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
# Chain the components
|
|
504
|
+
chain = prompt | llm | parser
|
|
505
|
+
|
|
506
|
+
# Invoke the chain with a query
|
|
507
|
+
result = chain.invoke({{"query": "Write joke about the sky"}})
|
|
508
|
+
print("deepseek",result)
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
llm = Fireworks(
|
|
512
|
+
model="accounts/fireworks/models/mixtral-8x7b-instruct",
|
|
513
|
+
temperature=0)
|
|
514
|
+
# Chain the components
|
|
515
|
+
chain = prompt | llm | parser
|
|
516
|
+
|
|
517
|
+
# Invoke the chain with a query
|
|
518
|
+
result = chain.invoke({{"query": "Tell me a joke about the president"}})
|
|
519
|
+
print("fireworks",result)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
prompt = ChatPromptTemplate.from_template(
|
|
526
|
+
"Tell me a short joke about {{topic}}"
|
|
527
|
+
)
|
|
528
|
+
chat_openai = ChatOpenAI(model="gpt-3.5-turbo")
|
|
529
|
+
openai = OpenAI(model="gpt-3.5-turbo-instruct")
|
|
530
|
+
anthropic = ChatAnthropic(model="claude-2")
|
|
531
|
+
model = (
|
|
532
|
+
chat_openai
|
|
533
|
+
.with_fallbacks([anthropic])
|
|
534
|
+
.configurable_alternatives(
|
|
535
|
+
ConfigurableField(id="model"),
|
|
536
|
+
default_key="chat_openai",
|
|
537
|
+
openai=openai,
|
|
538
|
+
anthropic=anthropic,
|
|
539
|
+
)
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
chain = (
|
|
543
|
+
{{"topic": RunnablePassthrough()}}
|
|
544
|
+
| prompt
|
|
545
|
+
| model
|
|
546
|
+
| StrOutputParser()
|
|
547
|
+
)
|
|
548
|
+
result = chain.invoke({{"topic": "Tell me a joke about the president"}})
|
|
549
|
+
print("config alt:",result)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
llm = ChatGroq(temperature=0, model_name="mixtral-8x7b-32768")
|
|
554
|
+
system = "You are a helpful assistant."
|
|
555
|
+
human = "{{text}}"
|
|
556
|
+
prompt = ChatPromptTemplate.from_messages([("system", system), ("human", human)])
|
|
557
|
+
|
|
558
|
+
chain = prompt | llm | StrOutputParser()
|
|
559
|
+
print(chain.invoke({{"text": "Explain the importance of low latency LLMs."}}))
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
llm = Together(
|
|
563
|
+
model="meta-llama/Llama-3-70b-chat-hf",
|
|
564
|
+
)
|
|
565
|
+
chain = prompt | llm | StrOutputParser()
|
|
566
|
+
print(chain.invoke({{"text": "Explain the importance of together.ai."}}))
|
|
567
|
+
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
% Example of selecting a Langchain LLM and counting tokens using llm_selector: ```
|
|
571
|
+
import os
|
|
572
|
+
from llm_selector import llm_selector
|
|
573
|
+
|
|
574
|
+
def main() -> None:
|
|
575
|
+
"""
|
|
576
|
+
Main function to demonstrate the usage of the llm_selector function.
|
|
577
|
+
Sets environment variables, defines parameters, and calls the function.
|
|
578
|
+
"""
|
|
579
|
+
# Set environment variables (for demonstration purposes)
|
|
580
|
+
os.environ['PDD_MODEL_DEFAULT'] = 'gpt-4o-mini' # Default model
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
# Define desired strength and temperature
|
|
584
|
+
strength: float = .3 # Desired strength of the model (0.0 to 1.0)
|
|
585
|
+
temperature: float = 0 # Temperature for the model (0.0 to 1.0)
|
|
586
|
+
|
|
587
|
+
try:
|
|
588
|
+
# Call the llm_selector function
|
|
589
|
+
llm, token_counter, input_cost, output_cost, model_name = llm_selector(strength, temperature)
|
|
590
|
+
|
|
591
|
+
# Output the selected model details
|
|
592
|
+
print(f"Selected LLM: {{model_name}}")
|
|
593
|
+
print(f"Input Cost: {{input_cost}}")
|
|
594
|
+
print(f"Output Cost: {{output_cost}}")
|
|
595
|
+
except Exception as e:
|
|
596
|
+
print(f"An error occurred: {{e}}")
|
|
597
|
+
|
|
598
|
+
if __name__ == "__main__":
|
|
599
|
+
main()
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
% Steps to be followed by the function:
|
|
603
|
+
1. Load the '$PDD_PATH/prompts/split_LLM.prompt' and '$PDD_PATH/prompts/extract_prompt_split_LLM.prompt' files.
|
|
604
|
+
2. Preprocess the split_LLM prompt using the preprocess function from the preprocess module.
|
|
605
|
+
2. Create a Langchain LCEL template from the processed split_LLM prompt to return a string output.
|
|
606
|
+
3. Use the llm_selector function for the LLM model and token counting.
|
|
607
|
+
4. Run the input through the model using Langchain LCEL:
|
|
608
|
+
- a. Pass the following string parameters to the prompt during invocation: 'input_prompt', 'input_code', 'example_code'.
|
|
609
|
+
- b. Calculate the input and output token count using token_counter from llm_selector and pretty print the running message, including the token count and estimated cost. The cost from llm_selector is in dollars per million tokens.
|
|
610
|
+
5. Create a Langchain LCEL template from the extract_prompt_split_LLM prompt that outputs JSON:
|
|
611
|
+
- a. Pass the following string parameters to the prompt during invocation: 'llm_output' (this string is from Step 4).
|
|
612
|
+
- b. Calculate input and output token count using token_counter from llm_selector and pretty print the running message with the token count and cost.
|
|
613
|
+
- c. get 'sub_prompt' and 'modified_prompt' from the JSON output.
|
|
614
|
+
6. Pretty print the extracted sub_prompt and modified_prompt using Rich Markdown function. Include token counts and costs.
|
|
615
|
+
7. Return the 'sub_prompt' and 'modified_prompt' strings and the total_cost.
|
|
616
|
+
|
|
617
|
+
% Ensure the function handles edge cases, such as missing inputs or model errors, and provide clear error messages.
|
|
618
|
+
```</example_modified_prompt>
|
|
619
|
+
</example>
|
|
620
|
+
<example>
|
|
621
|
+
<example_number>6</example_number>
|
|
622
|
+
<example_input_prompt>```
|
|
623
|
+
% You are an expert Python engineer. Your goal is to write a Python function, "xml_tagger", that will enhance a given LLM prompt by adding XML tags to improve its structure and readability. All output to the console will be pretty printed using the Python rich library.
|
|
624
|
+
|
|
625
|
+
% Here are the inputs and outputs of the function:
|
|
626
|
+
Input:
|
|
627
|
+
'raw_prompt' - A string containing the prompt that needs XML tagging to improve its organization and clarity.
|
|
628
|
+
'strength' - A float value representing the strength parameter for the LLM model.
|
|
629
|
+
'temperature' - A float value representing the temperature parameter for the LLM model.
|
|
630
|
+
Output:
|
|
631
|
+
'xml_tagged' - A string containing the prompt with properly added XML tags.
|
|
632
|
+
|
|
633
|
+
% Here is an example of a Langchain LCEL program: ```
|
|
634
|
+
import os
|
|
635
|
+
from langchain_core.prompts import PromptTemplate
|
|
636
|
+
from langchain_community.cache import SQLiteCache
|
|
637
|
+
from langchain.globals import set_llm_cache
|
|
638
|
+
from langchain_core.output_parsers import JsonOutputParser
|
|
639
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
640
|
+
from langchain.output_parsers import RetryOutputParser
|
|
641
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
642
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
643
|
+
from langchain_core.runnables import RunnablePassthrough, ConfigurableField
|
|
644
|
+
|
|
645
|
+
from langchain_fireworks import Fireworks
|
|
646
|
+
from langchain_anthropic import ChatAnthropic
|
|
647
|
+
from langchain_openai import ChatOpenAI
|
|
648
|
+
from langchain_openai import OpenAI
|
|
649
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
650
|
+
from langchain_groq import ChatGroq
|
|
651
|
+
from langchain_together import Together
|
|
652
|
+
|
|
653
|
+
# Define a base output parser (e.g., PydanticOutputParser)
|
|
654
|
+
from pydantic import BaseModel, Field
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
# Setup cache to save money and increase speeds
|
|
658
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
# Create the LCEL template. Make note of the variable {{topic}} which will be filled in later.
|
|
662
|
+
prompt_template = PromptTemplate.from_template("Tell me a joke about {{topic}}")
|
|
663
|
+
|
|
664
|
+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
|
665
|
+
# Combine with a model and parser to output a string
|
|
666
|
+
chain = prompt_template |llm| StrOutputParser()
|
|
667
|
+
|
|
668
|
+
# Run the template. Notice that the input is a dictionary with a single key "topic" which feeds it into the above prompt template. This is needed because the prompt template has a variable {{topic}} which needs to be filled in when invoked.
|
|
669
|
+
result = chain.invoke({{"topic": "cats"}})
|
|
670
|
+
print(result)
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
# Define your desired data structure.
|
|
674
|
+
class Joke(BaseModel):
|
|
675
|
+
setup: str = Field(description="question to set up a joke")
|
|
676
|
+
punchline: str = Field(description="answer to resolve the joke")
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
llm = ChatGoogleGenerativeAI(model="gemini-pro", temperature=0)
|
|
680
|
+
# Set up a parser
|
|
681
|
+
parser = JsonOutputParser(pydantic_object=Joke)
|
|
682
|
+
|
|
683
|
+
# Create a prompt template
|
|
684
|
+
prompt = PromptTemplate(
|
|
685
|
+
template="Answer the user query.\n{{format_instructions}}\n{{query}}\n",
|
|
686
|
+
input_variables=["query"],
|
|
687
|
+
partial_variables={{"format_instructions": parser.get_format_instructions()}},
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
# Chain the components
|
|
691
|
+
chain = prompt | llm | parser
|
|
692
|
+
|
|
693
|
+
# Invoke the chain with a query
|
|
694
|
+
result = chain.invoke({{"query": "Tell me a joke."}})
|
|
695
|
+
print(result)
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
# Get DEEKSEEK_API_KEY environmental variable
|
|
699
|
+
deepseek_api_key = os.getenv('DEEKSEEK_API_KEY')
|
|
700
|
+
|
|
701
|
+
# Ensure the API key is retrieved successfully
|
|
702
|
+
if deepseek_api_key is None:
|
|
703
|
+
raise ValueError("DEEKSEEK_API_KEY environment variable is not set")
|
|
704
|
+
|
|
705
|
+
llm = ChatOpenAI(
|
|
706
|
+
model='deepseek-chat',
|
|
707
|
+
openai_api_key=deepseek_api_key,
|
|
708
|
+
openai_api_base='https://api.deepseek.com',
|
|
709
|
+
temperature=0
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
# Chain the components
|
|
713
|
+
chain = prompt | llm | parser
|
|
714
|
+
|
|
715
|
+
# Invoke the chain with a query
|
|
716
|
+
result = chain.invoke({{"query": "Write joke about the sky"}})
|
|
717
|
+
print("deepseek",result)
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
llm = Fireworks(
|
|
721
|
+
model="accounts/fireworks/models/mixtral-8x7b-instruct",
|
|
722
|
+
temperature=0)
|
|
723
|
+
# Chain the components
|
|
724
|
+
chain = prompt | llm | parser
|
|
725
|
+
|
|
726
|
+
# Invoke the chain with a query
|
|
727
|
+
result = chain.invoke({{"query": "Tell me a joke about the president"}})
|
|
728
|
+
print("fireworks",result)
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
prompt = ChatPromptTemplate.from_template(
|
|
735
|
+
"Tell me a short joke about {{topic}}"
|
|
736
|
+
)
|
|
737
|
+
chat_openai = ChatOpenAI(model="gpt-3.5-turbo")
|
|
738
|
+
openai = OpenAI(model="gpt-3.5-turbo-instruct")
|
|
739
|
+
anthropic = ChatAnthropic(model="claude-2")
|
|
740
|
+
model = (
|
|
741
|
+
chat_openai
|
|
742
|
+
.with_fallbacks([anthropic])
|
|
743
|
+
.configurable_alternatives(
|
|
744
|
+
ConfigurableField(id="model"),
|
|
745
|
+
default_key="chat_openai",
|
|
746
|
+
openai=openai,
|
|
747
|
+
anthropic=anthropic,
|
|
748
|
+
)
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
chain = (
|
|
752
|
+
{{"topic": RunnablePassthrough()}}
|
|
753
|
+
| prompt
|
|
754
|
+
| model
|
|
755
|
+
| StrOutputParser()
|
|
756
|
+
)
|
|
757
|
+
result = chain.invoke({{"topic": "Tell me a joke about the president"}})
|
|
758
|
+
print("config alt:",result)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
llm = ChatGroq(temperature=0, model_name="mixtral-8x7b-32768")
|
|
763
|
+
system = "You are a helpful assistant."
|
|
764
|
+
human = "{{text}}"
|
|
765
|
+
prompt = ChatPromptTemplate.from_messages([("system", system), ("human", human)])
|
|
766
|
+
|
|
767
|
+
chain = prompt | llm | StrOutputParser()
|
|
768
|
+
print(chain.invoke({{"text": "Explain the importance of low latency LLMs."}}))
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
llm = Together(
|
|
772
|
+
model="meta-llama/Llama-3-70b-chat-hf",
|
|
773
|
+
)
|
|
774
|
+
chain = prompt | llm | StrOutputParser()
|
|
775
|
+
print(chain.invoke({{"text": "Explain the importance of together.ai."}}))
|
|
776
|
+
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
% Here is an example how to select the Langchain llm: ```
|
|
780
|
+
import os
|
|
781
|
+
from llm_selector import llm_selector
|
|
782
|
+
|
|
783
|
+
def main() -> None:
|
|
784
|
+
"""
|
|
785
|
+
Main function to demonstrate the usage of the llm_selector function.
|
|
786
|
+
Sets environment variables, defines parameters, and calls the function.
|
|
787
|
+
"""
|
|
788
|
+
# Set environment variables (for demonstration purposes)
|
|
789
|
+
os.environ['PDD_MODEL_DEFAULT'] = 'gpt-4o-mini' # Default model
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
# Define desired strength and temperature
|
|
793
|
+
strength: float = .3 # Desired strength of the model (0.0 to 1.0)
|
|
794
|
+
temperature: float = 0 # Temperature for the model (0.0 to 1.0)
|
|
795
|
+
|
|
796
|
+
try:
|
|
797
|
+
# Call the llm_selector function
|
|
798
|
+
llm, token_counter, input_cost, output_cost, model_name = llm_selector(strength, temperature)
|
|
799
|
+
|
|
800
|
+
# Output the selected model details
|
|
801
|
+
print(f"Selected LLM: {{model_name}}")
|
|
802
|
+
print(f"Input Cost: {{input_cost}}")
|
|
803
|
+
print(f"Output Cost: {{output_cost}}")
|
|
804
|
+
except Exception as e:
|
|
805
|
+
print(f"An error occurred: {{e}}")
|
|
806
|
+
|
|
807
|
+
if __name__ == "__main__":
|
|
808
|
+
main()
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
% Here is an example how to use tiktoken: ```
|
|
812
|
+
import tiktoken
|
|
813
|
+
encoding = tiktoken.get_encoding("cl100k_base") # or another encoding name
|
|
814
|
+
token_count = len(encoding.encode(preprocessed_prompt))
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
% This program will use Langchain to do the following:
|
|
818
|
+
Step 1. Use $PDD_PATH environment variable to get the path to the project. Load the '$PDD_PATH/prompts/xml_convertor_LLM.prompt' and '$PDD_PATH/prompts/extract_xml_LLM.prompt' files.
|
|
819
|
+
Step 2. Create a Langchain LCEL template from xml_convertor prompt so that it returns a string output.
|
|
820
|
+
Step 3. Use the llm_selector function for the LLM model.
|
|
821
|
+
Step 4. Run the code through the model using Langchain LCEL.
|
|
822
|
+
4a. Pass the following string parameters to the prompt during invoke:
|
|
823
|
+
- 'raw_prompt'
|
|
824
|
+
4b. Pretty print a message letting the user know it is running and how many tokens (using tiktoken) are in the prompt and the cost. The cost from llm_selector is in dollars per million tokens.
|
|
825
|
+
4c. The string output of the LCEL will be 'xml_generated_analysis' that contains the tagged prompt.
|
|
826
|
+
Step 5. The code result of the model will contain a mix of text and XML separated by triple backticks. Create a Langchain LCEL template from the extract_xml prompt that has a JSON output.
|
|
827
|
+
5a. Pass the following string parameters to the prompt during invoke:
|
|
828
|
+
- 'xml_generated_analysis'
|
|
829
|
+
5b. Pretty print a message letting the user know it is running and how many tokens (using tiktoken) are in the prompt and the cost. The cost from llm_selector is in dollars per million tokens.
|
|
830
|
+
5c. The JSON output of the LCEL will have the key 'xml_tagged' that contains the extracted tagged prompt.
|
|
831
|
+
Step 6. Pretty print the extracted tagged prompt using the rich Markdown function. Also, print the number of tokens in the result and the cost.
|
|
832
|
+
Step 7. Return the 'xml_tagged' string.
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
% Ensure that the function handles potential errors gracefully, such as missing input parameters or issues with the LLM model responses.
|
|
836
|
+
|
|
837
|
+
```</example_input_prompt>
|
|
838
|
+
<example_input_code>```
|
|
839
|
+
import os
|
|
840
|
+
import tiktoken
|
|
841
|
+
from rich import print as rprint
|
|
842
|
+
from rich.markdown import Markdown
|
|
843
|
+
from langchain_core.prompts import PromptTemplate
|
|
844
|
+
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
|
|
845
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
846
|
+
from langchain_community.cache import SQLiteCache
|
|
847
|
+
from langchain.globals import set_llm_cache
|
|
848
|
+
from llm_selector import llm_selector
|
|
849
|
+
|
|
850
|
+
# Ensure cache is set up
|
|
851
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
852
|
+
|
|
853
|
+
# Define a Pydantic model for JSON output
|
|
854
|
+
class XMLTaggedOutput(BaseModel):
|
|
855
|
+
xml_tagged: str = Field(description="The XML tagged prompt")
|
|
856
|
+
|
|
857
|
+
def xml_tagger(raw_prompt: str, strength: float, temperature: float) -> str:
|
|
858
|
+
"""
|
|
859
|
+
Process a raw prompt to apply XML tagging using Langchain.
|
|
860
|
+
|
|
861
|
+
:param raw_prompt: The input prompt to be processed.
|
|
862
|
+
:param strength: The strength parameter for LLM selection.
|
|
863
|
+
:param temperature: The temperature parameter for LLM selection.
|
|
864
|
+
:return: The XML tagged prompt as a string.
|
|
865
|
+
"""
|
|
866
|
+
try:
|
|
867
|
+
# Step 1: Load the prompt files
|
|
868
|
+
pdd_path = os.getenv('PDD_PATH')
|
|
869
|
+
if not pdd_path:
|
|
870
|
+
raise ValueError("PDD_PATH environment variable is not set")
|
|
871
|
+
|
|
872
|
+
with open(os.path.join(pdd_path, 'prompts/xml_convertor_LLM.prompt'), 'r') as file:
|
|
873
|
+
xml_convertor_prompt = file.read()
|
|
874
|
+
|
|
875
|
+
with open(os.path.join(pdd_path, 'prompts/extract_xml_LLM.prompt'), 'r') as file:
|
|
876
|
+
extract_xml_prompt = file.read()
|
|
877
|
+
|
|
878
|
+
# Step 2: Create LCEL template from xml_convertor prompt
|
|
879
|
+
xml_convertor_template = PromptTemplate.from_template(xml_convertor_prompt)
|
|
880
|
+
|
|
881
|
+
# Step 3: Use the llm_selector function
|
|
882
|
+
llm, input_cost, output_cost = llm_selector(strength, temperature)
|
|
883
|
+
|
|
884
|
+
# Step 4: Run the code through the model using Langchain LCEL
|
|
885
|
+
chain = xml_convertor_template | llm | StrOutputParser()
|
|
886
|
+
|
|
887
|
+
# Token count and cost calculation
|
|
888
|
+
encoding = tiktoken.get_encoding("cl100k_base")
|
|
889
|
+
token_count = len(encoding.encode(raw_prompt))
|
|
890
|
+
cost = (token_count / 1_000_000) * input_cost
|
|
891
|
+
|
|
892
|
+
rprint(f"[bold green]Running XML conversion...[/bold green]")
|
|
893
|
+
rprint(f"Token count: {{token_count}}, Cost: ${{cost:.6f}}")
|
|
894
|
+
|
|
895
|
+
# Invoke the chain
|
|
896
|
+
xml_generated_analysis = chain.invoke({{"raw_prompt": raw_prompt}})
|
|
897
|
+
|
|
898
|
+
# Step 5: Create LCEL template from extract_xml prompt
|
|
899
|
+
extract_xml_template = PromptTemplate.from_template(extract_xml_prompt)
|
|
900
|
+
parser = JsonOutputParser(pydantic_object=XMLTaggedOutput)
|
|
901
|
+
|
|
902
|
+
chain = extract_xml_template | llm | parser
|
|
903
|
+
|
|
904
|
+
# Token count and cost calculation for the second step
|
|
905
|
+
token_count = len(encoding.encode(xml_generated_analysis))
|
|
906
|
+
cost = (token_count / 1_000_000) * output_cost
|
|
907
|
+
|
|
908
|
+
rprint(f"[bold green]Extracting XML...[/bold green]")
|
|
909
|
+
rprint(f"Token count: {{token_count}}, Cost: ${{cost:.6f}}")
|
|
910
|
+
|
|
911
|
+
# Invoke the chain
|
|
912
|
+
result = chain.invoke({{"xml_generated_analysis": xml_generated_analysis}})
|
|
913
|
+
|
|
914
|
+
# Step 6: Pretty print the extracted tagged prompt
|
|
915
|
+
xml_tagged = result['xml_tagged']
|
|
916
|
+
rprint(Markdown(xml_tagged))
|
|
917
|
+
rprint(f"Token count in result: {{len(encoding.encode(xml_tagged))}}, Cost: ${{(len(encoding.encode(xml_tagged)) / 1_000_000) * output_cost:.6f}}")
|
|
918
|
+
|
|
919
|
+
# Step 7: Return the 'xml_tagged' string
|
|
920
|
+
return xml_tagged
|
|
921
|
+
|
|
922
|
+
except Exception as e:
|
|
923
|
+
rprint(f"[bold red]Error:[/bold red] {{e}}")
|
|
924
|
+
return ""
|
|
925
|
+
|
|
926
|
+
# Example usage
|
|
927
|
+
# xml_tagger("Tell me a joke about cats", 0.5, 0.7)
|
|
928
|
+
```</example_input_code>
|
|
929
|
+
<example_change_prompt>```
|
|
930
|
+
Use token_counter from llm_selector instead of tiktoken to count tokens in the prompt. Also, xml tagger should also return the total cost of running the LCELs.
|
|
931
|
+
```</example_change_prompt>
|
|
932
|
+
<example_modified_prompt>```
|
|
933
|
+
% You are an expert Python engineer. Your goal is to write a Python function, "xml_tagger", that will enhance a given LLM prompt by adding XML tags to improve its structure and readability. All output to the console will be pretty printed using the Python rich library.
|
|
934
|
+
|
|
935
|
+
% Here are the inputs and outputs of the function:
|
|
936
|
+
Input:
|
|
937
|
+
'raw_prompt' - A string containing the prompt that needs XML tagging to improve its organization and clarity.
|
|
938
|
+
'strength' - A float value representing the strength parameter for the LLM model.
|
|
939
|
+
'temperature' - A float value representing the temperature parameter for the LLM model.
|
|
940
|
+
Output:
|
|
941
|
+
'xml_tagged' - A string containing the prompt with properly added XML tags.
|
|
942
|
+
'total_cost' - A float representing the total cost of running the LCELs.
|
|
943
|
+
|
|
944
|
+
% Here is an example of a Langchain LCEL program: ```
|
|
945
|
+
import os
|
|
946
|
+
from langchain_core.prompts import PromptTemplate
|
|
947
|
+
from langchain_community.cache import SQLiteCache
|
|
948
|
+
from langchain.globals import set_llm_cache
|
|
949
|
+
from langchain_core.output_parsers import JsonOutputParser
|
|
950
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
951
|
+
from langchain.output_parsers import RetryOutputParser
|
|
952
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
953
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
954
|
+
from langchain_core.runnables import RunnablePassthrough, ConfigurableField
|
|
955
|
+
|
|
956
|
+
from langchain_fireworks import Fireworks
|
|
957
|
+
from langchain_anthropic import ChatAnthropic
|
|
958
|
+
from langchain_openai import ChatOpenAI
|
|
959
|
+
from langchain_openai import OpenAI
|
|
960
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
961
|
+
from langchain_groq import ChatGroq
|
|
962
|
+
from langchain_together import Together
|
|
963
|
+
|
|
964
|
+
# Define a base output parser (e.g., PydanticOutputParser)
|
|
965
|
+
from pydantic import BaseModel, Field
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
# Setup cache to save money and increase speeds
|
|
969
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
# Create the LCEL template. Make note of the variable {{topic}} which will be filled in later.
|
|
973
|
+
prompt_template = PromptTemplate.from_template("Tell me a joke about {{topic}}")
|
|
974
|
+
|
|
975
|
+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
|
976
|
+
# Combine with a model and parser to output a string
|
|
977
|
+
chain = prompt_template |llm| StrOutputParser()
|
|
978
|
+
|
|
979
|
+
# Run the template. Notice that the input is a dictionary with a single key "topic" which feeds it into the above prompt template. This is needed because the prompt template has a variable {{topic}} which needs to be filled in when invoked.
|
|
980
|
+
result = chain.invoke({{"topic": "cats"}})
|
|
981
|
+
print(result)
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
# Define your desired data structure.
|
|
985
|
+
class Joke(BaseModel):
|
|
986
|
+
setup: str = Field(description="question to set up a joke")
|
|
987
|
+
punchline: str = Field(description="answer to resolve the joke")
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
llm = ChatGoogleGenerativeAI(model="gemini-pro", temperature=0)
|
|
991
|
+
# Set up a parser
|
|
992
|
+
parser = JsonOutputParser(pydantic_object=Joke)
|
|
993
|
+
|
|
994
|
+
# Create a prompt template
|
|
995
|
+
prompt = PromptTemplate(
|
|
996
|
+
template="Answer the user query.\n{{format_instructions}}\n{{query}}\n",
|
|
997
|
+
input_variables=["query"],
|
|
998
|
+
partial_variables={{"format_instructions": parser.get_format_instructions()}},
|
|
999
|
+
)
|
|
1000
|
+
|
|
1001
|
+
# Chain the components
|
|
1002
|
+
chain = prompt | llm | parser
|
|
1003
|
+
|
|
1004
|
+
# Invoke the chain with a query
|
|
1005
|
+
result = chain.invoke({{"query": "Tell me a joke."}})
|
|
1006
|
+
print(result)
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
# Get DEEKSEEK_API_KEY environmental variable
|
|
1010
|
+
deepseek_api_key = os.getenv('DEEKSEEK_API_KEY')
|
|
1011
|
+
|
|
1012
|
+
# Ensure the API key is retrieved successfully
|
|
1013
|
+
if deepseek_api_key is None:
|
|
1014
|
+
raise ValueError("DEEKSEEK_API_KEY environment variable is not set")
|
|
1015
|
+
|
|
1016
|
+
llm = ChatOpenAI(
|
|
1017
|
+
model='deepseek-chat',
|
|
1018
|
+
openai_api_key=deepseek_api_key,
|
|
1019
|
+
openai_api_base='https://api.deepseek.com',
|
|
1020
|
+
temperature=0
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
# Chain the components
|
|
1024
|
+
chain = prompt | llm | parser
|
|
1025
|
+
|
|
1026
|
+
# Invoke the chain with a query
|
|
1027
|
+
result = chain.invoke({{"query": "Write joke about the sky"}})
|
|
1028
|
+
print("deepseek",result)
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
llm = Fireworks(
|
|
1032
|
+
model="accounts/fireworks/models/mixtral-8x7b-instruct",
|
|
1033
|
+
temperature=0)
|
|
1034
|
+
# Chain the components
|
|
1035
|
+
chain = prompt | llm | parser
|
|
1036
|
+
|
|
1037
|
+
# Invoke the chain with a query
|
|
1038
|
+
result = chain.invoke({{"query": "Tell me a joke about the president"}})
|
|
1039
|
+
print("fireworks",result)
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
prompt = ChatPromptTemplate.from_template(
|
|
1046
|
+
"Tell me a short joke about {{topic}}"
|
|
1047
|
+
)
|
|
1048
|
+
chat_openai = ChatOpenAI(model="gpt-3.5-turbo")
|
|
1049
|
+
openai = OpenAI(model="gpt-3.5-turbo-instruct")
|
|
1050
|
+
anthropic = ChatAnthropic(model="claude-2")
|
|
1051
|
+
model = (
|
|
1052
|
+
chat_openai
|
|
1053
|
+
.with_fallbacks([anthropic])
|
|
1054
|
+
.configurable_alternatives(
|
|
1055
|
+
ConfigurableField(id="model"),
|
|
1056
|
+
default_key="chat_openai",
|
|
1057
|
+
openai=openai,
|
|
1058
|
+
anthropic=anthropic,
|
|
1059
|
+
)
|
|
1060
|
+
)
|
|
1061
|
+
|
|
1062
|
+
chain = (
|
|
1063
|
+
{{"topic": RunnablePassthrough()}}
|
|
1064
|
+
| prompt
|
|
1065
|
+
| model
|
|
1066
|
+
| StrOutputParser()
|
|
1067
|
+
)
|
|
1068
|
+
result = chain.invoke({{"topic": "Tell me a joke about the president"}})
|
|
1069
|
+
print("config alt:",result)
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
llm = ChatGroq(temperature=0, model_name="mixtral-8x7b-32768")
|
|
1074
|
+
system = "You are a helpful assistant."
|
|
1075
|
+
human = "{{text}}"
|
|
1076
|
+
prompt = ChatPromptTemplate.from_messages([("system", system), ("human", human)])
|
|
1077
|
+
|
|
1078
|
+
chain = prompt | llm | StrOutputParser()
|
|
1079
|
+
print(chain.invoke({{"text": "Explain the importance of low latency LLMs."}}))
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
llm = Together(
|
|
1083
|
+
model="meta-llama/Llama-3-70b-chat-hf",
|
|
1084
|
+
)
|
|
1085
|
+
chain = prompt | llm | StrOutputParser()
|
|
1086
|
+
print(chain.invoke({{"text": "Explain the importance of together.ai."}}))
|
|
1087
|
+
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
% Here is an example how to select the Langchain llm and count tokens: ```
|
|
1091
|
+
import os
|
|
1092
|
+
from llm_selector import llm_selector
|
|
1093
|
+
|
|
1094
|
+
def main() -> None:
|
|
1095
|
+
"""
|
|
1096
|
+
Main function to demonstrate the usage of the llm_selector function.
|
|
1097
|
+
Sets environment variables, defines parameters, and calls the function.
|
|
1098
|
+
"""
|
|
1099
|
+
# Set environment variables (for demonstration purposes)
|
|
1100
|
+
os.environ['PDD_MODEL_DEFAULT'] = 'gpt-4o-mini' # Default model
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
# Define desired strength and temperature
|
|
1104
|
+
strength: float = .3 # Desired strength of the model (0.0 to 1.0)
|
|
1105
|
+
temperature: float = 0 # Temperature for the model (0.0 to 1.0)
|
|
1106
|
+
|
|
1107
|
+
try:
|
|
1108
|
+
# Call the llm_selector function
|
|
1109
|
+
llm, token_counter, input_cost, output_cost, model_name = llm_selector(strength, temperature)
|
|
1110
|
+
|
|
1111
|
+
# Output the selected model details
|
|
1112
|
+
print(f"Selected LLM: {{model_name}}")
|
|
1113
|
+
print(f"Input Cost: {{input_cost}}")
|
|
1114
|
+
print(f"Output Cost: {{output_cost}}")
|
|
1115
|
+
except Exception as e:
|
|
1116
|
+
print(f"An error occurred: {{e}}")
|
|
1117
|
+
|
|
1118
|
+
if __name__ == "__main__":
|
|
1119
|
+
main()
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
% This program will use Langchain to do the following:
|
|
1123
|
+
Step 1. Use $PDD_PATH environment variable to get the path to the project. Load the '$PDD_PATH/prompts/xml_convertor_LLM.prompt' and '$PDD_PATH/prompts/extract_xml_LLM.prompt' files.
|
|
1124
|
+
Step 2. Create a Langchain LCEL template from xml_convertor prompt so that it returns a string output.
|
|
1125
|
+
Step 3. Use the llm_selector function for the LLM model and token counting.
|
|
1126
|
+
Step 4. Run the code through the model using Langchain LCEL.
|
|
1127
|
+
4a. Pass the following string parameters to the prompt during invoke:
|
|
1128
|
+
- 'raw_prompt'
|
|
1129
|
+
4b. Pretty print a message letting the user know it is running and how many tokens (using token_counter from llm_selector) are in the prompt and the cost. The cost from llm_selector is in dollars per million tokens.
|
|
1130
|
+
4c. The string output of the LCEL will be 'xml_generated_analysis' that contains the tagged prompt.
|
|
1131
|
+
Step 5. The code result of the model will contain a mix of text and XML separated by triple backticks. Create a Langchain LCEL template from the extract_xml prompt that has a JSON output.
|
|
1132
|
+
5a. Pass the following string parameters to the prompt during invoke:
|
|
1133
|
+
- 'xml_generated_analysis'
|
|
1134
|
+
5b. Pretty print a message letting the user know it is running and how many tokens (using token_counter from llm_selector) are in the prompt and the cost. The cost from llm_selector is in dollars per million tokens.
|
|
1135
|
+
5c. The JSON output of the LCEL will have the key 'xml_tagged' that contains the extracted tagged prompt.
|
|
1136
|
+
Step 6. Pretty print the extracted tagged prompt using the rich Markdown function. Also, print the number of tokens in the result and the cost.
|
|
1137
|
+
Step 7. Calculate the total cost by summing the costs from both LCEL runs.
|
|
1138
|
+
Step 8. Return the 'xml_tagged' string and the 'total_cost'.
|
|
1139
|
+
|
|
1140
|
+
% Ensure that the function handles potential errors gracefully, such as missing input parameters or issues with the LLM model responses.
|
|
1141
|
+
```</example_modified_prompt>
|
|
1142
|
+
</example>
|
|
1143
|
+
<example>
|
|
1144
|
+
<example_number>7</example_number>
|
|
1145
|
+
<example_input_prompt>```
|
|
1146
|
+
% You are an expert Python Software engineer. Your goal is to write a Python program, "fix_errors.py". All output to the console will be pretty printed with the Python rich package.
|
|
1147
|
+
|
|
1148
|
+
% You will be using a CLI program called pdd. Here is a detailed description of the program functionality: ```
|
|
1149
|
+
# PDD (Prompt-Driven Development) Command Line Interface
|
|
1150
|
+
|
|
1151
|
+
PDD is a versatile tool for generating code, examples, unit tests, and managing prompts through various features like splitting large prompts into smaller ones.
|
|
1152
|
+
|
|
1153
|
+
## Prompt File Naming Convention
|
|
1154
|
+
|
|
1155
|
+
Prompt files in PDD follow this specific naming format:
|
|
1156
|
+
```
|
|
1157
|
+
<basename>_<language>.prompt
|
|
1158
|
+
```
|
|
1159
|
+
Where:
|
|
1160
|
+
- `<basename>` is the base name of the file or project
|
|
1161
|
+
- `<language>` is the programming language or context of the prompt
|
|
1162
|
+
|
|
1163
|
+
Examples:
|
|
1164
|
+
- `pdd_cli_python.prompt` (basename: pdd_cli, language: python)
|
|
1165
|
+
- `Makefile_makefile.prompt` (basename: Makefile, language: makefile)
|
|
1166
|
+
- `setup_bash.prompt` (basename: setup, language: bash)
|
|
1167
|
+
|
|
1168
|
+
## Basic Usage
|
|
1169
|
+
|
|
1170
|
+
```
|
|
1171
|
+
python pdd/pdd.py [GLOBAL OPTIONS] COMMAND [OPTIONS] [ARGS]...
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
## Global Options
|
|
1175
|
+
|
|
1176
|
+
These options can be used with any command:
|
|
1177
|
+
|
|
1178
|
+
- `--force`: Overwrite existing files without asking for confirmation.
|
|
1179
|
+
- `--strength`: Set the strength of the AI model (default is 0.5).
|
|
1180
|
+
- `--temperature`: Set the temperature of the AI model (default is 0.0).
|
|
1181
|
+
- `--verbose`: Increase output verbosity for more detailed information.
|
|
1182
|
+
- `--quiet`: Decrease output verbosity for minimal information.
|
|
1183
|
+
|
|
1184
|
+
## Commands
|
|
1185
|
+
|
|
1186
|
+
Here are the main commands:
|
|
1187
|
+
|
|
1188
|
+
### 1. Generate
|
|
1189
|
+
|
|
1190
|
+
Create runnable code from a prompt file.
|
|
1191
|
+
|
|
1192
|
+
```
|
|
1193
|
+
pdd generate [GLOBAL OPTIONS] [OPTIONS] PROMPT_FILE
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
Options:
|
|
1197
|
+
- `--output LOCATION`: Specify where to save the generated code. The default file name is `<basename>.<language_file_extension>`. If an environment variable `PDD_GENERATE_OUTPUT_PATH` is set, the file will be saved in that path unless overridden by this option.
|
|
1198
|
+
|
|
1199
|
+
### 2. Example
|
|
1200
|
+
|
|
1201
|
+
Create an example file from an existing code file.
|
|
1202
|
+
|
|
1203
|
+
```
|
|
1204
|
+
pdd example [GLOBAL OPTIONS] [OPTIONS] CODE_FILE
|
|
1205
|
+
```
|
|
1206
|
+
|
|
1207
|
+
Options:
|
|
1208
|
+
- `--output LOCATION`: Specify where to save the generated example code. The default file name is `<basename>_example.<language_file_extension>`. If an environment variable `PDD_EXAMPLE_OUTPUT_PATH` is set, the file will be saved in that path unless overridden by this option.
|
|
1209
|
+
|
|
1210
|
+
### 3. Test
|
|
1211
|
+
|
|
1212
|
+
Generate a unit test file for a given code file and its corresponding prompt.
|
|
1213
|
+
|
|
1214
|
+
```
|
|
1215
|
+
pdd test [GLOBAL OPTIONS] [OPTIONS] CODE_FILE PROMPT_FILE
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
Options:
|
|
1219
|
+
- `--output LOCATION`: Specify where to save the generated test file. The default file name is `test_<basename>.<language_file_extension>`. If an environment variable `PDD_TEST_OUTPUT_PATH` is set, the file will be saved in that path unless overridden by this option.
|
|
1220
|
+
- `--language`: Specify the programming language. Defaults to the language specified by the prompt file name.
|
|
1221
|
+
|
|
1222
|
+
### 4. Preprocess
|
|
1223
|
+
|
|
1224
|
+
Preprocess prompts and save the results.
|
|
1225
|
+
|
|
1226
|
+
```
|
|
1227
|
+
pdd preprocess [GLOBAL OPTIONS] [OPTIONS] PROMPT_FILE
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
Options:
|
|
1231
|
+
- `--output LOCATION`: Specify where to save the preprocessed prompt. The default file name is `<basename>_<language>_preprocessed.prompt`. If an environment variable `PDD_PREPROCESS_OUTPUT_PATH` is set, the file will be saved in that path unless overridden by this option.
|
|
1232
|
+
- `--xml`: Automatically insert XML delimiters for long and complex prompts to structure the content better.
|
|
1233
|
+
|
|
1234
|
+
### 5. Fix
|
|
1235
|
+
|
|
1236
|
+
Fix errors in code and unit tests based on error messages.
|
|
1237
|
+
|
|
1238
|
+
```
|
|
1239
|
+
pdd fix [GLOBAL OPTIONS] [OPTIONS] UNIT_TEST_FILE CODE_FILE ERROR_FILE
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
Options:
|
|
1243
|
+
- `--output-test LOCATION`: Specify where to save the fixed unit test file. The default file name is `test_<basename>_fixed.<language_file_extension>`. If an environment variable `PDD_FIX_TEST_OUTPUT_PATH` is set, the file will be saved in that path unless overridden by this option.
|
|
1244
|
+
- `--output-code LOCATION`: Specify where to save the fixed code file. The default file name is `<basename>_fixed.<language_file_extension>`. If an environment variable `PDD_FIX_CODE_OUTPUT_PATH` is set, the file will be saved in that path unless overridden by this option.
|
|
1245
|
+
|
|
1246
|
+
### 6. Split
|
|
1247
|
+
|
|
1248
|
+
Split large complex prompts into smaller, more manageable prompts.
|
|
1249
|
+
|
|
1250
|
+
```
|
|
1251
|
+
pdd split [GLOBAL OPTIONS] [OPTIONS] INPUT_PROMPT INPUT_CODE EXAMPLE_CODE
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1254
|
+
Options:
|
|
1255
|
+
- `--output-sub LOCATION`: Specify where to save the generated sub-prompt. The default file name is `sub_<basename>.prompt`. If an environment variable `PDD_SPLIT_SUB_PROMPT_OUTPUT_PATH` is set, the file will be saved in that path unless overridden by this option.
|
|
1256
|
+
- `--output-modified LOCATION`: Specify where to save the modified prompt. The default file name is `modified_<basename>.prompt`. If an environment variable `PDD_SPLIT_MODIFIED_PROMPT_OUTPUT_PATH` is set, the file will be saved in that path unless overridden by this option.
|
|
1257
|
+
- `--output-cost LOCATION`: Specify where to save the cost estimation report. The default file name is `cost_<basename>.txt`. If an environment variable `PDD_SPLIT_COST_OUTPUT_PATH` is set, the file will be saved in that path unless overridden by this option.
|
|
1258
|
+
|
|
1259
|
+
## Output Location Specification
|
|
1260
|
+
|
|
1261
|
+
For all commands that generate or modify files, the `--output` option (or its variant, such as `--output-sub`, `--output-modified`, or `--output-cost` for the `split` command) allows flexible specification of the output location:
|
|
1262
|
+
|
|
1263
|
+
1. **Filename only**: If you provide just a filename (e.g., `--output result.py`), the file will be created in the current working directory.
|
|
1264
|
+
2. **Full path**: If you provide a full path (e.g., `--output /home/user/projects/result.py`), the file will be created at that exact location.
|
|
1265
|
+
3. **Directory**: If you provide a directory name (e.g., `--output ./generated/`), a file with an automatically generated name will be created in that directory.
|
|
1266
|
+
4. **Environment Variable**: If the `--output` option is not provided, and an environment variable specific to the command (`PDD_GENERATE_OUTPUT_PATH`, `PDD_EXAMPLE_OUTPUT_PATH`, `PDD_TEST_OUTPUT_PATH`, `PDD_PREPROCESS_OUTPUT_PATH`, `PDD_FIX_TEST_OUTPUT_PATH`, `PDD_FIX_CODE_OUTPUT_PATH`, `PDD_SPLIT_SUB_PROMPT_OUTPUT_PATH`, `PDD_SPLIT_MODIFIED_PROMPT_OUTPUT_PATH`, `PDD_SPLIT_COST_OUTPUT_PATH`) is set, PDD will use the path specified by this variable. Otherwise, it will use default naming conventions and save the file in the current working directory.
|
|
1267
|
+
5. **No Output Location**: If no output location is specified and no environment variable is set, the file will be saved in the current working directory with a default name given the command.
|
|
1268
|
+
|
|
1269
|
+
## Multi-Command Chaining
|
|
1270
|
+
|
|
1271
|
+
PDD supports multi-command chaining, allowing you to execute multiple commands in a single line. Commands will be executed in the order they are specified.
|
|
1272
|
+
|
|
1273
|
+
Basic syntax for multi-command chaining:
|
|
1274
|
+
```
|
|
1275
|
+
pdd [GLOBAL OPTIONS] COMMAND1 [OPTIONS] [ARGS]... [COMMAND2 [OPTIONS] [ARGS]...]...
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
This feature enables you to perform complex workflows efficiently.
|
|
1279
|
+
|
|
1280
|
+
## Getting Help
|
|
1281
|
+
|
|
1282
|
+
PDD provides comprehensive help features:
|
|
1283
|
+
|
|
1284
|
+
1. **General Help**:
|
|
1285
|
+
```
|
|
1286
|
+
pdd --help
|
|
1287
|
+
```
|
|
1288
|
+
Displays a list of available commands and options.
|
|
1289
|
+
|
|
1290
|
+
2. **Command-Specific Help**:
|
|
1291
|
+
```
|
|
1292
|
+
pdd COMMAND --help
|
|
1293
|
+
```
|
|
1294
|
+
Provides detailed help for a specific command, including available options and usage examples.
|
|
1295
|
+
|
|
1296
|
+
## Additional Features
|
|
1297
|
+
|
|
1298
|
+
- **Tab Completion**: PDD supports tab completion for commands and options in compatible shells. You can install tab completion by running:
|
|
1299
|
+
```
|
|
1300
|
+
pdd --install-completion
|
|
1301
|
+
```
|
|
1302
|
+
- **Colorized Output**: PDD provides colorized output for better readability in compatible terminals.
|
|
1303
|
+
- **Progress Indicators**: For long-running operations, PDD includes progress indicators to keep you informed of the task's status.
|
|
1304
|
+
|
|
1305
|
+
## Examples of Common Workflows
|
|
1306
|
+
|
|
1307
|
+
1. Preprocess a prompt, generate code, create an example, and generate tests (using multi-command chaining):
|
|
1308
|
+
```
|
|
1309
|
+
pdd preprocess --output preprocessed/ --temperature 0.0 app_python.prompt generate --output src/app.py --temperature 0.0 preprocessed/app_python_preprocessed.prompt example --output examples/ --temperature 0.0 src/app.py test --output tests/ --language python --temperature 0.0 src/app.py app_python.prompt
|
|
1310
|
+
```
|
|
1311
|
+
|
|
1312
|
+
2. Generate code and create examples for multiple prompts (using multi-command chaining):
|
|
1313
|
+
```
|
|
1314
|
+
pdd generate --output src/api.py --temperature 0.0 api_python.prompt generate --output src/db.py --temperature 0.0 database_sql.prompt example --output examples/api_usage.py --temperature 0.0 src/api.py example --output examples/db_usage.py --temperature 0.0 src/db.py
|
|
1315
|
+
```
|
|
1316
|
+
|
|
1317
|
+
3. Preprocess a prompt and view the diff:
|
|
1318
|
+
```
|
|
1319
|
+
pdd preprocess --output preprocessed/app_python_preprocessed.prompt --diff --temperature 0.0 app_python.prompt
|
|
1320
|
+
```
|
|
1321
|
+
|
|
1322
|
+
4. Preprocess a prompt with XML delimiters inserted:
|
|
1323
|
+
```
|
|
1324
|
+
pdd preprocess --output preprocessed/app_python_preprocessed.xml --xml app_python.prompt
|
|
1325
|
+
```
|
|
1326
|
+
|
|
1327
|
+
5. Preprocess multiple prompts and generate code for each (using multi-command chaining):
|
|
1328
|
+
```
|
|
1329
|
+
pdd preprocess --output preprocessed/ --temperature 0.0 api_python.prompt preprocess --output preprocessed/ --temperature 0.0 db_sql.prompt generate --output src/ --temperature 0.0 preprocessed/api_python_preprocessed.prompt generate --output src/ --temperature 0.0 preprocessed/db_sql_preprocessed.prompt
|
|
1330
|
+
```
|
|
1331
|
+
|
|
1332
|
+
6. Fix errors in code and unit tests:
|
|
1333
|
+
```
|
|
1334
|
+
pdd fix --output-test fixed/test_app_fixed.py --output-code fixed/app_fixed.py --strength 0.7 --temperature 0.0 tests/test_app.py src/app.py error_log.txt
|
|
1335
|
+
```
|
|
1336
|
+
|
|
1337
|
+
7. Split a large prompt into smaller prompts:
|
|
1338
|
+
```
|
|
1339
|
+
pdd split --output-sub sub_prompts/sub_app_python.prompt --output-modified modified_prompts/modified_app_python.prompt --output-cost cost_reports/cost_app_python.txt --strength 0.8 --temperature 0.0 large_app_python.prompt related_code.py example_code.py
|
|
1340
|
+
```
|
|
1341
|
+
|
|
1342
|
+
This example splits a large prompt (`large_app_python.prompt`) into smaller sub-prompts and modifies the original prompt accordingly. The sub-prompt is saved in the `sub_prompts/` directory, the modified prompt is saved in the `modified_prompts/` directory, and a cost estimation report is saved in the `cost_reports/` directory.
|
|
1343
|
+
|
|
1344
|
+
## Environment Variables for Output Paths
|
|
1345
|
+
|
|
1346
|
+
You can set environment variables to define default output paths for each command, reducing the need to specify output locations in the command line. The following environment variables are supported:
|
|
1347
|
+
|
|
1348
|
+
- **`PDD_GENERATE_OUTPUT_PATH`**: Default path for the `generate` command.
|
|
1349
|
+
- **`PDD_EXAMPLE_OUTPUT_PATH`**: Default path for the `example` command.
|
|
1350
|
+
- **`PDD_TEST_OUTPUT_PATH`**: Default path for the `test` command.
|
|
1351
|
+
- **`PDD_PREPROCESS_OUTPUT_PATH`**: Default path for the `preprocess` command.
|
|
1352
|
+
- **`PDD_FIX_TEST_OUTPUT_PATH`**: Default path for the fixed unit test files in the `fix` command.
|
|
1353
|
+
- **`PDD_FIX_CODE_OUTPUT_PATH`**: Default path for the fixed code files in the `fix` command.
|
|
1354
|
+
- **`PDD_SPLIT_SUB_PROMPT_OUTPUT_PATH`**: Default path for the sub-prompts generated by the `split` command.
|
|
1355
|
+
- **`PDD_SPLIT_MODIFIED_PROMPT_OUTPUT_PATH`**: Default path for the modified prompts generated by the `split` command.
|
|
1356
|
+
- **`PDD_SPLIT_COST_OUTPUT_PATH`**: Default path for the cost estimation reports generated by the `split` command.
|
|
1357
|
+
|
|
1358
|
+
If these environment variables are set, the corresponding files will be saved to the specified paths by default unless overridden by the `--output`, `--output-sub`, `--output-modified`, or `--output-cost` options.
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
% This script will take in the following arguments:
|
|
1362
|
+
- unit_test_file
|
|
1363
|
+
- code_file
|
|
1364
|
+
- Python program to run to verify code still runs
|
|
1365
|
+
- Strength
|
|
1366
|
+
- Number of times to run before giving up
|
|
1367
|
+
|
|
1368
|
+
% Follow these steps:
|
|
1369
|
+
Step 1. Remove the existing error.log file.
|
|
1370
|
+
Step 2. Run the unit_test_file with 'python -m pytest -vv' and pipe all output to error.log. If the test fails, then proceed to step 3.
|
|
1371
|
+
Step 3.
|
|
1372
|
+
a. Print out the error message from error.log.
|
|
1373
|
+
b. Count the number of 'FAILED' and 'ERROR' from stdout. Keep in mind that given the '-vv' flag, the output will contain doubled the number of 'FAILED' and 'ERROR' messages.
|
|
1374
|
+
c. Make a copy of the unit_test_file and code_file but append the number of failed and errors, and the loop iteration number to the file names like this "unit_test_1_0_3.py" and "code_1_0_3.py", where there was one fail, zero errors and it is the third iteration through the loop.
|
|
1375
|
+
Step 4. Run 'python pdd/pdd.py' fix on the unit_test_file, code_file and error.log with the output being written to unit_test_file and code_file. Make sure global options come before the command when calling pdd. The pdd console output will get appended to the error.log with a separator between pytest and pdd program runs.
|
|
1376
|
+
Step 5. Run the Python program to verify the code still runs.
|
|
1377
|
+
a. If the program still runs then repeat the process from Step 2 with the updated unit test and code files unless the loop limit is reached.
|
|
1378
|
+
b. Otherwise, if program fails, then restore the original files and repeat the process from Step 4.
|
|
1379
|
+
Step 6. Run pytest one last time and pipe all output to error.log and print to console.
|
|
1380
|
+
```</example_input_prompt>
|
|
1381
|
+
<example_input_code>```
|
|
1382
|
+
# To create the `fix_errors.py` script, we'll follow the steps outlined in your instructions. The script will automate the process of running unit tests, capturing errors, and using the PDD tool to attempt to fix those errors. Here's how the script can be structured:
|
|
1383
|
+
|
|
1384
|
+
# ```python
|
|
1385
|
+
import subprocess
|
|
1386
|
+
import shutil
|
|
1387
|
+
import os
|
|
1388
|
+
import sys
|
|
1389
|
+
|
|
1390
|
+
def run_pytest(unit_test_file):
|
|
1391
|
+
"""Run pytest on the given unit test file and capture output."""
|
|
1392
|
+
result = subprocess.run(
|
|
1393
|
+
['python', '-m', 'pytest', '-vv',
|
|
1394
|
+
unit_test_file],
|
|
1395
|
+
stdout=subprocess.PIPE,
|
|
1396
|
+
stderr=subprocess.PIPE,
|
|
1397
|
+
text=True
|
|
1398
|
+
)
|
|
1399
|
+
with open('error.log', 'a') as log_file:
|
|
1400
|
+
log_file.write(result.stdout)
|
|
1401
|
+
log_file.write(result.stderr)
|
|
1402
|
+
print(result.stdout)
|
|
1403
|
+
print(result.stderr)
|
|
1404
|
+
return result.returncode, result.stdout
|
|
1405
|
+
|
|
1406
|
+
def extract_errors(log_content):
|
|
1407
|
+
"""Extract the number of failed tests and errors from the pytest output."""
|
|
1408
|
+
failed = int(log_content.count('FAILED') / 2)
|
|
1409
|
+
errors = int(log_content.count('ERROR') / 2)
|
|
1410
|
+
|
|
1411
|
+
return failed, errors
|
|
1412
|
+
|
|
1413
|
+
def copy_files_with_suffix(unit_test_file, code_file, failed, errors, iteration):
|
|
1414
|
+
"""Copy the unit test and code files with a suffix indicating the current state."""
|
|
1415
|
+
unit_test_copy = f"{{os.path.splitext(unit_test_file)[0]}}_{{failed}}_{{errors}}_{{iteration}}.py"
|
|
1416
|
+
code_copy = f"{{os.path.splitext(code_file)[0]}}_{{failed}}_{{errors}}_{{iteration}}.py"
|
|
1417
|
+
shutil.copy(unit_test_file, unit_test_copy)
|
|
1418
|
+
shutil.copy(code_file, code_copy)
|
|
1419
|
+
return unit_test_copy, code_copy
|
|
1420
|
+
|
|
1421
|
+
def run_pdd_fix(unit_test_file, code_file, error_file, strength):
|
|
1422
|
+
"""Run the PDD fix command."""
|
|
1423
|
+
result = subprocess.run(
|
|
1424
|
+
[
|
|
1425
|
+
'python', 'pdd/pdd.py', '--strength', str(strength), 'fix',
|
|
1426
|
+
'--output-test', unit_test_file, '--output-code', code_file,
|
|
1427
|
+
unit_test_file, code_file, error_file
|
|
1428
|
+
],
|
|
1429
|
+
stdout=subprocess.PIPE,
|
|
1430
|
+
stderr=subprocess.PIPE,
|
|
1431
|
+
text=True
|
|
1432
|
+
)
|
|
1433
|
+
with open('error.log', 'a') as log_file:
|
|
1434
|
+
log_file.write("\n--- Attempted Fix of Above Pytest Failures and Errors ---\n")
|
|
1435
|
+
log_file.write(result.stdout)
|
|
1436
|
+
log_file.write(result.stderr)
|
|
1437
|
+
print(result.stdout)
|
|
1438
|
+
print(result.stderr)
|
|
1439
|
+
return result.returncode
|
|
1440
|
+
|
|
1441
|
+
def run_verification_program(verification_program):
|
|
1442
|
+
"""Run the verification program to ensure the code still runs."""
|
|
1443
|
+
result = subprocess.run(
|
|
1444
|
+
['python', verification_program],
|
|
1445
|
+
stdout=subprocess.PIPE,
|
|
1446
|
+
stderr=subprocess.PIPE,
|
|
1447
|
+
text=True
|
|
1448
|
+
)
|
|
1449
|
+
return result.returncode == 0
|
|
1450
|
+
|
|
1451
|
+
def main(unit_test_file, code_file, verification_program, strength, max_iterations):
|
|
1452
|
+
iteration = 0
|
|
1453
|
+
previous_failed = previous_errors = 0
|
|
1454
|
+
|
|
1455
|
+
while iteration < max_iterations:
|
|
1456
|
+
return_code, log_content = run_pytest(unit_test_file)
|
|
1457
|
+
if return_code == 0:
|
|
1458
|
+
print("All tests passed.")
|
|
1459
|
+
break
|
|
1460
|
+
|
|
1461
|
+
failed, errors = extract_errors(log_content)
|
|
1462
|
+
|
|
1463
|
+
print(f"Iteration {{iteration}}: {{failed}} failed, {{errors}} errors")
|
|
1464
|
+
|
|
1465
|
+
if failed == 0 and errors == 0:
|
|
1466
|
+
print("No new errors detected. Exiting.")
|
|
1467
|
+
break
|
|
1468
|
+
|
|
1469
|
+
unit_test_copy, code_copy = copy_files_with_suffix(unit_test_file, code_file, failed, errors, iteration)
|
|
1470
|
+
|
|
1471
|
+
run_pdd_fix(unit_test_file, code_file, 'error.log', strength)
|
|
1472
|
+
|
|
1473
|
+
if run_verification_program(verification_program):
|
|
1474
|
+
print("Verification program ran successfully.")
|
|
1475
|
+
else:
|
|
1476
|
+
print("Verification program failed. Restoring original files.")
|
|
1477
|
+
shutil.copy(unit_test_copy, unit_test_file)
|
|
1478
|
+
shutil.copy(code_copy, code_file)
|
|
1479
|
+
|
|
1480
|
+
iteration += 1
|
|
1481
|
+
|
|
1482
|
+
run_pytest(unit_test_file)
|
|
1483
|
+
|
|
1484
|
+
if __name__ == "__main__":
|
|
1485
|
+
if len(sys.argv) != 6:
|
|
1486
|
+
print("Usage: python fix_errors.py <unit_test_file> <code_file> <verification_program> <strength> <max_iterations>")
|
|
1487
|
+
sys.exit(1)
|
|
1488
|
+
|
|
1489
|
+
unit_test_file = sys.argv[1]
|
|
1490
|
+
code_file = sys.argv[2]
|
|
1491
|
+
verification_program = sys.argv[3]
|
|
1492
|
+
strength = float(sys.argv[4])
|
|
1493
|
+
max_iterations = int(sys.argv[5])
|
|
1494
|
+
|
|
1495
|
+
main(unit_test_file, code_file, verification_program, strength, max_iterations)
|
|
1496
|
+
# ```
|
|
1497
|
+
|
|
1498
|
+
# ### Explanation:
|
|
1499
|
+
|
|
1500
|
+
# 1. **Run Pytest**: The script runs the unit tests using `pytest` and captures the output in `error.log`.
|
|
1501
|
+
|
|
1502
|
+
# 2. **Extract Errors**: It extracts the number of failed tests and errors from the pytest output.
|
|
1503
|
+
|
|
1504
|
+
# 3. **Copy Files**: It makes copies of the unit test and code files with a suffix indicating the number of failures, errors, and the current iteration.
|
|
1505
|
+
|
|
1506
|
+
# 4. **Run PDD Fix**: It uses the PDD tool to attempt to fix the errors, appending the output to `error.log`.
|
|
1507
|
+
|
|
1508
|
+
# 5. **Verification**: It runs a verification program to ensure the code still runs correctly.
|
|
1509
|
+
|
|
1510
|
+
# 6. **Loop Control**: The process repeats until all tests pass or the maximum number of iterations is reached.
|
|
1511
|
+
|
|
1512
|
+
# This script should be run with the appropriate arguments as specified in the usage message.
|
|
1513
|
+
```</example_input_code>
|
|
1514
|
+
<example_change_prompt>```
|
|
1515
|
+
% Instead of using the CLI program 'pdd', you will be using this code module instead: ```from fix_errors_from_unit_tests import fix_errors_from_unit_tests
|
|
1516
|
+
|
|
1517
|
+
if __name__ == "__main__":
|
|
1518
|
+
# Define the inputs
|
|
1519
|
+
unit_test = "def test_add():\n assert add(1, 2) == 3" # A unit test that is expected to fail
|
|
1520
|
+
code = "def add(a, b):\n return a + b" # The code that the unit test is testing
|
|
1521
|
+
error = "NameError: name 'add' is not defined" # The error message indicating the issue
|
|
1522
|
+
strength = 0.8 # A strength parameter for the LLM selection
|
|
1523
|
+
|
|
1524
|
+
# Call the function to fix errors
|
|
1525
|
+
updated_unit_test, updated_code, fixed_unit_test, fixed_code = fix_errors_from_unit_tests(unit_test, code, error, strength)
|
|
1526
|
+
|
|
1527
|
+
# Print the results
|
|
1528
|
+
print("Updated Unit Test:", updated_unit_test)
|
|
1529
|
+
print("Updated Code:", updated_code)
|
|
1530
|
+
print("Fixed Unit Test:", fixed_unit_test)
|
|
1531
|
+
print("Fixed Code:", fixed_code)
|
|
1532
|
+
# ```
|
|
1533
|
+
|
|
1534
|
+
# ### Input Parameters
|
|
1535
|
+
|
|
1536
|
+
# - `unit_test` (str): The unit test code that is expected to fail due to an error.
|
|
1537
|
+
# - `code` (str): The implementation code that the unit test is testing.
|
|
1538
|
+
# - `error` (str): The error message that indicates what went wrong during the unit test execution.
|
|
1539
|
+
# - `strength` (float): A parameter that influences the selection of the language model (LLM) used for fixing the errors. It typically ranges from 0 to 1.
|
|
1540
|
+
|
|
1541
|
+
# ### Output Parameters
|
|
1542
|
+
|
|
1543
|
+
# The function returns four values:
|
|
1544
|
+
# - `updated_unit_test` (bool): Indicates whether the unit test needs to be updated.
|
|
1545
|
+
# - `updated_code` (bool): Indicates whether the code needs to be updated.
|
|
1546
|
+
# - `fixed_unit_test` (str): The corrected version of the unit test code.
|
|
1547
|
+
# - `fixed_code` (str): The corrected version of the implementation code.```
|
|
1548
|
+
|
|
1549
|
+
% Also, instead of being a CLI program, this code module will be a Python function "fix_error_loop" that will also take in temperature.
|
|
1550
|
+
```</example_change_prompt>
|
|
1551
|
+
<example_modified_prompt>```
|
|
1552
|
+
% You are an expert Python Software Engineer. Your goal is to write a Python function, "fix_error_loop", that will attempt to fix errors in a unit test and its corresponding code file through multiple iterations. All output to the console will be pretty printed using the Python rich library.
|
|
1553
|
+
|
|
1554
|
+
% Here are the inputs and outputs of the function:
|
|
1555
|
+
Inputs:
|
|
1556
|
+
'unit_test_file' - A string containing the path to the unit test file.
|
|
1557
|
+
'code_file' - A string containing the path to the code file being tested.
|
|
1558
|
+
'verification_program' - A string containing the path to a Python program that verifies if the code still runs correctly.
|
|
1559
|
+
'strength' - A float between 0 and 1 that represents the strength of the LLM model to use.
|
|
1560
|
+
'temperature' - A float that represents the temperature parameter for the LLM model.
|
|
1561
|
+
'max_attempts' - An integer representing the maximum number of fix attempts before giving up.
|
|
1562
|
+
Outputs:
|
|
1563
|
+
'success' - A boolean indicating whether the errors were successfully fixed.
|
|
1564
|
+
'final_unit_test' - A string containing the contents of the final unit test file.
|
|
1565
|
+
'final_code' - A string containing the contents of the final code file.
|
|
1566
|
+
'total_attempts' - An integer representing the number of fix attempts made.
|
|
1567
|
+
|
|
1568
|
+
% Here is an example of the fix_errors_from_unit_tests function that will be used: ```
|
|
1569
|
+
from fix_errors_from_unit_tests import fix_errors_from_unit_tests
|
|
1570
|
+
|
|
1571
|
+
# Define the inputs for the function
|
|
1572
|
+
unit_test: str = """
|
|
1573
|
+
def test_addition():
|
|
1574
|
+
assert add(1, 1) == 3 # Intentional error
|
|
1575
|
+
"""
|
|
1576
|
+
|
|
1577
|
+
code: str = """
|
|
1578
|
+
def add(a, b):
|
|
1579
|
+
return a + b
|
|
1580
|
+
"""
|
|
1581
|
+
|
|
1582
|
+
error: str = "AssertionError: assert 2 == 3"
|
|
1583
|
+
error_file: str = "error_log.txt"
|
|
1584
|
+
strength: float = 0.7 # Strength parameter for LLM selection
|
|
1585
|
+
temperature: float = 0 # Temperature parameter for LLM selection
|
|
1586
|
+
|
|
1587
|
+
try:
|
|
1588
|
+
# Call the function to fix errors in the unit tests
|
|
1589
|
+
update_unit_test, update_code, fixed_unit_test, fixed_code, total_cost = fix_errors_from_unit_tests(
|
|
1590
|
+
unit_test=unit_test,
|
|
1591
|
+
code=code,
|
|
1592
|
+
error=error,
|
|
1593
|
+
error_file=error_file,
|
|
1594
|
+
strength=strength,
|
|
1595
|
+
temperature=temperature
|
|
1596
|
+
)
|
|
1597
|
+
|
|
1598
|
+
# Print the results
|
|
1599
|
+
print(f"Update Unit Test: {{update_unit_test}}")
|
|
1600
|
+
print(f"Update Code: {{update_code}}")
|
|
1601
|
+
print(f"Fixed Unit Test:\n{{fixed_unit_test}}")
|
|
1602
|
+
print(f"Fixed Code:\n{{fixed_code}}")
|
|
1603
|
+
print(f"Total Cost: ${{total_cost:.6f}}")
|
|
1604
|
+
except Exception as e:
|
|
1605
|
+
print(f"An error occurred: {{e}}")
|
|
1606
|
+
```
|
|
1607
|
+
|
|
1608
|
+
% This function will do the following:
|
|
1609
|
+
Step 1. Remove the existing error.log file if it exists.
|
|
1610
|
+
Step 2. Initialize a counter for the number of attempts.
|
|
1611
|
+
Step 3. Enter a while loop that continues until max_attempts is reached:
|
|
1612
|
+
a. Run the unit_test_file with 'python -m pytest -vv' and pipe all output to error.log.
|
|
1613
|
+
b. If the test passes, break the loop.
|
|
1614
|
+
c. If the test fails:
|
|
1615
|
+
- Read and print the error message from error.log.
|
|
1616
|
+
- Count the number of 'FAILED' and 'ERROR' from stdout (accounting for the -vv flag doubling these messages).
|
|
1617
|
+
- Create backup copies of the unit_test_file and code_file, appending the number of fails, errors, and the current iteration number to the filenames like this "unit_test_1_0_3.py" and "code_1_0_3.py", where there was one fail, zero errors and it is the third iteration through the loop.
|
|
1618
|
+
- Read the contents of the unit_test_file and code_file.
|
|
1619
|
+
- Call fix_errors_from_unit_tests with the file contents, error from error.log, and the provided strength.
|
|
1620
|
+
- If both updated_unit_test and updated_code are False, break the loop as no changes were needed.
|
|
1621
|
+
- If either updated_unit_test or updated_code is True:
|
|
1622
|
+
* Write the fixed_unit_test and fixed_code back to their respective files.
|
|
1623
|
+
* Run the verification_program to check if the code still runs.
|
|
1624
|
+
* If the verification fails, restore the original files from the backups and continue the loop.
|
|
1625
|
+
* If the verification succeeds, continue to the next iteration.
|
|
1626
|
+
d. Increment the attempt counter.
|
|
1627
|
+
Step 4. After the loop ends, run pytest one last time, pipe the output to error.log, and print it to the console.
|
|
1628
|
+
Step 5. Return the success status, final unit test contents, final code contents, and total number of attempts.
|
|
1629
|
+
|
|
1630
|
+
% Ensure that the function handles potential errors gracefully, such as file I/O errors or subprocess execution failures. Use the rich library for all console output to enhance readability. Consider using context managers for file operations to ensure proper resource management.
|
|
1631
|
+
|
|
1632
|
+
% Note: The temperature parameter should be incorporated into the LLM selection process. You may need to modify the fix_errors_from_unit_tests function or use it in conjunction with the llm_selector to properly utilize this parameter.
|
|
1633
|
+
```</example_modified_prompt>
|
|
1634
|
+
</example>
|
|
1635
|
+
<example>
|
|
1636
|
+
<example_number>8</example_number>
|
|
1637
|
+
<example_input_prompt>```
|
|
1638
|
+
% You are an expert Python Software Engineer. Your goal is to write a python function, "fix_errors_from_unit_tests", that will fix unit test errors in a code file. All output to the console will be pretty print using the Python rich library.
|
|
1639
|
+
|
|
1640
|
+
% Here are the inputs and outputs of the function:
|
|
1641
|
+
Inputs:
|
|
1642
|
+
'unit_test' - A string containing the unit test code.
|
|
1643
|
+
'code' - A string containing the code under test.
|
|
1644
|
+
'error' - A string that contains the errors that need to be fixed.
|
|
1645
|
+
'strength' - A float between 0 and 1 that is the strength of the LLM model to use.
|
|
1646
|
+
Outputs:
|
|
1647
|
+
'update_unit_test': Boolean indicating whether the unit test needs to be updated.
|
|
1648
|
+
'update_code': Boolean indicating whether the code under test needs to be updated.
|
|
1649
|
+
'fixed_unit_test' - a string that is the fixed unit test.
|
|
1650
|
+
'fixed_code' - a string that is the fixed code under test.
|
|
1651
|
+
|
|
1652
|
+
% Here is an example of a Langchain LCEL program: ```
|
|
1653
|
+
import os
|
|
1654
|
+
from langchain_core.prompts import PromptTemplate
|
|
1655
|
+
from langchain_community.cache import SQLiteCache
|
|
1656
|
+
from langchain.globals import set_llm_cache
|
|
1657
|
+
from langchain_core.output_parsers import JsonOutputParser
|
|
1658
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
1659
|
+
from langchain.output_parsers import RetryOutputParser
|
|
1660
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
1661
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
1662
|
+
from langchain_core.runnables import RunnablePassthrough, ConfigurableField
|
|
1663
|
+
|
|
1664
|
+
from langchain_fireworks import Fireworks
|
|
1665
|
+
from langchain_anthropic import ChatAnthropic
|
|
1666
|
+
from langchain_openai import ChatOpenAI
|
|
1667
|
+
from langchain_openai import OpenAI
|
|
1668
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
1669
|
+
from langchain_groq import ChatGroq
|
|
1670
|
+
from langchain_together import Together
|
|
1671
|
+
|
|
1672
|
+
# Define a base output parser (e.g., PydanticOutputParser)
|
|
1673
|
+
from pydantic import BaseModel, Field
|
|
1674
|
+
|
|
1675
|
+
|
|
1676
|
+
# Setup cache to save money and increase speeds
|
|
1677
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
1678
|
+
|
|
1679
|
+
|
|
1680
|
+
# Create the LCEL template. Make note of the variable {{topic}} which will be filled in later.
|
|
1681
|
+
prompt_template = PromptTemplate.from_template("Tell me a joke about {{topic}}")
|
|
1682
|
+
|
|
1683
|
+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
|
1684
|
+
# Combine with a model and parser to output a string
|
|
1685
|
+
chain = prompt_template |llm| StrOutputParser()
|
|
1686
|
+
|
|
1687
|
+
# Run the template. Notice that the input is a dictionary with a single key "topic" which feeds it into the above prompt template. This is needed because the prompt template has a variable {{topic}} which needs to be filled in when invoked.
|
|
1688
|
+
result = chain.invoke({{"topic": "cats"}})
|
|
1689
|
+
print(result)
|
|
1690
|
+
|
|
1691
|
+
|
|
1692
|
+
# Define your desired data structure.
|
|
1693
|
+
class Joke(BaseModel):
|
|
1694
|
+
setup: str = Field(description="question to set up a joke")
|
|
1695
|
+
punchline: str = Field(description="answer to resolve the joke")
|
|
1696
|
+
|
|
1697
|
+
|
|
1698
|
+
llm = ChatGoogleGenerativeAI(model="gemini-pro", temperature=0)
|
|
1699
|
+
# Set up a parser
|
|
1700
|
+
parser = JsonOutputParser(pydantic_object=Joke)
|
|
1701
|
+
|
|
1702
|
+
# Create a prompt template
|
|
1703
|
+
prompt = PromptTemplate(
|
|
1704
|
+
template="Answer the user query.\n{{format_instructions}}\n{{query}}\n",
|
|
1705
|
+
input_variables=["query"],
|
|
1706
|
+
partial_variables={{"format_instructions": parser.get_format_instructions()}},
|
|
1707
|
+
)
|
|
1708
|
+
|
|
1709
|
+
# Chain the components
|
|
1710
|
+
chain = prompt | llm | parser
|
|
1711
|
+
|
|
1712
|
+
# Invoke the chain with a query
|
|
1713
|
+
result = chain.invoke({{"query": "Tell me a joke."}})
|
|
1714
|
+
print(result)
|
|
1715
|
+
|
|
1716
|
+
|
|
1717
|
+
# Get DEEKSEEK_API_KEY environmental variable
|
|
1718
|
+
deepseek_api_key = os.getenv('DEEKSEEK_API_KEY')
|
|
1719
|
+
|
|
1720
|
+
# Ensure the API key is retrieved successfully
|
|
1721
|
+
if deepseek_api_key is None:
|
|
1722
|
+
raise ValueError("DEEKSEEK_API_KEY environment variable is not set")
|
|
1723
|
+
|
|
1724
|
+
llm = ChatOpenAI(
|
|
1725
|
+
model='deepseek-chat',
|
|
1726
|
+
openai_api_key=deepseek_api_key,
|
|
1727
|
+
openai_api_base='https://api.deepseek.com',
|
|
1728
|
+
temperature=0
|
|
1729
|
+
)
|
|
1730
|
+
|
|
1731
|
+
# Chain the components
|
|
1732
|
+
chain = prompt | llm | parser
|
|
1733
|
+
|
|
1734
|
+
# Invoke the chain with a query
|
|
1735
|
+
result = chain.invoke({{"query": "Write joke about the sky"}})
|
|
1736
|
+
print("deepseek",result)
|
|
1737
|
+
|
|
1738
|
+
|
|
1739
|
+
llm = Fireworks(
|
|
1740
|
+
model="accounts/fireworks/models/mixtral-8x7b-instruct",
|
|
1741
|
+
temperature=0)
|
|
1742
|
+
# Chain the components
|
|
1743
|
+
chain = prompt | llm | parser
|
|
1744
|
+
|
|
1745
|
+
# Invoke the chain with a query
|
|
1746
|
+
result = chain.invoke({{"query": "Tell me a joke about the president"}})
|
|
1747
|
+
print("fireworks",result)
|
|
1748
|
+
|
|
1749
|
+
|
|
1750
|
+
|
|
1751
|
+
|
|
1752
|
+
|
|
1753
|
+
prompt = ChatPromptTemplate.from_template(
|
|
1754
|
+
"Tell me a short joke about {{topic}}"
|
|
1755
|
+
)
|
|
1756
|
+
chat_openai = ChatOpenAI(model="gpt-3.5-turbo")
|
|
1757
|
+
openai = OpenAI(model="gpt-3.5-turbo-instruct")
|
|
1758
|
+
anthropic = ChatAnthropic(model="claude-2")
|
|
1759
|
+
model = (
|
|
1760
|
+
chat_openai
|
|
1761
|
+
.with_fallbacks([anthropic])
|
|
1762
|
+
.configurable_alternatives(
|
|
1763
|
+
ConfigurableField(id="model"),
|
|
1764
|
+
default_key="chat_openai",
|
|
1765
|
+
openai=openai,
|
|
1766
|
+
anthropic=anthropic,
|
|
1767
|
+
)
|
|
1768
|
+
)
|
|
1769
|
+
|
|
1770
|
+
chain = (
|
|
1771
|
+
{{"topic": RunnablePassthrough()}}
|
|
1772
|
+
| prompt
|
|
1773
|
+
| model
|
|
1774
|
+
| StrOutputParser()
|
|
1775
|
+
)
|
|
1776
|
+
result = chain.invoke({{"topic": "Tell me a joke about the president"}})
|
|
1777
|
+
print("config alt:",result)
|
|
1778
|
+
|
|
1779
|
+
|
|
1780
|
+
|
|
1781
|
+
llm = ChatGroq(temperature=0, model_name="mixtral-8x7b-32768")
|
|
1782
|
+
system = "You are a helpful assistant."
|
|
1783
|
+
human = "{{text}}"
|
|
1784
|
+
prompt = ChatPromptTemplate.from_messages([("system", system), ("human", human)])
|
|
1785
|
+
|
|
1786
|
+
chain = prompt | llm | StrOutputParser()
|
|
1787
|
+
print(chain.invoke({{"text": "Explain the importance of low latency LLMs."}}))
|
|
1788
|
+
|
|
1789
|
+
|
|
1790
|
+
llm = Together(
|
|
1791
|
+
model="meta-llama/Llama-3-70b-chat-hf",
|
|
1792
|
+
)
|
|
1793
|
+
chain = prompt | llm | StrOutputParser()
|
|
1794
|
+
print(chain.invoke({{"text": "Explain the importance of together.ai."}}))
|
|
1795
|
+
|
|
1796
|
+
```
|
|
1797
|
+
|
|
1798
|
+
% Here is an example how to select the Langchain llm: ```
|
|
1799
|
+
import os
|
|
1800
|
+
from llm_selector import llm_selector
|
|
1801
|
+
|
|
1802
|
+
def main() -> None:
|
|
1803
|
+
"""
|
|
1804
|
+
Main function to demonstrate the usage of the llm_selector function.
|
|
1805
|
+
Sets environment variables, defines parameters, and calls the function.
|
|
1806
|
+
"""
|
|
1807
|
+
# Set environment variables (for demonstration purposes)
|
|
1808
|
+
os.environ['PDD_MODEL_DEFAULT'] = 'gpt-4o-mini' # Default model
|
|
1809
|
+
|
|
1810
|
+
|
|
1811
|
+
# Define desired strength and temperature
|
|
1812
|
+
strength: float = .3 # Desired strength of the model (0.0 to 1.0)
|
|
1813
|
+
temperature: float = 0 # Temperature for the model (0.0 to 1.0)
|
|
1814
|
+
|
|
1815
|
+
try:
|
|
1816
|
+
# Call the llm_selector function
|
|
1817
|
+
llm, token_counter, input_cost, output_cost, model_name = llm_selector(strength, temperature)
|
|
1818
|
+
|
|
1819
|
+
# Output the selected model details
|
|
1820
|
+
print(f"Selected LLM: {{model_name}}")
|
|
1821
|
+
print(f"Input Cost: {{input_cost}}")
|
|
1822
|
+
print(f"Output Cost: {{output_cost}}")
|
|
1823
|
+
except Exception as e:
|
|
1824
|
+
print(f"An error occurred: {{e}}")
|
|
1825
|
+
|
|
1826
|
+
if __name__ == "__main__":
|
|
1827
|
+
main()
|
|
1828
|
+
```
|
|
1829
|
+
|
|
1830
|
+
% Here is an example how to use tiktoken: ```
|
|
1831
|
+
import tiktoken
|
|
1832
|
+
encoding = tiktoken.get_encoding("cl100k_base") # or another encoding name
|
|
1833
|
+
token_count = len(encoding.encode(preprocessed_prompt))
|
|
1834
|
+
```
|
|
1835
|
+
|
|
1836
|
+
% This program will use Langchain to do the following:
|
|
1837
|
+
Step 1. Use $PDD_PATH environment variable to get the path to the project. Load the '$PDD_PATH/prompts/fix_errors_from_unit_tests_LLM.prompt' file. Also load the 'extract_unit_code_fix_LLM.prompt' from the same directory.
|
|
1838
|
+
Step 2. Then this will create a Langchain LCEL template from the fix_errors_from_unit_tests prompt.
|
|
1839
|
+
Step 3. This will use llm_selector and a temperature of 0 for the llm model.
|
|
1840
|
+
Step 4. This will run the code through the model using Langchain LCEL.
|
|
1841
|
+
4a. Be sure to pass the following string parameters to the prompt during invoke:
|
|
1842
|
+
- 'unit_test'
|
|
1843
|
+
- 'code'
|
|
1844
|
+
- 'errors'
|
|
1845
|
+
4b. Pretty print a message letting the user know it is running and how many tokens (using tiktoken) are in the prompt and the cost. The cost from llm_selector is in dollars per million tokens.
|
|
1846
|
+
Step 5. This will pretty print the markdown formatting that is present in the result via the rich Markdown function. It will also pretty print the number of tokens in the result and the cost. Also, print out the total cost.
|
|
1847
|
+
Step 7. Then this will create a second Langchain LCEL template from the extract_unit_code_fix prompt.
|
|
1848
|
+
Step 8. This will use llm_selector with a strength setting of 0.5 instead of the strength function input above and a temperature of 0 for the llm model. However, instead of using String output, it will use the JSON output parser to get these keys: 'update_unit_test', 'update_code', 'fixed_unit_test' and 'fixed_code'.
|
|
1849
|
+
Step 9. This will run the code through the model using Langchain LCEL from Step 8.
|
|
1850
|
+
9a. Be sure to pass the following string parameters to the prompt during invoke:
|
|
1851
|
+
- 'unit_test_fix': This is the result of the Langchain LCEL from Step 4.
|
|
1852
|
+
- 'unit_test'
|
|
1853
|
+
- 'code'
|
|
1854
|
+
9b. Pretty print a message letting the user know it is running and how many tokens (using tiktoken) are in the prompt and the cost.
|
|
1855
|
+
Step 10. Print the total cost of both runs and return 'update_unit_test', 'update_code', 'fixed_unit_test' and 'fixed_code' as individual values from the JSON output parser.
|
|
1856
|
+
```</example_input_prompt>
|
|
1857
|
+
<example_input_code>```
|
|
1858
|
+
import os
|
|
1859
|
+
from rich import print as rprint
|
|
1860
|
+
from rich.markdown import Markdown
|
|
1861
|
+
import tiktoken
|
|
1862
|
+
from langchain_core.prompts import PromptTemplate
|
|
1863
|
+
from langchain_openai import ChatOpenAI
|
|
1864
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
1865
|
+
from langchain_community.cache import SQLiteCache
|
|
1866
|
+
from langchain.globals import set_llm_cache
|
|
1867
|
+
from llm_selector import llm_selector
|
|
1868
|
+
from pydantic import BaseModel, Field
|
|
1869
|
+
from langchain_core.pydantic_v1 import BaseModel as LangchainBaseModel
|
|
1870
|
+
|
|
1871
|
+
# Setup cache to save money and increase speeds
|
|
1872
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
1873
|
+
|
|
1874
|
+
class FixResult(LangchainBaseModel):
|
|
1875
|
+
update_unit_test: bool = Field(description="Whether the unit test needs to be updated")
|
|
1876
|
+
update_code: bool = Field(description="Whether the code needs to be updated")
|
|
1877
|
+
fixed_unit_test: str = Field(description="The fixed unit test code")
|
|
1878
|
+
fixed_code: str = Field(description="The fixed code")
|
|
1879
|
+
|
|
1880
|
+
def fix_errors_from_unit_tests(unit_test, code, error, strength):
|
|
1881
|
+
# Step 1: Load the prompt files
|
|
1882
|
+
pdd_path = os.getenv('PDD_PATH')
|
|
1883
|
+
if not pdd_path:
|
|
1884
|
+
raise EnvironmentError("PDD_PATH environment variable is not set.")
|
|
1885
|
+
|
|
1886
|
+
with open(os.path.join(pdd_path, 'prompts', 'fix_errors_from_unit_tests_LLM.prompt'), 'r') as file:
|
|
1887
|
+
fix_errors_prompt = file.read()
|
|
1888
|
+
|
|
1889
|
+
with open(os.path.join(pdd_path, 'prompts', 'extract_unit_code_fix_LLM.prompt'), 'r') as file:
|
|
1890
|
+
extract_fix_prompt = file.read()
|
|
1891
|
+
|
|
1892
|
+
# Step 2: Create Langchain LCEL template from the fix_errors_from_unit_tests prompt
|
|
1893
|
+
fix_errors_template = PromptTemplate.from_template(fix_errors_prompt)
|
|
1894
|
+
|
|
1895
|
+
# Step 3: Use llm_selector and a temperature of 0 for the llm model
|
|
1896
|
+
llm, token_counter, input_cost, output_cost = llm_selector(strength, 0)
|
|
1897
|
+
|
|
1898
|
+
# Step 4: Run the code through the model using Langchain LCEL
|
|
1899
|
+
chain = fix_errors_template | llm | StrOutputParser()
|
|
1900
|
+
|
|
1901
|
+
# Prepare the input for the prompt
|
|
1902
|
+
prompt_input = {{
|
|
1903
|
+
"unit_test": unit_test,
|
|
1904
|
+
"code": code,
|
|
1905
|
+
"errors": error
|
|
1906
|
+
}}
|
|
1907
|
+
|
|
1908
|
+
# Calculate token count and cost
|
|
1909
|
+
encoding = tiktoken.get_encoding("cl100k_base")
|
|
1910
|
+
token_count = len(encoding.encode(str(prompt_input)))
|
|
1911
|
+
cost = (token_count / 1_000_000) * input_cost
|
|
1912
|
+
|
|
1913
|
+
rprint(f"[bold green]Running the model with {{token_count}} tokens. Estimated cost: ${{cost:.6f}}[/bold green]")
|
|
1914
|
+
|
|
1915
|
+
# Invoke the chain
|
|
1916
|
+
result = chain.invoke(prompt_input)
|
|
1917
|
+
|
|
1918
|
+
# Pretty print the result
|
|
1919
|
+
rprint(Markdown(result))
|
|
1920
|
+
|
|
1921
|
+
# Calculate result token count and cost
|
|
1922
|
+
result_token_count = len(encoding.encode(result))
|
|
1923
|
+
result_cost = (result_token_count / 1_000_000) * output_cost
|
|
1924
|
+
|
|
1925
|
+
rprint(f"[bold green]Result contains {{result_token_count}} tokens. Estimated cost: ${{result_cost:.6f}}[/bold green]")
|
|
1926
|
+
rprint(f"[bold green]Total cost: ${{cost + result_cost:.6f}}[/bold green]")
|
|
1927
|
+
|
|
1928
|
+
# Step 7: Create a second Langchain LCEL template from the extract_unit_code_fix prompt
|
|
1929
|
+
extract_fix_template = PromptTemplate.from_template(extract_fix_prompt)
|
|
1930
|
+
|
|
1931
|
+
# Step 8: Use llm_selector with a strength setting of 0.5 and a temperature of 0
|
|
1932
|
+
llm, token_counter, input_cost, output_cost = llm_selector(0.5, 0)
|
|
1933
|
+
|
|
1934
|
+
from langchain.output_parsers import PydanticOutputParser
|
|
1935
|
+
parser = PydanticOutputParser(pydantic_object=FixResult)
|
|
1936
|
+
|
|
1937
|
+
chain = extract_fix_template | llm | parser
|
|
1938
|
+
|
|
1939
|
+
# Prepare the input for the second prompt
|
|
1940
|
+
prompt_input = {{
|
|
1941
|
+
"unit_test_fix": result,
|
|
1942
|
+
"unit_test": unit_test,
|
|
1943
|
+
"code": code
|
|
1944
|
+
}}
|
|
1945
|
+
|
|
1946
|
+
# Calculate token count and cost for the second run
|
|
1947
|
+
token_count = len(encoding.encode(str(prompt_input)))
|
|
1948
|
+
cost = (token_count / 1_000_000) * input_cost
|
|
1949
|
+
|
|
1950
|
+
rprint(f"[bold green]Running the second model with {{token_count}} tokens. Estimated cost: ${{cost:.6f}}[/bold green]")
|
|
1951
|
+
|
|
1952
|
+
# Invoke the chain
|
|
1953
|
+
result = chain.invoke(prompt_input)
|
|
1954
|
+
|
|
1955
|
+
# Calculate result token count and cost for the second run
|
|
1956
|
+
result_token_count = len(encoding.encode(str(result.dict())))
|
|
1957
|
+
result_cost = (result_token_count / 1_000_000) * output_cost
|
|
1958
|
+
|
|
1959
|
+
rprint(f"[bold green]Result contains {{result_token_count}} tokens. Estimated cost: ${{result_cost:.6f}}[/bold green]")
|
|
1960
|
+
rprint(f"[bold green]Total cost of both runs: ${{cost + result_cost:.6f}}[/bold green]")
|
|
1961
|
+
|
|
1962
|
+
# Return the parsed result as separate values
|
|
1963
|
+
return result.update_unit_test, result.update_code, result.fixed_unit_test, result.fixed_code
|
|
1964
|
+
|
|
1965
|
+
# Example usage
|
|
1966
|
+
if __name__ == "__main__":
|
|
1967
|
+
unit_test = "def test_add():\n assert add(1, 2) == 3"
|
|
1968
|
+
code = "def add(a, b):\n return a + b"
|
|
1969
|
+
error = "NameError: name 'add' is not defined"
|
|
1970
|
+
strength = 0.8
|
|
1971
|
+
|
|
1972
|
+
updated_unit_test, updated_code, fixed_unit_test, fixed_code = fix_errors_from_unit_tests(unit_test, code, error, strength)
|
|
1973
|
+
print("Updated Unit Test:", updated_unit_test)
|
|
1974
|
+
print("Updated Code:", updated_code)
|
|
1975
|
+
print("Fixed Unit Test:", fixed_unit_test)
|
|
1976
|
+
print("Fixed Code:", fixed_code)
|
|
1977
|
+
```</example_input_code>
|
|
1978
|
+
<example_change_prompt>```
|
|
1979
|
+
Use token_counter from llm_selector instead of tiktoken to count tokens in the prompt. Also, fix_errors_from_unit_tests should take in and use temperature. This should also output the total cost of the LCEL runs.
|
|
1980
|
+
```</example_change_prompt>
|
|
1981
|
+
<example_modified_prompt>```
|
|
1982
|
+
% You are an expert Python Software Engineer. Your goal is to write a python function, "fix_errors_from_unit_tests", that will fix unit test errors in a code file. All output to the console will be pretty print using the Python rich library.
|
|
1983
|
+
|
|
1984
|
+
% Here are the inputs and outputs of the function:
|
|
1985
|
+
Inputs:
|
|
1986
|
+
'unit_test' - A string containing the unit test code.
|
|
1987
|
+
'code' - A string containing the code under test.
|
|
1988
|
+
'error' - A string that contains the errors that need to be fixed.
|
|
1989
|
+
'strength' - A float between 0 and 1 that is the strength of the LLM model to use.
|
|
1990
|
+
'temperature' - A float that controls the randomness of the LLM's output.
|
|
1991
|
+
Outputs:
|
|
1992
|
+
'update_unit_test': Boolean indicating whether the unit test needs to be updated.
|
|
1993
|
+
'update_code': Boolean indicating whether the code under test needs to be updated.
|
|
1994
|
+
'fixed_unit_test' - A string that is the fixed unit test.
|
|
1995
|
+
'fixed_code' - A string that is the fixed code under test.
|
|
1996
|
+
'total_cost' - A float representing the total cost of the LCEL runs.
|
|
1997
|
+
|
|
1998
|
+
% Here is an example of a Langchain LCEL program: ```
|
|
1999
|
+
import os
|
|
2000
|
+
from langchain_core.prompts import PromptTemplate
|
|
2001
|
+
from langchain_community.cache import SQLiteCache
|
|
2002
|
+
from langchain.globals import set_llm_cache
|
|
2003
|
+
from langchain_core.output_parsers import JsonOutputParser
|
|
2004
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
2005
|
+
from langchain.output_parsers import RetryOutputParser
|
|
2006
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
2007
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
2008
|
+
from langchain_core.runnables import RunnablePassthrough, ConfigurableField
|
|
2009
|
+
|
|
2010
|
+
from langchain_fireworks import Fireworks
|
|
2011
|
+
from langchain_anthropic import ChatAnthropic
|
|
2012
|
+
from langchain_openai import ChatOpenAI
|
|
2013
|
+
from langchain_openai import OpenAI
|
|
2014
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
2015
|
+
from langchain_groq import ChatGroq
|
|
2016
|
+
from langchain_together import Together
|
|
2017
|
+
|
|
2018
|
+
# Define a base output parser (e.g., PydanticOutputParser)
|
|
2019
|
+
from pydantic import BaseModel, Field
|
|
2020
|
+
|
|
2021
|
+
|
|
2022
|
+
# Setup cache to save money and increase speeds
|
|
2023
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
2024
|
+
|
|
2025
|
+
|
|
2026
|
+
# Create the LCEL template. Make note of the variable {{topic}} which will be filled in later.
|
|
2027
|
+
prompt_template = PromptTemplate.from_template("Tell me a joke about {{topic}}")
|
|
2028
|
+
|
|
2029
|
+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
|
2030
|
+
# Combine with a model and parser to output a string
|
|
2031
|
+
chain = prompt_template |llm| StrOutputParser()
|
|
2032
|
+
|
|
2033
|
+
# Run the template. Notice that the input is a dictionary with a single key "topic" which feeds it into the above prompt template. This is needed because the prompt template has a variable {{topic}} which needs to be filled in when invoked.
|
|
2034
|
+
result = chain.invoke({{"topic": "cats"}})
|
|
2035
|
+
print(result)
|
|
2036
|
+
|
|
2037
|
+
|
|
2038
|
+
# Define your desired data structure.
|
|
2039
|
+
class Joke(BaseModel):
|
|
2040
|
+
setup: str = Field(description="question to set up a joke")
|
|
2041
|
+
punchline: str = Field(description="answer to resolve the joke")
|
|
2042
|
+
|
|
2043
|
+
|
|
2044
|
+
llm = ChatGoogleGenerativeAI(model="gemini-pro", temperature=0)
|
|
2045
|
+
# Set up a parser
|
|
2046
|
+
parser = JsonOutputParser(pydantic_object=Joke)
|
|
2047
|
+
|
|
2048
|
+
# Create a prompt template
|
|
2049
|
+
prompt = PromptTemplate(
|
|
2050
|
+
template="Answer the user query.\n{{format_instructions}}\n{{query}}\n",
|
|
2051
|
+
input_variables=["query"],
|
|
2052
|
+
partial_variables={{"format_instructions": parser.get_format_instructions()}},
|
|
2053
|
+
)
|
|
2054
|
+
|
|
2055
|
+
# Chain the components
|
|
2056
|
+
chain = prompt | llm | parser
|
|
2057
|
+
|
|
2058
|
+
# Invoke the chain with a query
|
|
2059
|
+
result = chain.invoke({{"query": "Tell me a joke."}})
|
|
2060
|
+
print(result)
|
|
2061
|
+
|
|
2062
|
+
|
|
2063
|
+
# Get DEEKSEEK_API_KEY environmental variable
|
|
2064
|
+
deepseek_api_key = os.getenv('DEEKSEEK_API_KEY')
|
|
2065
|
+
|
|
2066
|
+
# Ensure the API key is retrieved successfully
|
|
2067
|
+
if deepseek_api_key is None:
|
|
2068
|
+
raise ValueError("DEEKSEEK_API_KEY environment variable is not set")
|
|
2069
|
+
|
|
2070
|
+
llm = ChatOpenAI(
|
|
2071
|
+
model='deepseek-chat',
|
|
2072
|
+
openai_api_key=deepseek_api_key,
|
|
2073
|
+
openai_api_base='https://api.deepseek.com',
|
|
2074
|
+
temperature=0
|
|
2075
|
+
)
|
|
2076
|
+
|
|
2077
|
+
# Chain the components
|
|
2078
|
+
chain = prompt | llm | parser
|
|
2079
|
+
|
|
2080
|
+
# Invoke the chain with a query
|
|
2081
|
+
result = chain.invoke({{"query": "Write joke about the sky"}})
|
|
2082
|
+
print("deepseek",result)
|
|
2083
|
+
|
|
2084
|
+
|
|
2085
|
+
llm = Fireworks(
|
|
2086
|
+
model="accounts/fireworks/models/mixtral-8x7b-instruct",
|
|
2087
|
+
temperature=0)
|
|
2088
|
+
# Chain the components
|
|
2089
|
+
chain = prompt | llm | parser
|
|
2090
|
+
|
|
2091
|
+
# Invoke the chain with a query
|
|
2092
|
+
result = chain.invoke({{"query": "Tell me a joke about the president"}})
|
|
2093
|
+
print("fireworks",result)
|
|
2094
|
+
|
|
2095
|
+
|
|
2096
|
+
|
|
2097
|
+
|
|
2098
|
+
|
|
2099
|
+
prompt = ChatPromptTemplate.from_template(
|
|
2100
|
+
"Tell me a short joke about {{topic}}"
|
|
2101
|
+
)
|
|
2102
|
+
chat_openai = ChatOpenAI(model="gpt-3.5-turbo")
|
|
2103
|
+
openai = OpenAI(model="gpt-3.5-turbo-instruct")
|
|
2104
|
+
anthropic = ChatAnthropic(model="claude-2")
|
|
2105
|
+
model = (
|
|
2106
|
+
chat_openai
|
|
2107
|
+
.with_fallbacks([anthropic])
|
|
2108
|
+
.configurable_alternatives(
|
|
2109
|
+
ConfigurableField(id="model"),
|
|
2110
|
+
default_key="chat_openai",
|
|
2111
|
+
openai=openai,
|
|
2112
|
+
anthropic=anthropic,
|
|
2113
|
+
)
|
|
2114
|
+
)
|
|
2115
|
+
|
|
2116
|
+
chain = (
|
|
2117
|
+
{{"topic": RunnablePassthrough()}}
|
|
2118
|
+
| prompt
|
|
2119
|
+
| model
|
|
2120
|
+
| StrOutputParser()
|
|
2121
|
+
)
|
|
2122
|
+
result = chain.invoke({{"topic": "Tell me a joke about the president"}})
|
|
2123
|
+
print("config alt:",result)
|
|
2124
|
+
|
|
2125
|
+
|
|
2126
|
+
|
|
2127
|
+
llm = ChatGroq(temperature=0, model_name="mixtral-8x7b-32768")
|
|
2128
|
+
system = "You are a helpful assistant."
|
|
2129
|
+
human = "{{text}}"
|
|
2130
|
+
prompt = ChatPromptTemplate.from_messages([("system", system), ("human", human)])
|
|
2131
|
+
|
|
2132
|
+
chain = prompt | llm | StrOutputParser()
|
|
2133
|
+
print(chain.invoke({{"text": "Explain the importance of low latency LLMs."}}))
|
|
2134
|
+
|
|
2135
|
+
|
|
2136
|
+
llm = Together(
|
|
2137
|
+
model="meta-llama/Llama-3-70b-chat-hf",
|
|
2138
|
+
)
|
|
2139
|
+
chain = prompt | llm | StrOutputParser()
|
|
2140
|
+
print(chain.invoke({{"text": "Explain the importance of together.ai."}}))
|
|
2141
|
+
|
|
2142
|
+
```
|
|
2143
|
+
|
|
2144
|
+
% Here is an example how to select the Langchain llm and count tokens: ```
|
|
2145
|
+
import os
|
|
2146
|
+
from llm_selector import llm_selector
|
|
2147
|
+
|
|
2148
|
+
def main() -> None:
|
|
2149
|
+
"""
|
|
2150
|
+
Main function to demonstrate the usage of the llm_selector function.
|
|
2151
|
+
Sets environment variables, defines parameters, and calls the function.
|
|
2152
|
+
"""
|
|
2153
|
+
# Set environment variables (for demonstration purposes)
|
|
2154
|
+
os.environ['PDD_MODEL_DEFAULT'] = 'gpt-4o-mini' # Default model
|
|
2155
|
+
|
|
2156
|
+
|
|
2157
|
+
# Define desired strength and temperature
|
|
2158
|
+
strength: float = .3 # Desired strength of the model (0.0 to 1.0)
|
|
2159
|
+
temperature: float = 0 # Temperature for the model (0.0 to 1.0)
|
|
2160
|
+
|
|
2161
|
+
try:
|
|
2162
|
+
# Call the llm_selector function
|
|
2163
|
+
llm, token_counter, input_cost, output_cost, model_name = llm_selector(strength, temperature)
|
|
2164
|
+
|
|
2165
|
+
# Output the selected model details
|
|
2166
|
+
print(f"Selected LLM: {{model_name}}")
|
|
2167
|
+
print(f"Input Cost: {{input_cost}}")
|
|
2168
|
+
print(f"Output Cost: {{output_cost}}")
|
|
2169
|
+
except Exception as e:
|
|
2170
|
+
print(f"An error occurred: {{e}}")
|
|
2171
|
+
|
|
2172
|
+
if __name__ == "__main__":
|
|
2173
|
+
main()
|
|
2174
|
+
```
|
|
2175
|
+
|
|
2176
|
+
% This program will use Langchain to do the following:
|
|
2177
|
+
Step 1. Use $PDD_PATH environment variable to get the path to the project. Load the '$PDD_PATH/prompts/fix_errors_from_unit_tests_LLM.prompt' file. Also load the 'extract_unit_code_fix_LLM.prompt' from the same directory.
|
|
2178
|
+
Step 2. Then this will create a Langchain LCEL template from the fix_errors_from_unit_tests prompt.
|
|
2179
|
+
Step 3. This will use llm_selector with the provided strength and temperature for the llm model.
|
|
2180
|
+
Step 4. This will run the code through the model using Langchain LCEL.
|
|
2181
|
+
4a. Be sure to pass the following string parameters to the prompt during invoke:
|
|
2182
|
+
- 'unit_test'
|
|
2183
|
+
- 'code'
|
|
2184
|
+
- 'errors'
|
|
2185
|
+
4b. Pretty print a message letting the user know it is running and how many tokens (using token_counter from llm_selector) are in the prompt and the cost. The cost from llm_selector is in dollars per million tokens.
|
|
2186
|
+
Step 5. This will pretty print the markdown formatting that is present in the result via the rich Markdown function. It will also pretty print the number of tokens in the result and the cost. Also, print out the cost of this run.
|
|
2187
|
+
Step 6. Then this will create a second Langchain LCEL template from the extract_unit_code_fix prompt.
|
|
2188
|
+
Step 7. This will use llm_selector with a strength setting of 0.5 and the provided temperature for the llm model. However, instead of using String output, it will use the JSON output parser to get these keys: 'update_unit_test', 'update_code', 'fixed_unit_test' and 'fixed_code'.
|
|
2189
|
+
Step 8. This will run the code through the model using Langchain LCEL from Step 7.
|
|
2190
|
+
8a. Be sure to pass the following string parameters to the prompt during invoke:
|
|
2191
|
+
- 'unit_test_fix': This is the result of the Langchain LCEL from Step 4.
|
|
2192
|
+
- 'unit_test'
|
|
2193
|
+
- 'code'
|
|
2194
|
+
8b. Pretty print a message letting the user know it is running and how many tokens (using token_counter from llm_selector) are in the prompt and the cost.
|
|
2195
|
+
Step 9. Calculate the total cost by summing the costs from both LCEL runs.
|
|
2196
|
+
Step 10. Print the total cost of both runs and return 'update_unit_test', 'update_code', 'fixed_unit_test', 'fixed_code', and 'total_cost' as individual values from the JSON output parser.
|
|
2197
|
+
|
|
2198
|
+
% Ensure that the function handles potential errors gracefully, such as missing input parameters or issues with the LLM model responses.
|
|
2199
|
+
```</example_modified_prompt>
|
|
2200
|
+
</example>
|
|
2201
|
+
<example>
|
|
2202
|
+
<example_number>9</example_number>
|
|
2203
|
+
<example_input_prompt>```
|
|
2204
|
+
% You are an expert Python Software Engineer. Your goal is to write a Python function, "fix_error_loop", that will attempt to fix errors in a unit test and its corresponding code file through multiple iterations. All output to the console will be pretty printed using the Python rich library.
|
|
2205
|
+
|
|
2206
|
+
% Here are the inputs and outputs of the function:
|
|
2207
|
+
Inputs:
|
|
2208
|
+
'unit_test_file' - A string containing the path to the unit test file.
|
|
2209
|
+
'code_file' - A string containing the path to the code file being tested.
|
|
2210
|
+
'verification_program' - A string containing the path to a Python program that verifies if the code still runs correctly.
|
|
2211
|
+
'strength' - A float between 0 and 1 that represents the strength of the LLM model to use.
|
|
2212
|
+
'temperature' - A float that represents the temperature parameter for the LLM model.
|
|
2213
|
+
'max_attempts' - An integer representing the maximum number of fix attempts before giving up.
|
|
2214
|
+
Outputs:
|
|
2215
|
+
'success' - A boolean indicating whether the errors were successfully fixed.
|
|
2216
|
+
'final_unit_test' - A string containing the contents of the final unit test file.
|
|
2217
|
+
'final_code' - A string containing the contents of the final code file.
|
|
2218
|
+
'total_attempts' - An integer representing the number of fix attempts made.
|
|
2219
|
+
|
|
2220
|
+
% Here is an example of the fix_errors_from_unit_tests function that will be used: ```
|
|
2221
|
+
from fix_errors_from_unit_tests import fix_errors_from_unit_tests
|
|
2222
|
+
|
|
2223
|
+
# Define the inputs for the function
|
|
2224
|
+
unit_test: str = """
|
|
2225
|
+
def test_addition():
|
|
2226
|
+
assert add(1, 1) == 3 # Intentional error
|
|
2227
|
+
"""
|
|
2228
|
+
|
|
2229
|
+
code: str = """
|
|
2230
|
+
def add(a, b):
|
|
2231
|
+
return a + b
|
|
2232
|
+
"""
|
|
2233
|
+
|
|
2234
|
+
error: str = "AssertionError: assert 2 == 3"
|
|
2235
|
+
error_file: str = "error_log.txt"
|
|
2236
|
+
strength: float = 0.7 # Strength parameter for LLM selection
|
|
2237
|
+
temperature: float = 0 # Temperature parameter for LLM selection
|
|
2238
|
+
|
|
2239
|
+
try:
|
|
2240
|
+
# Call the function to fix errors in the unit tests
|
|
2241
|
+
update_unit_test, update_code, fixed_unit_test, fixed_code, total_cost = fix_errors_from_unit_tests(
|
|
2242
|
+
unit_test=unit_test,
|
|
2243
|
+
code=code,
|
|
2244
|
+
error=error,
|
|
2245
|
+
error_file=error_file,
|
|
2246
|
+
strength=strength,
|
|
2247
|
+
temperature=temperature
|
|
2248
|
+
)
|
|
2249
|
+
|
|
2250
|
+
# Print the results
|
|
2251
|
+
print(f"Update Unit Test: {{update_unit_test}}")
|
|
2252
|
+
print(f"Update Code: {{update_code}}")
|
|
2253
|
+
print(f"Fixed Unit Test:\n{{fixed_unit_test}}")
|
|
2254
|
+
print(f"Fixed Code:\n{{fixed_code}}")
|
|
2255
|
+
print(f"Total Cost: ${{total_cost:.6f}}")
|
|
2256
|
+
except Exception as e:
|
|
2257
|
+
print(f"An error occurred: {{e}}")
|
|
2258
|
+
```
|
|
2259
|
+
|
|
2260
|
+
% This function will do the following:
|
|
2261
|
+
Step 1. Remove the existing error.log file if it exists.
|
|
2262
|
+
Step 2. Initialize a counter for the number of attempts.
|
|
2263
|
+
Step 3. Enter a while loop that continues until max_attempts is reached:
|
|
2264
|
+
a. Run the unit_test_file with 'python -m pytest -vv' and pipe all output to error.log.
|
|
2265
|
+
b. If the test passes, break the loop.
|
|
2266
|
+
c. If the test fails:
|
|
2267
|
+
- Read and print the error message from error.log.
|
|
2268
|
+
- Count the number of 'FAILED' and 'ERROR' from stdout (accounting for the -vv flag doubling these messages).
|
|
2269
|
+
- Create backup copies of the unit_test_file and code_file, appending the number of fails, errors, and the current iteration number to the filenames like this "unit_test_1_0_3.py" and "code_1_0_3.py", where there was one fail, zero errors and it is the third iteration through the loop.
|
|
2270
|
+
- Read the contents of the unit_test_file and code_file.
|
|
2271
|
+
- Call fix_errors_from_unit_tests with the file contents, error from error.log, and the provided strength.
|
|
2272
|
+
- If both updated_unit_test and updated_code are False, break the loop as no changes were needed.
|
|
2273
|
+
- If either updated_unit_test or updated_code is True:
|
|
2274
|
+
* Write the fixed_unit_test and fixed_code back to their respective files.
|
|
2275
|
+
* Run the verification_program to check if the code still runs.
|
|
2276
|
+
* If the verification fails, restore the original files from the backups and continue the loop.
|
|
2277
|
+
* If the verification succeeds, continue to the next iteration.
|
|
2278
|
+
d. Increment the attempt counter.
|
|
2279
|
+
Step 4. After the loop ends, run pytest one last time, pipe the output to error.log, and print it to the console.
|
|
2280
|
+
Step 5. Return the success status, final unit test contents, final code contents, and total number of attempts.
|
|
2281
|
+
|
|
2282
|
+
% Ensure that the function handles potential errors gracefully, such as file I/O errors or subprocess execution failures. Use the rich library for all console output to enhance readability. Consider using context managers for file operations to ensure proper resource management.
|
|
2283
|
+
|
|
2284
|
+
% Note: The temperature parameter should be incorporated into the LLM selection process. You may need to modify the fix_errors_from_unit_tests function or use it in conjunction with the llm_selector to properly utilize this parameter.
|
|
2285
|
+
```</example_input_prompt>
|
|
2286
|
+
<example_input_code>```
|
|
2287
|
+
import os
|
|
2288
|
+
import shutil
|
|
2289
|
+
import subprocess
|
|
2290
|
+
from rich import print
|
|
2291
|
+
from rich.console import Console
|
|
2292
|
+
from fix_errors_from_unit_tests import fix_errors_from_unit_tests
|
|
2293
|
+
|
|
2294
|
+
console = Console()
|
|
2295
|
+
|
|
2296
|
+
def fix_error_loop(unit_test_file: str, code_file: str, verification_program: str, strength: float, temperature: float, max_attempts: int) -> tuple:
|
|
2297
|
+
"""
|
|
2298
|
+
Iteratively attempts to fix errors in unit tests and code files.
|
|
2299
|
+
|
|
2300
|
+
Args:
|
|
2301
|
+
unit_test_file (str): Path to the unit test file.
|
|
2302
|
+
code_file (str): Path to the code file.
|
|
2303
|
+
verification_program (str): Path to the verification program.
|
|
2304
|
+
strength (float): Strength parameter for error fixing.
|
|
2305
|
+
temperature (float): Temperature parameter for error fixing.
|
|
2306
|
+
max_attempts (int): Maximum number of attempts to fix errors.
|
|
2307
|
+
|
|
2308
|
+
Returns:
|
|
2309
|
+
tuple: A tuple containing success status, final unit test contents,
|
|
2310
|
+
final code contents, and total number of attempts.
|
|
2311
|
+
"""
|
|
2312
|
+
# Step 1: Remove the existing error.log file if it exists
|
|
2313
|
+
error_log_path = "error.log"
|
|
2314
|
+
if os.path.exists(error_log_path):
|
|
2315
|
+
os.remove(error_log_path)
|
|
2316
|
+
|
|
2317
|
+
# Step 2: Initialize a counter for the number of attempts
|
|
2318
|
+
attempts = 0
|
|
2319
|
+
success = False
|
|
2320
|
+
|
|
2321
|
+
# Step 3: Enter a while loop that continues until max_attempts is reached
|
|
2322
|
+
while attempts < max_attempts:
|
|
2323
|
+
attempts += 1
|
|
2324
|
+
|
|
2325
|
+
# a. Run the unit_test_file with 'python -m pytest -vv' and pipe all output to error.log
|
|
2326
|
+
console.print(f"[bold blue]Running pytest for attempt {{attempts}}...[/bold blue]")
|
|
2327
|
+
with open(error_log_path, "w") as error_log:
|
|
2328
|
+
result = subprocess.run(
|
|
2329
|
+
["python", "-m", "pytest", unit_test_file, "-vv"],
|
|
2330
|
+
stdout=error_log,
|
|
2331
|
+
stderr=subprocess.STDOUT
|
|
2332
|
+
)
|
|
2333
|
+
|
|
2334
|
+
# b. If the test passes, break the loop
|
|
2335
|
+
if result.returncode == 0:
|
|
2336
|
+
console.print("[bold green]Tests passed![/bold green]")
|
|
2337
|
+
success = True
|
|
2338
|
+
break
|
|
2339
|
+
|
|
2340
|
+
# c. If the test fails:
|
|
2341
|
+
console.print("[bold red]Tests failed. Reading error log...[/bold red]")
|
|
2342
|
+
with open(error_log_path, "r") as error_log:
|
|
2343
|
+
error_message = error_log.read()
|
|
2344
|
+
console.print(error_message)
|
|
2345
|
+
|
|
2346
|
+
# Count the number of 'FAILED' and 'ERROR' from the error log
|
|
2347
|
+
fail_count = error_message.count("FAILED")
|
|
2348
|
+
error_count = error_message.count("ERROR")
|
|
2349
|
+
|
|
2350
|
+
# Create backup copies of the unit_test_file and code_file
|
|
2351
|
+
backup_unit_test = f"unit_test_{{fail_count}}_{{error_count}}_{{attempts}}.py"
|
|
2352
|
+
backup_code = f"code_{{fail_count}}_{{error_count}}_{{attempts}}.py"
|
|
2353
|
+
shutil.copy(unit_test_file, backup_unit_test)
|
|
2354
|
+
shutil.copy(code_file, backup_code)
|
|
2355
|
+
|
|
2356
|
+
# Read the contents of the unit_test_file and code_file
|
|
2357
|
+
with open(unit_test_file, "r") as f:
|
|
2358
|
+
unit_test_code = f.read()
|
|
2359
|
+
with open(code_file, "r") as f:
|
|
2360
|
+
code_under_test = f.read()
|
|
2361
|
+
|
|
2362
|
+
# Call fix_errors_from_unit_tests
|
|
2363
|
+
try:
|
|
2364
|
+
update_unit_test, update_code, fixed_unit_test, fixed_code, _ = fix_errors_from_unit_tests(
|
|
2365
|
+
unit_test=unit_test_code,
|
|
2366
|
+
code=code_under_test,
|
|
2367
|
+
error=error_message,
|
|
2368
|
+
strength=strength,
|
|
2369
|
+
temperature=temperature
|
|
2370
|
+
)
|
|
2371
|
+
except Exception as e:
|
|
2372
|
+
console.print(f"[bold red]Error during fix attempt: {{e}}[/bold red]")
|
|
2373
|
+
break
|
|
2374
|
+
|
|
2375
|
+
# If both updated_unit_test and updated_code are False, break the loop
|
|
2376
|
+
if not update_unit_test and not update_code:
|
|
2377
|
+
console.print("[bold yellow]No changes were needed. Exiting loop.[/bold yellow]")
|
|
2378
|
+
break
|
|
2379
|
+
|
|
2380
|
+
# If either updated_unit_test or updated_code is True, write the changes
|
|
2381
|
+
if update_unit_test:
|
|
2382
|
+
with open(unit_test_file, "w") as f:
|
|
2383
|
+
f.write(fixed_unit_test)
|
|
2384
|
+
if update_code:
|
|
2385
|
+
with open(code_file, "w") as f:
|
|
2386
|
+
f.write(fixed_code)
|
|
2387
|
+
|
|
2388
|
+
# Run the verification_program
|
|
2389
|
+
console.print("[bold blue]Running verification program...[/bold blue]")
|
|
2390
|
+
verification_result = subprocess.run(
|
|
2391
|
+
["python", verification_program],
|
|
2392
|
+
stdout=subprocess.PIPE,
|
|
2393
|
+
stderr=subprocess.PIPE
|
|
2394
|
+
)
|
|
2395
|
+
|
|
2396
|
+
if verification_result.returncode != 0:
|
|
2397
|
+
console.print("[bold red]Verification failed. Restoring backups...[/bold red]")
|
|
2398
|
+
shutil.copy(backup_unit_test, unit_test_file)
|
|
2399
|
+
shutil.copy(backup_code, code_file)
|
|
2400
|
+
else:
|
|
2401
|
+
console.print("[bold green]Verification succeeded.[/bold green]")
|
|
2402
|
+
|
|
2403
|
+
# Step 4: After the loop ends, run pytest one last time
|
|
2404
|
+
console.print("[bold blue]Final pytest run...[/bold blue]")
|
|
2405
|
+
with open(error_log_path, "w") as error_log:
|
|
2406
|
+
subprocess.run(
|
|
2407
|
+
["python", "-m", "pytest", unit_test_file, "-vv"],
|
|
2408
|
+
stdout=error_log,
|
|
2409
|
+
stderr=subprocess.STDOUT
|
|
2410
|
+
)
|
|
2411
|
+
with open(error_log_path, "r") as error_log:
|
|
2412
|
+
console.print(error_log.read())
|
|
2413
|
+
|
|
2414
|
+
# Step 5: Return the success status, final unit test contents, final code contents, and total number of attempts
|
|
2415
|
+
with open(unit_test_file, "r") as f:
|
|
2416
|
+
final_unit_test = f.read()
|
|
2417
|
+
with open(code_file, "r") as f:
|
|
2418
|
+
final_code = f.read()
|
|
2419
|
+
|
|
2420
|
+
return success, final_unit_test, final_code, attempts
|
|
2421
|
+
|
|
2422
|
+
```</example_input_code>
|
|
2423
|
+
<example_change_prompt>```
|
|
2424
|
+
% Update the prompt use the latest definition of fix_errors_from_unit_tests as shown in this example: ```from fix_errors_from_unit_tests import fix_errors_from_unit_tests
|
|
2425
|
+
|
|
2426
|
+
# Define the inputs
|
|
2427
|
+
unit_test_code = """
|
|
2428
|
+
def test_addition():
|
|
2429
|
+
assert add(1, 2) == 4 # Intentional error
|
|
2430
|
+
"""
|
|
2431
|
+
|
|
2432
|
+
code_under_test = """
|
|
2433
|
+
def add(a, b):
|
|
2434
|
+
return a + b
|
|
2435
|
+
"""
|
|
2436
|
+
|
|
2437
|
+
error_message = "AssertionError: assert 3 == 4"
|
|
2438
|
+
strength = 0.7 # Adjust the strength for LLM selection
|
|
2439
|
+
temperature = 0 # Adjust the temperature for LLM selection
|
|
2440
|
+
|
|
2441
|
+
try:
|
|
2442
|
+
# Call the function to fix errors
|
|
2443
|
+
update_unit_test, update_code, fixed_unit_test, fixed_code, total_cost = fix_errors_from_unit_tests(
|
|
2444
|
+
unit_test=unit_test_code,
|
|
2445
|
+
code=code_under_test,
|
|
2446
|
+
error=error_message,
|
|
2447
|
+
strength=strength,
|
|
2448
|
+
temperature=temperature
|
|
2449
|
+
)
|
|
2450
|
+
|
|
2451
|
+
# Output the results
|
|
2452
|
+
print(f"Update Unit Test: {{update_unit_test}}")
|
|
2453
|
+
print(f"Update Code: {{update_code}}")
|
|
2454
|
+
print(f"Fixed Unit Test:\n{{fixed_unit_test}}")
|
|
2455
|
+
print(f"Fixed Code:\n{{fixed_code}}")
|
|
2456
|
+
print(f"Total Cost: ${{total_cost:.6f}}")
|
|
2457
|
+
except Exception as e:
|
|
2458
|
+
print(f"An error occurred: {{e}}")```
|
|
2459
|
+
|
|
2460
|
+
% Also, output the total cost of all the runs and take in an 'budget' input to stop the iterations if the total cost exceeds the budget.
|
|
2461
|
+
|
|
2462
|
+
% Before finishing the function, copy back the iteration of fixed_unit_test and fixed_code that meets these criteria in priority order:
|
|
2463
|
+
1) Had the lowest number of 'ERROR's
|
|
2464
|
+
2) Had the lowest number of 'FAILED's
|
|
2465
|
+
This is so that the function saves the most successful iteration of the fixed code and unit test where error's are prioritized over fail's. Be sure to also consider the last run when decided which iteration to copy back. If the last run is the best, no need to copy back.
|
|
2466
|
+
```</example_change_prompt>
|
|
2467
|
+
<example_modified_prompt>```
|
|
2468
|
+
% You are an expert Python Software Engineer. Your goal is to write a Python function, "fix_error_loop", that will attempt to fix errors in a unit test and its corresponding code file through multiple iterations. All output to the console will be pretty printed using the Python rich library.
|
|
2469
|
+
|
|
2470
|
+
% Here are the inputs and outputs of the function:
|
|
2471
|
+
Inputs:
|
|
2472
|
+
'unit_test_file' - A string containing the path to the unit test file.
|
|
2473
|
+
'code_file' - A string containing the path to the code file being tested.
|
|
2474
|
+
'verification_program' - A string containing the path to a Python program that verifies if the code still runs correctly.
|
|
2475
|
+
'strength' - A float between 0 and 1 that represents the strength of the LLM model to use.
|
|
2476
|
+
'temperature' - A float that represents the temperature parameter for the LLM model.
|
|
2477
|
+
'max_attempts' - An integer representing the maximum number of fix attempts before giving up.
|
|
2478
|
+
'budget' - A float representing the maximum cost allowed for the fixing process.
|
|
2479
|
+
Outputs:
|
|
2480
|
+
'success' - A boolean indicating whether the errors were successfully fixed.
|
|
2481
|
+
'final_unit_test' - A string containing the contents of the final unit test file.
|
|
2482
|
+
'final_code' - A string containing the contents of the final code file.
|
|
2483
|
+
'total_attempts' - An integer representing the number of fix attempts made.
|
|
2484
|
+
'total_cost' - A float representing the total cost of all fix attempts.
|
|
2485
|
+
|
|
2486
|
+
% Here is an example of the fix_errors_from_unit_tests function that will be used: ```
|
|
2487
|
+
from fix_errors_from_unit_tests import fix_errors_from_unit_tests
|
|
2488
|
+
|
|
2489
|
+
# Define the inputs for the function
|
|
2490
|
+
unit_test: str = """
|
|
2491
|
+
def test_addition():
|
|
2492
|
+
assert add(1, 1) == 3 # Intentional error
|
|
2493
|
+
"""
|
|
2494
|
+
|
|
2495
|
+
code: str = """
|
|
2496
|
+
def add(a, b):
|
|
2497
|
+
return a + b
|
|
2498
|
+
"""
|
|
2499
|
+
|
|
2500
|
+
error: str = "AssertionError: assert 2 == 3"
|
|
2501
|
+
error_file: str = "error_log.txt"
|
|
2502
|
+
strength: float = 0.7 # Strength parameter for LLM selection
|
|
2503
|
+
temperature: float = 0 # Temperature parameter for LLM selection
|
|
2504
|
+
|
|
2505
|
+
try:
|
|
2506
|
+
# Call the function to fix errors in the unit tests
|
|
2507
|
+
update_unit_test, update_code, fixed_unit_test, fixed_code, total_cost = fix_errors_from_unit_tests(
|
|
2508
|
+
unit_test=unit_test,
|
|
2509
|
+
code=code,
|
|
2510
|
+
error=error,
|
|
2511
|
+
error_file=error_file,
|
|
2512
|
+
strength=strength,
|
|
2513
|
+
temperature=temperature
|
|
2514
|
+
)
|
|
2515
|
+
|
|
2516
|
+
# Print the results
|
|
2517
|
+
print(f"Update Unit Test: {{update_unit_test}}")
|
|
2518
|
+
print(f"Update Code: {{update_code}}")
|
|
2519
|
+
print(f"Fixed Unit Test:\n{{fixed_unit_test}}")
|
|
2520
|
+
print(f"Fixed Code:\n{{fixed_code}}")
|
|
2521
|
+
print(f"Total Cost: ${{total_cost:.6f}}")
|
|
2522
|
+
except Exception as e:
|
|
2523
|
+
print(f"An error occurred: {{e}}")
|
|
2524
|
+
```
|
|
2525
|
+
|
|
2526
|
+
% This function will do the following:
|
|
2527
|
+
Step 1. Remove the existing error.log file if it exists.
|
|
2528
|
+
Step 2. Initialize variables:
|
|
2529
|
+
- Counter for the number of attempts
|
|
2530
|
+
- Total cost accumulator
|
|
2531
|
+
- Best iteration tracker (lowest errors, then lowest fails)
|
|
2532
|
+
Step 3. Enter a while loop that continues until max_attempts is reached or budget is exceeded:
|
|
2533
|
+
a. Run the unit_test_file with 'python -m pytest -vv' and pipe all output to error.log.
|
|
2534
|
+
b. If the test passes, break the loop.
|
|
2535
|
+
c. If the test fails:
|
|
2536
|
+
- Read and print the error message from error.log.
|
|
2537
|
+
- Count the number of 'FAILED' and 'ERROR' from stdout (accounting for the -vv flag doubling these messages).
|
|
2538
|
+
- Create backup copies of the unit_test_file and code_file, appending the number of fails, errors, and the current iteration number to the filenames like this "unit_test_1_0_3.py" and "code_1_0_3.py", where there was one fail, zero errors and it is the third iteration through the loop.
|
|
2539
|
+
- Read the contents of the unit_test_file and code_file.
|
|
2540
|
+
- Call fix_errors_from_unit_tests with the file contents, error from error.log, and the provided strength and temperature.
|
|
2541
|
+
- Add the returned total_cost to the total cost accumulator.
|
|
2542
|
+
- If the total cost exceeds the budget, break the loop.
|
|
2543
|
+
- If both updated_unit_test and updated_code are False, break the loop as no changes were needed.
|
|
2544
|
+
- If either updated_unit_test or updated_code is True:
|
|
2545
|
+
* Write the fixed_unit_test and fixed_code back to their respective files.
|
|
2546
|
+
* Run the verification_program to check if the code still runs.
|
|
2547
|
+
* If the verification fails, restore the original files from the backups and continue the loop.
|
|
2548
|
+
* If the verification succeeds, update the best iteration tracker if this iteration has fewer errors or fails.
|
|
2549
|
+
d. Increment the attempt counter.
|
|
2550
|
+
Step 4. After the loop ends, run pytest one last time, pipe the output to error.log, and print it to the console.
|
|
2551
|
+
Step 5. If the last run isn't the best iteration, copy back the files from the best iteration.
|
|
2552
|
+
Step 6. Return the success status, final unit test contents, final code contents, total number of attempts, and total cost.
|
|
2553
|
+
|
|
2554
|
+
% Ensure that the function handles potential errors gracefully, such as file I/O errors or subprocess execution failures. Use the rich library for all console output to enhance readability. Consider using context managers for file operations to ensure proper resource management.
|
|
2555
|
+
```</example_modified_prompt>
|
|
2556
|
+
</example>
|
|
2557
|
+
<example>
|
|
2558
|
+
<example_number>10</example_number>
|
|
2559
|
+
<example_input_prompt>```
|
|
2560
|
+
% You are an expert Python Software Engineer. Your goal is to write a python function, "fix_errors_from_unit_tests", that will fix unit test errors in a code file. All output to the console will be pretty print using the Python rich library.
|
|
2561
|
+
|
|
2562
|
+
% Here are the inputs and outputs of the function:
|
|
2563
|
+
Inputs:
|
|
2564
|
+
'unit_test' - A string containing the unit test code.
|
|
2565
|
+
'code' - A string containing the code under test.
|
|
2566
|
+
'error' - A string that contains the errors that need to be fixed.
|
|
2567
|
+
'strength' - A float between 0 and 1 that is the strength of the LLM model to use.
|
|
2568
|
+
'temperature' - A float that controls the randomness of the LLM's output.
|
|
2569
|
+
Outputs:
|
|
2570
|
+
'update_unit_test': Boolean indicating whether the unit test needs to be updated.
|
|
2571
|
+
'update_code': Boolean indicating whether the code under test needs to be updated.
|
|
2572
|
+
'fixed_unit_test' - A string that is the fixed unit test.
|
|
2573
|
+
'fixed_code' - A string that is the fixed code under test.
|
|
2574
|
+
'total_cost' - A float representing the total cost of the LCEL runs.
|
|
2575
|
+
|
|
2576
|
+
% Here is an example of a Langchain LCEL program: ```
|
|
2577
|
+
import os
|
|
2578
|
+
from langchain_core.prompts import PromptTemplate
|
|
2579
|
+
from langchain_community.cache import SQLiteCache
|
|
2580
|
+
from langchain.globals import set_llm_cache
|
|
2581
|
+
from langchain_core.output_parsers import JsonOutputParser
|
|
2582
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
2583
|
+
from langchain.output_parsers import RetryOutputParser
|
|
2584
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
2585
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
2586
|
+
from langchain_core.runnables import RunnablePassthrough, ConfigurableField
|
|
2587
|
+
|
|
2588
|
+
from langchain_fireworks import Fireworks
|
|
2589
|
+
from langchain_anthropic import ChatAnthropic
|
|
2590
|
+
from langchain_openai import ChatOpenAI
|
|
2591
|
+
from langchain_openai import OpenAI
|
|
2592
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
2593
|
+
from langchain_groq import ChatGroq
|
|
2594
|
+
from langchain_together import Together
|
|
2595
|
+
|
|
2596
|
+
# Define a base output parser (e.g., PydanticOutputParser)
|
|
2597
|
+
from pydantic import BaseModel, Field
|
|
2598
|
+
|
|
2599
|
+
|
|
2600
|
+
# Setup cache to save money and increase speeds
|
|
2601
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
2602
|
+
|
|
2603
|
+
|
|
2604
|
+
# Create the LCEL template. Make note of the variable {{topic}} which will be filled in later.
|
|
2605
|
+
prompt_template = PromptTemplate.from_template("Tell me a joke about {{topic}}")
|
|
2606
|
+
|
|
2607
|
+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
|
2608
|
+
# Combine with a model and parser to output a string
|
|
2609
|
+
chain = prompt_template |llm| StrOutputParser()
|
|
2610
|
+
|
|
2611
|
+
# Run the template. Notice that the input is a dictionary with a single key "topic" which feeds it into the above prompt template. This is needed because the prompt template has a variable {{topic}} which needs to be filled in when invoked.
|
|
2612
|
+
result = chain.invoke({{"topic": "cats"}})
|
|
2613
|
+
print(result)
|
|
2614
|
+
|
|
2615
|
+
|
|
2616
|
+
# Define your desired data structure.
|
|
2617
|
+
class Joke(BaseModel):
|
|
2618
|
+
setup: str = Field(description="question to set up a joke")
|
|
2619
|
+
punchline: str = Field(description="answer to resolve the joke")
|
|
2620
|
+
|
|
2621
|
+
|
|
2622
|
+
llm = ChatGoogleGenerativeAI(model="gemini-pro", temperature=0)
|
|
2623
|
+
# Set up a parser
|
|
2624
|
+
parser = JsonOutputParser(pydantic_object=Joke)
|
|
2625
|
+
|
|
2626
|
+
# Create a prompt template
|
|
2627
|
+
prompt = PromptTemplate(
|
|
2628
|
+
template="Answer the user query.\n{{format_instructions}}\n{{query}}\n",
|
|
2629
|
+
input_variables=["query"],
|
|
2630
|
+
partial_variables={{"format_instructions": parser.get_format_instructions()}},
|
|
2631
|
+
)
|
|
2632
|
+
|
|
2633
|
+
# Chain the components
|
|
2634
|
+
chain = prompt | llm | parser
|
|
2635
|
+
|
|
2636
|
+
# Invoke the chain with a query
|
|
2637
|
+
result = chain.invoke({{"query": "Tell me a joke."}})
|
|
2638
|
+
print(result)
|
|
2639
|
+
|
|
2640
|
+
|
|
2641
|
+
# Get DEEKSEEK_API_KEY environmental variable
|
|
2642
|
+
deepseek_api_key = os.getenv('DEEKSEEK_API_KEY')
|
|
2643
|
+
|
|
2644
|
+
# Ensure the API key is retrieved successfully
|
|
2645
|
+
if deepseek_api_key is None:
|
|
2646
|
+
raise ValueError("DEEKSEEK_API_KEY environment variable is not set")
|
|
2647
|
+
|
|
2648
|
+
llm = ChatOpenAI(
|
|
2649
|
+
model='deepseek-chat',
|
|
2650
|
+
openai_api_key=deepseek_api_key,
|
|
2651
|
+
openai_api_base='https://api.deepseek.com',
|
|
2652
|
+
temperature=0
|
|
2653
|
+
)
|
|
2654
|
+
|
|
2655
|
+
# Chain the components
|
|
2656
|
+
chain = prompt | llm | parser
|
|
2657
|
+
|
|
2658
|
+
# Invoke the chain with a query
|
|
2659
|
+
result = chain.invoke({{"query": "Write joke about the sky"}})
|
|
2660
|
+
print("deepseek",result)
|
|
2661
|
+
|
|
2662
|
+
|
|
2663
|
+
llm = Fireworks(
|
|
2664
|
+
model="accounts/fireworks/models/mixtral-8x7b-instruct",
|
|
2665
|
+
temperature=0)
|
|
2666
|
+
# Chain the components
|
|
2667
|
+
chain = prompt | llm | parser
|
|
2668
|
+
|
|
2669
|
+
# Invoke the chain with a query
|
|
2670
|
+
result = chain.invoke({{"query": "Tell me a joke about the president"}})
|
|
2671
|
+
print("fireworks",result)
|
|
2672
|
+
|
|
2673
|
+
|
|
2674
|
+
|
|
2675
|
+
|
|
2676
|
+
|
|
2677
|
+
prompt = ChatPromptTemplate.from_template(
|
|
2678
|
+
"Tell me a short joke about {{topic}}"
|
|
2679
|
+
)
|
|
2680
|
+
chat_openai = ChatOpenAI(model="gpt-3.5-turbo")
|
|
2681
|
+
openai = OpenAI(model="gpt-3.5-turbo-instruct")
|
|
2682
|
+
anthropic = ChatAnthropic(model="claude-2")
|
|
2683
|
+
model = (
|
|
2684
|
+
chat_openai
|
|
2685
|
+
.with_fallbacks([anthropic])
|
|
2686
|
+
.configurable_alternatives(
|
|
2687
|
+
ConfigurableField(id="model"),
|
|
2688
|
+
default_key="chat_openai",
|
|
2689
|
+
openai=openai,
|
|
2690
|
+
anthropic=anthropic,
|
|
2691
|
+
)
|
|
2692
|
+
)
|
|
2693
|
+
|
|
2694
|
+
chain = (
|
|
2695
|
+
{{"topic": RunnablePassthrough()}}
|
|
2696
|
+
| prompt
|
|
2697
|
+
| model
|
|
2698
|
+
| StrOutputParser()
|
|
2699
|
+
)
|
|
2700
|
+
result = chain.invoke({{"topic": "Tell me a joke about the president"}})
|
|
2701
|
+
print("config alt:",result)
|
|
2702
|
+
|
|
2703
|
+
|
|
2704
|
+
|
|
2705
|
+
llm = ChatGroq(temperature=0, model_name="mixtral-8x7b-32768")
|
|
2706
|
+
system = "You are a helpful assistant."
|
|
2707
|
+
human = "{{text}}"
|
|
2708
|
+
prompt = ChatPromptTemplate.from_messages([("system", system), ("human", human)])
|
|
2709
|
+
|
|
2710
|
+
chain = prompt | llm | StrOutputParser()
|
|
2711
|
+
print(chain.invoke({{"text": "Explain the importance of low latency LLMs."}}))
|
|
2712
|
+
|
|
2713
|
+
|
|
2714
|
+
llm = Together(
|
|
2715
|
+
model="meta-llama/Llama-3-70b-chat-hf",
|
|
2716
|
+
)
|
|
2717
|
+
chain = prompt | llm | StrOutputParser()
|
|
2718
|
+
print(chain.invoke({{"text": "Explain the importance of together.ai."}}))
|
|
2719
|
+
|
|
2720
|
+
```
|
|
2721
|
+
|
|
2722
|
+
% Here is an example how to select the Langchain llm and count tokens: ```
|
|
2723
|
+
import os
|
|
2724
|
+
from llm_selector import llm_selector
|
|
2725
|
+
|
|
2726
|
+
def main() -> None:
|
|
2727
|
+
"""
|
|
2728
|
+
Main function to demonstrate the usage of the llm_selector function.
|
|
2729
|
+
Sets environment variables, defines parameters, and calls the function.
|
|
2730
|
+
"""
|
|
2731
|
+
# Set environment variables (for demonstration purposes)
|
|
2732
|
+
os.environ['PDD_MODEL_DEFAULT'] = 'gpt-4o-mini' # Default model
|
|
2733
|
+
|
|
2734
|
+
|
|
2735
|
+
# Define desired strength and temperature
|
|
2736
|
+
strength: float = .3 # Desired strength of the model (0.0 to 1.0)
|
|
2737
|
+
temperature: float = 0 # Temperature for the model (0.0 to 1.0)
|
|
2738
|
+
|
|
2739
|
+
try:
|
|
2740
|
+
# Call the llm_selector function
|
|
2741
|
+
llm, token_counter, input_cost, output_cost, model_name = llm_selector(strength, temperature)
|
|
2742
|
+
|
|
2743
|
+
# Output the selected model details
|
|
2744
|
+
print(f"Selected LLM: {{model_name}}")
|
|
2745
|
+
print(f"Input Cost: {{input_cost}}")
|
|
2746
|
+
print(f"Output Cost: {{output_cost}}")
|
|
2747
|
+
except Exception as e:
|
|
2748
|
+
print(f"An error occurred: {{e}}")
|
|
2749
|
+
|
|
2750
|
+
if __name__ == "__main__":
|
|
2751
|
+
main()
|
|
2752
|
+
```
|
|
2753
|
+
|
|
2754
|
+
% This program will use Langchain to do the following:
|
|
2755
|
+
Step 1. Use $PDD_PATH environment variable to get the path to the project. Load the '$PDD_PATH/prompts/fix_errors_from_unit_tests_LLM.prompt' file. Also load the 'extract_unit_code_fix_LLM.prompt' from the same directory.
|
|
2756
|
+
Step 2. Then this will create a Langchain LCEL template from the fix_errors_from_unit_tests prompt.
|
|
2757
|
+
Step 3. This will use llm_selector with the provided strength and temperature for the llm model.
|
|
2758
|
+
Step 4. This will run the code through the model using Langchain LCEL.
|
|
2759
|
+
4a. Be sure to pass the following string parameters to the prompt during invoke:
|
|
2760
|
+
- 'unit_test'
|
|
2761
|
+
- 'code'
|
|
2762
|
+
- 'errors'
|
|
2763
|
+
4b. Pretty print a message letting the user know it is running and how many tokens (using token_counter from llm_selector) are in the prompt and the cost. The cost from llm_selector is in dollars per million tokens.
|
|
2764
|
+
Step 5. This will pretty print the markdown formatting that is present in the result via the rich Markdown function. It will also pretty print the number of tokens in the result and the cost. Also, print out the cost of this run.
|
|
2765
|
+
Step 6. Then this will create a second Langchain LCEL template from the extract_unit_code_fix prompt.
|
|
2766
|
+
Step 7. This will use llm_selector with a strength setting of 0.5 and the provided temperature for the llm model. However, instead of using String output, it will use the JSON output parser to use the 'get' function to extract the value of these keys: 'update_unit_test', 'update_code', 'fixed_unit_test' and 'fixed_code'.
|
|
2767
|
+
Step 8. This will run the code through the model using Langchain LCEL from Step 7.
|
|
2768
|
+
8a. Be sure to pass the following string parameters to the prompt during invoke:
|
|
2769
|
+
- 'unit_test_fix': This is the result of the Langchain LCEL from Step 4.
|
|
2770
|
+
- 'unit_test'
|
|
2771
|
+
- 'code'
|
|
2772
|
+
8b. Pretty print a message letting the user know it is running and how many tokens (using token_counter from llm_selector) are in the prompt and the cost.
|
|
2773
|
+
Step 9. Calculate the total cost by summing the costs from both LCEL runs.
|
|
2774
|
+
Step 10. Print the total cost of both runs and return 'update_unit_test', 'update_code', 'fixed_unit_test', 'fixed_code', and 'total_cost' as individual values from the JSON output parser.
|
|
2775
|
+
|
|
2776
|
+
% Ensure that the function handles potential errors gracefully, such as missing input parameters or issues with the LLM model responses.
|
|
2777
|
+
```</example_input_prompt>
|
|
2778
|
+
<example_input_code>```
|
|
2779
|
+
import os
|
|
2780
|
+
from rich import print as rprint
|
|
2781
|
+
from rich.markdown import Markdown
|
|
2782
|
+
from langchain_core.prompts import PromptTemplate
|
|
2783
|
+
from langchain_core.output_parsers import JsonOutputParser, StrOutputParser
|
|
2784
|
+
from langchain_community.cache import SQLiteCache
|
|
2785
|
+
from langchain.globals import set_llm_cache
|
|
2786
|
+
from llm_selector import llm_selector
|
|
2787
|
+
|
|
2788
|
+
# Setup cache to save money and increase speeds
|
|
2789
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
2790
|
+
|
|
2791
|
+
def fix_errors_from_unit_tests(unit_test: str, code: str, error: str, strength: float, temperature: float):
|
|
2792
|
+
"""
|
|
2793
|
+
Fix errors in unit tests using LLM models.
|
|
2794
|
+
|
|
2795
|
+
:param unit_test: The unit test code as a string.
|
|
2796
|
+
:param code: The code to be tested as a string.
|
|
2797
|
+
:param error: The error message from the unit test.
|
|
2798
|
+
:param strength: The strength parameter for LLM selection.
|
|
2799
|
+
:param temperature: The temperature parameter for LLM selection.
|
|
2800
|
+
:return: A tuple containing flags for updates and the fixed code and unit test.
|
|
2801
|
+
"""
|
|
2802
|
+
try:
|
|
2803
|
+
# Step 1: Load prompt files
|
|
2804
|
+
pdd_path = os.getenv('PDD_PATH')
|
|
2805
|
+
if not pdd_path:
|
|
2806
|
+
raise ValueError("PDD_PATH environment variable is not set")
|
|
2807
|
+
|
|
2808
|
+
with open(os.path.join(pdd_path, 'prompts', 'fix_errors_from_unit_tests_LLM.prompt'), 'r') as file:
|
|
2809
|
+
fix_errors_prompt = file.read()
|
|
2810
|
+
|
|
2811
|
+
with open(os.path.join(pdd_path, 'prompts', 'extract_unit_code_fix_LLM.prompt'), 'r') as file:
|
|
2812
|
+
extract_fix_prompt = file.read()
|
|
2813
|
+
|
|
2814
|
+
# Step 2: Create Langchain LCEL template for fix_errors_from_unit_tests
|
|
2815
|
+
fix_errors_template = PromptTemplate.from_template(fix_errors_prompt)
|
|
2816
|
+
|
|
2817
|
+
# Step 3: Use llm_selector with provided strength and temperature
|
|
2818
|
+
llm, token_counter, input_cost, output_cost = llm_selector(strength, temperature)
|
|
2819
|
+
|
|
2820
|
+
# Step 4: Run the code through the model using Langchain LCEL
|
|
2821
|
+
chain = fix_errors_template | llm | StrOutputParser()
|
|
2822
|
+
input_data = {{"unit_test": unit_test, "code": code, "errors": error}}
|
|
2823
|
+
prompt_tokens = token_counter(str(input_data))
|
|
2824
|
+
cost_run_1 = (prompt_tokens / 1_000_000) * input_cost
|
|
2825
|
+
|
|
2826
|
+
# 4a: Pretty print running message
|
|
2827
|
+
rprint(f"[bold green]Running fix_errors_from_unit_tests...[/bold green]")
|
|
2828
|
+
rprint(f"Prompt tokens: {{prompt_tokens}}, Cost: ${{cost_run_1:.6f}}")
|
|
2829
|
+
|
|
2830
|
+
# Invoke the chain
|
|
2831
|
+
result_1 = chain.invoke(input_data)
|
|
2832
|
+
|
|
2833
|
+
# Step 5: Pretty print the markdown formatting and cost
|
|
2834
|
+
rprint(Markdown(result_1))
|
|
2835
|
+
result_tokens = token_counter(result_1)
|
|
2836
|
+
cost_result_1 = (result_tokens / 1_000_000) * output_cost
|
|
2837
|
+
rprint(f"Result tokens: {{result_tokens}}, Cost: ${{cost_result_1:.6f}}")
|
|
2838
|
+
|
|
2839
|
+
# Step 6: Create a second Langchain LCEL template for extract_unit_code_fix
|
|
2840
|
+
extract_fix_template = PromptTemplate.from_template(extract_fix_prompt)
|
|
2841
|
+
|
|
2842
|
+
# Step 7: Use llm_selector with strength 0.5 and provided temperature
|
|
2843
|
+
llm, token_counter, input_cost, output_cost = llm_selector(0.5, temperature)
|
|
2844
|
+
parser = JsonOutputParser()
|
|
2845
|
+
|
|
2846
|
+
# Step 8: Run the code through the model using Langchain LCEL
|
|
2847
|
+
chain = extract_fix_template | llm | parser
|
|
2848
|
+
input_data_2 = {{
|
|
2849
|
+
"unit_test_fix": result_1,
|
|
2850
|
+
"unit_test": unit_test,
|
|
2851
|
+
"code": code
|
|
2852
|
+
}}
|
|
2853
|
+
prompt_tokens_2 = token_counter(str(input_data_2))
|
|
2854
|
+
cost_run_2 = (prompt_tokens_2 / 1_000_000) * input_cost
|
|
2855
|
+
|
|
2856
|
+
# 8a: Pretty print running message
|
|
2857
|
+
rprint(f"[bold green]Running extract_unit_code_fix...[/bold green]")
|
|
2858
|
+
rprint(f"Prompt tokens: {{prompt_tokens_2}}, Cost: ${{cost_run_2:.6f}}")
|
|
2859
|
+
|
|
2860
|
+
# Invoke the chain
|
|
2861
|
+
result_2 = chain.invoke(input_data_2)
|
|
2862
|
+
|
|
2863
|
+
# Step 9: Calculate the total cost
|
|
2864
|
+
total_cost = cost_run_1 + cost_result_1 + cost_run_2
|
|
2865
|
+
|
|
2866
|
+
# Step 10: Print the total cost and return results
|
|
2867
|
+
rprint(f"Total cost of both runs: ${{total_cost:.6f}}")
|
|
2868
|
+
|
|
2869
|
+
return (
|
|
2870
|
+
result_2.get('update_unit_test', False),
|
|
2871
|
+
result_2.get('update_code', False),
|
|
2872
|
+
result_2.get('fixed_unit_test', ''),
|
|
2873
|
+
result_2.get('fixed_code', ''),
|
|
2874
|
+
total_cost
|
|
2875
|
+
)
|
|
2876
|
+
|
|
2877
|
+
except Exception as e:
|
|
2878
|
+
rprint(f"[bold red]An error occurred: {{e}}[/bold red]")
|
|
2879
|
+
return False, False, '', '', 0.0
|
|
2880
|
+
|
|
2881
|
+
```</example_input_code>
|
|
2882
|
+
<example_change_prompt>```
|
|
2883
|
+
Add error_file to the inputs of the fix_errors_from_unit_tests function. This file will be used appended with the output of the first LCEL run that is also printed out. Have a separator for the output of the LCEL from what is in the error file already so it is easy to know what part of the log came from what function.
|
|
2884
|
+
```</example_change_prompt>
|
|
2885
|
+
<example_modified_prompt>```
|
|
2886
|
+
% You are an expert Python Software Engineer. Your goal is to write a python function, "fix_errors_from_unit_tests", that will fix unit test errors in a code file and log the process. All output to the console will be pretty printed using the Python rich library.
|
|
2887
|
+
|
|
2888
|
+
% Here are the inputs and outputs of the function:
|
|
2889
|
+
Inputs:
|
|
2890
|
+
'unit_test' - A string containing the unit test code.
|
|
2891
|
+
'code' - A string containing the code under test.
|
|
2892
|
+
'error' - A string that contains the errors that need to be fixed.
|
|
2893
|
+
'error_file' - A string containing the path to the file where error logs will be appended.
|
|
2894
|
+
'strength' - A float between 0 and 1 that is the strength of the LLM model to use.
|
|
2895
|
+
'temperature' - A float that controls the randomness of the LLM's output.
|
|
2896
|
+
Outputs:
|
|
2897
|
+
'update_unit_test': Boolean indicating whether the unit test needs to be updated.
|
|
2898
|
+
'update_code': Boolean indicating whether the code under test needs to be updated.
|
|
2899
|
+
'fixed_unit_test' - A string that is the fixed unit test.
|
|
2900
|
+
'fixed_code' - A string that is the fixed code under test.
|
|
2901
|
+
'total_cost' - A float representing the total cost of the LCEL runs.
|
|
2902
|
+
|
|
2903
|
+
% Here is an example of a Langchain LCEL program: ```
|
|
2904
|
+
import os
|
|
2905
|
+
from langchain_core.prompts import PromptTemplate
|
|
2906
|
+
from langchain_community.cache import SQLiteCache
|
|
2907
|
+
from langchain.globals import set_llm_cache
|
|
2908
|
+
from langchain_core.output_parsers import JsonOutputParser
|
|
2909
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
2910
|
+
from langchain.output_parsers import RetryOutputParser
|
|
2911
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
2912
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
2913
|
+
from langchain_core.runnables import RunnablePassthrough, ConfigurableField
|
|
2914
|
+
|
|
2915
|
+
from langchain_fireworks import Fireworks
|
|
2916
|
+
from langchain_anthropic import ChatAnthropic
|
|
2917
|
+
from langchain_openai import ChatOpenAI
|
|
2918
|
+
from langchain_openai import OpenAI
|
|
2919
|
+
from langchain_google_genai import ChatGoogleGenerativeAI
|
|
2920
|
+
from langchain_groq import ChatGroq
|
|
2921
|
+
from langchain_together import Together
|
|
2922
|
+
|
|
2923
|
+
# Define a base output parser (e.g., PydanticOutputParser)
|
|
2924
|
+
from pydantic import BaseModel, Field
|
|
2925
|
+
|
|
2926
|
+
|
|
2927
|
+
# Setup cache to save money and increase speeds
|
|
2928
|
+
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
|
|
2929
|
+
|
|
2930
|
+
|
|
2931
|
+
# Create the LCEL template. Make note of the variable {{topic}} which will be filled in later.
|
|
2932
|
+
prompt_template = PromptTemplate.from_template("Tell me a joke about {{topic}}")
|
|
2933
|
+
|
|
2934
|
+
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
|
|
2935
|
+
# Combine with a model and parser to output a string
|
|
2936
|
+
chain = prompt_template |llm| StrOutputParser()
|
|
2937
|
+
|
|
2938
|
+
# Run the template. Notice that the input is a dictionary with a single key "topic" which feeds it into the above prompt template. This is needed because the prompt template has a variable {{topic}} which needs to be filled in when invoked.
|
|
2939
|
+
result = chain.invoke({{"topic": "cats"}})
|
|
2940
|
+
print(result)
|
|
2941
|
+
|
|
2942
|
+
|
|
2943
|
+
# Define your desired data structure.
|
|
2944
|
+
class Joke(BaseModel):
|
|
2945
|
+
setup: str = Field(description="question to set up a joke")
|
|
2946
|
+
punchline: str = Field(description="answer to resolve the joke")
|
|
2947
|
+
|
|
2948
|
+
|
|
2949
|
+
llm = ChatGoogleGenerativeAI(model="gemini-pro", temperature=0)
|
|
2950
|
+
# Set up a parser
|
|
2951
|
+
parser = JsonOutputParser(pydantic_object=Joke)
|
|
2952
|
+
|
|
2953
|
+
# Create a prompt template
|
|
2954
|
+
prompt = PromptTemplate(
|
|
2955
|
+
template="Answer the user query.\n{{format_instructions}}\n{{query}}\n",
|
|
2956
|
+
input_variables=["query"],
|
|
2957
|
+
partial_variables={{"format_instructions": parser.get_format_instructions()}},
|
|
2958
|
+
)
|
|
2959
|
+
|
|
2960
|
+
# Chain the components
|
|
2961
|
+
chain = prompt | llm | parser
|
|
2962
|
+
|
|
2963
|
+
# Invoke the chain with a query
|
|
2964
|
+
result = chain.invoke({{"query": "Tell me a joke."}})
|
|
2965
|
+
print(result)
|
|
2966
|
+
|
|
2967
|
+
|
|
2968
|
+
# Get DEEKSEEK_API_KEY environmental variable
|
|
2969
|
+
deepseek_api_key = os.getenv('DEEKSEEK_API_KEY')
|
|
2970
|
+
|
|
2971
|
+
# Ensure the API key is retrieved successfully
|
|
2972
|
+
if deepseek_api_key is None:
|
|
2973
|
+
raise ValueError("DEEKSEEK_API_KEY environment variable is not set")
|
|
2974
|
+
|
|
2975
|
+
llm = ChatOpenAI(
|
|
2976
|
+
model='deepseek-chat',
|
|
2977
|
+
openai_api_key=deepseek_api_key,
|
|
2978
|
+
openai_api_base='https://api.deepseek.com',
|
|
2979
|
+
temperature=0
|
|
2980
|
+
)
|
|
2981
|
+
|
|
2982
|
+
# Chain the components
|
|
2983
|
+
chain = prompt | llm | parser
|
|
2984
|
+
|
|
2985
|
+
# Invoke the chain with a query
|
|
2986
|
+
result = chain.invoke({{"query": "Write joke about the sky"}})
|
|
2987
|
+
print("deepseek",result)
|
|
2988
|
+
|
|
2989
|
+
|
|
2990
|
+
llm = Fireworks(
|
|
2991
|
+
model="accounts/fireworks/models/mixtral-8x7b-instruct",
|
|
2992
|
+
temperature=0)
|
|
2993
|
+
# Chain the components
|
|
2994
|
+
chain = prompt | llm | parser
|
|
2995
|
+
|
|
2996
|
+
# Invoke the chain with a query
|
|
2997
|
+
result = chain.invoke({{"query": "Tell me a joke about the president"}})
|
|
2998
|
+
print("fireworks",result)
|
|
2999
|
+
|
|
3000
|
+
|
|
3001
|
+
|
|
3002
|
+
|
|
3003
|
+
|
|
3004
|
+
prompt = ChatPromptTemplate.from_template(
|
|
3005
|
+
"Tell me a short joke about {{topic}}"
|
|
3006
|
+
)
|
|
3007
|
+
chat_openai = ChatOpenAI(model="gpt-3.5-turbo")
|
|
3008
|
+
openai = OpenAI(model="gpt-3.5-turbo-instruct")
|
|
3009
|
+
anthropic = ChatAnthropic(model="claude-2")
|
|
3010
|
+
model = (
|
|
3011
|
+
chat_openai
|
|
3012
|
+
.with_fallbacks([anthropic])
|
|
3013
|
+
.configurable_alternatives(
|
|
3014
|
+
ConfigurableField(id="model"),
|
|
3015
|
+
default_key="chat_openai",
|
|
3016
|
+
openai=openai,
|
|
3017
|
+
anthropic=anthropic,
|
|
3018
|
+
)
|
|
3019
|
+
)
|
|
3020
|
+
|
|
3021
|
+
chain = (
|
|
3022
|
+
{{"topic": RunnablePassthrough()}}
|
|
3023
|
+
| prompt
|
|
3024
|
+
| model
|
|
3025
|
+
| StrOutputParser()
|
|
3026
|
+
)
|
|
3027
|
+
result = chain.invoke({{"topic": "Tell me a joke about the president"}})
|
|
3028
|
+
print("config alt:",result)
|
|
3029
|
+
|
|
3030
|
+
|
|
3031
|
+
|
|
3032
|
+
llm = ChatGroq(temperature=0, model_name="mixtral-8x7b-32768")
|
|
3033
|
+
system = "You are a helpful assistant."
|
|
3034
|
+
human = "{{text}}"
|
|
3035
|
+
prompt = ChatPromptTemplate.from_messages([("system", system), ("human", human)])
|
|
3036
|
+
|
|
3037
|
+
chain = prompt | llm | StrOutputParser()
|
|
3038
|
+
print(chain.invoke({{"text": "Explain the importance of low latency LLMs."}}))
|
|
3039
|
+
|
|
3040
|
+
|
|
3041
|
+
llm = Together(
|
|
3042
|
+
model="meta-llama/Llama-3-70b-chat-hf",
|
|
3043
|
+
)
|
|
3044
|
+
chain = prompt | llm | StrOutputParser()
|
|
3045
|
+
print(chain.invoke({{"text": "Explain the importance of together.ai."}}))
|
|
3046
|
+
|
|
3047
|
+
```
|
|
3048
|
+
|
|
3049
|
+
% Here is an example how to select the Langchain llm and count tokens: ```
|
|
3050
|
+
import os
|
|
3051
|
+
from llm_selector import llm_selector
|
|
3052
|
+
|
|
3053
|
+
def main() -> None:
|
|
3054
|
+
"""
|
|
3055
|
+
Main function to demonstrate the usage of the llm_selector function.
|
|
3056
|
+
Sets environment variables, defines parameters, and calls the function.
|
|
3057
|
+
"""
|
|
3058
|
+
# Set environment variables (for demonstration purposes)
|
|
3059
|
+
os.environ['PDD_MODEL_DEFAULT'] = 'gpt-4o-mini' # Default model
|
|
3060
|
+
|
|
3061
|
+
|
|
3062
|
+
# Define desired strength and temperature
|
|
3063
|
+
strength: float = .3 # Desired strength of the model (0.0 to 1.0)
|
|
3064
|
+
temperature: float = 0 # Temperature for the model (0.0 to 1.0)
|
|
3065
|
+
|
|
3066
|
+
try:
|
|
3067
|
+
# Call the llm_selector function
|
|
3068
|
+
llm, token_counter, input_cost, output_cost, model_name = llm_selector(strength, temperature)
|
|
3069
|
+
|
|
3070
|
+
# Output the selected model details
|
|
3071
|
+
print(f"Selected LLM: {{model_name}}")
|
|
3072
|
+
print(f"Input Cost: {{input_cost}}")
|
|
3073
|
+
print(f"Output Cost: {{output_cost}}")
|
|
3074
|
+
except Exception as e:
|
|
3075
|
+
print(f"An error occurred: {{e}}")
|
|
3076
|
+
|
|
3077
|
+
if __name__ == "__main__":
|
|
3078
|
+
main()
|
|
3079
|
+
```
|
|
3080
|
+
|
|
3081
|
+
% This program will use Langchain to do the following:
|
|
3082
|
+
Step 1. Use $PDD_PATH environment variable to get the path to the project. Load the '$PDD_PATH/prompts/fix_errors_from_unit_tests_LLM.prompt' file. Also load the 'extract_unit_code_fix_LLM.prompt' from the same directory.
|
|
3083
|
+
Step 2. Read the contents of the error_file specified in the input. Handle any file I/O errors gracefully.
|
|
3084
|
+
Step 3. Then this will create a Langchain LCEL template from the fix_errors_from_unit_tests prompt.
|
|
3085
|
+
Step 4. This will use llm_selector with the provided strength and temperature for the llm model.
|
|
3086
|
+
Step 5. This will run the code through the model using Langchain LCEL.
|
|
3087
|
+
5a. Be sure to pass the following string parameters to the prompt during invoke:
|
|
3088
|
+
- 'unit_test'
|
|
3089
|
+
- 'code'
|
|
3090
|
+
- 'errors'
|
|
3091
|
+
5b. Pretty print a message letting the user know it is running and how many tokens (using token_counter from llm_selector) are in the prompt and the cost. The cost from llm_selector is in dollars per million tokens.
|
|
3092
|
+
5c. Append the output of this LCEL run to the error_file, adding a clear separator to distinguish it from previous content. Handle any file I/O errors gracefully.
|
|
3093
|
+
Step 6. This will pretty print the markdown formatting that is present in the result via the rich Markdown function to both the console and the error_file. It will also pretty print the number of tokens in the result and the cost. Also, print out the cost of this run.
|
|
3094
|
+
Step 7. Then this will create a second Langchain LCEL template from the extract_unit_code_fix prompt.
|
|
3095
|
+
Step 8. This will use llm_selector with a strength setting of 0.5 and the provided temperature for the llm model. However, instead of using String output, it will use the JSON output parser to use the 'get' function to extract the value of these keys: 'update_unit_test', 'update_code', 'fixed_unit_test' and 'fixed_code'.
|
|
3096
|
+
Step 9. This will run the code through the model using Langchain LCEL from Step 8.
|
|
3097
|
+
9a. Be sure to pass the following string parameters to the prompt during invoke:
|
|
3098
|
+
- 'unit_test_fix': This is the result of the Langchain LCEL from Step 5.
|
|
3099
|
+
- 'unit_test'
|
|
3100
|
+
- 'code'
|
|
3101
|
+
9b. Pretty print a message letting the user know it is running and how many tokens (using token_counter from llm_selector) are in the prompt and the cost.
|
|
3102
|
+
Step 10. Calculate the total cost by summing the costs from both LCEL runs.
|
|
3103
|
+
Step 11. Print the total cost of both runs and return 'update_unit_test', 'update_code', 'fixed_unit_test', 'fixed_code', and 'total_cost' as individual values from the JSON output parser.
|
|
3104
|
+
|
|
3105
|
+
% Ensure that the function handles potential errors gracefully, such as missing input parameters, issues with the LLM model responses, or file I/O errors when reading from or writing to the error_file.
|
|
3106
|
+
|
|
3107
|
+
```</example_modified_prompt>
|
|
3108
|
+
</example>
|
|
3109
|
+
</examples>
|
|
3110
|
+
|
|
19
3111
|
</change_prompt_examples>
|
|
20
3112
|
|
|
21
3113
|
<context>
|