pdd-cli 0.0.90__py3-none-any.whl → 0.0.118__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. pdd/__init__.py +38 -6
  2. pdd/agentic_bug.py +323 -0
  3. pdd/agentic_bug_orchestrator.py +497 -0
  4. pdd/agentic_change.py +231 -0
  5. pdd/agentic_change_orchestrator.py +526 -0
  6. pdd/agentic_common.py +521 -786
  7. pdd/agentic_e2e_fix.py +319 -0
  8. pdd/agentic_e2e_fix_orchestrator.py +426 -0
  9. pdd/agentic_fix.py +118 -3
  10. pdd/agentic_update.py +25 -8
  11. pdd/architecture_sync.py +565 -0
  12. pdd/auth_service.py +210 -0
  13. pdd/auto_deps_main.py +63 -53
  14. pdd/auto_include.py +185 -3
  15. pdd/auto_update.py +125 -47
  16. pdd/bug_main.py +195 -23
  17. pdd/cmd_test_main.py +345 -197
  18. pdd/code_generator.py +4 -2
  19. pdd/code_generator_main.py +118 -32
  20. pdd/commands/__init__.py +6 -0
  21. pdd/commands/analysis.py +87 -29
  22. pdd/commands/auth.py +309 -0
  23. pdd/commands/connect.py +290 -0
  24. pdd/commands/fix.py +136 -113
  25. pdd/commands/maintenance.py +3 -2
  26. pdd/commands/misc.py +8 -0
  27. pdd/commands/modify.py +190 -164
  28. pdd/commands/sessions.py +284 -0
  29. pdd/construct_paths.py +334 -32
  30. pdd/context_generator_main.py +167 -170
  31. pdd/continue_generation.py +6 -3
  32. pdd/core/__init__.py +33 -0
  33. pdd/core/cli.py +27 -3
  34. pdd/core/cloud.py +237 -0
  35. pdd/core/errors.py +4 -0
  36. pdd/core/remote_session.py +61 -0
  37. pdd/crash_main.py +219 -23
  38. pdd/data/llm_model.csv +4 -4
  39. pdd/docs/prompting_guide.md +864 -0
  40. pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
  41. pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
  42. pdd/fix_code_loop.py +208 -34
  43. pdd/fix_code_module_errors.py +6 -2
  44. pdd/fix_error_loop.py +291 -38
  45. pdd/fix_main.py +204 -4
  46. pdd/fix_verification_errors_loop.py +235 -26
  47. pdd/fix_verification_main.py +269 -83
  48. pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
  49. pdd/frontend/dist/assets/index-DQ3wkeQ2.js +449 -0
  50. pdd/frontend/dist/index.html +376 -0
  51. pdd/frontend/dist/logo.svg +33 -0
  52. pdd/generate_output_paths.py +46 -5
  53. pdd/generate_test.py +212 -151
  54. pdd/get_comment.py +19 -44
  55. pdd/get_extension.py +8 -9
  56. pdd/get_jwt_token.py +309 -20
  57. pdd/get_language.py +8 -7
  58. pdd/get_run_command.py +7 -5
  59. pdd/insert_includes.py +2 -1
  60. pdd/llm_invoke.py +459 -95
  61. pdd/load_prompt_template.py +15 -34
  62. pdd/path_resolution.py +140 -0
  63. pdd/postprocess.py +4 -1
  64. pdd/preprocess.py +68 -12
  65. pdd/preprocess_main.py +33 -1
  66. pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
  67. pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
  68. pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
  69. pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
  70. pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
  71. pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
  72. pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
  73. pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
  74. pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
  75. pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
  76. pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
  77. pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
  78. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +131 -0
  79. pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
  80. pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
  81. pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
  82. pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
  83. pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
  84. pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
  85. pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
  86. pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
  87. pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
  88. pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
  89. pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
  90. pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
  91. pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
  92. pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
  93. pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
  94. pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
  95. pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
  96. pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
  97. pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
  98. pdd/prompts/agentic_update_LLM.prompt +192 -338
  99. pdd/prompts/auto_include_LLM.prompt +22 -0
  100. pdd/prompts/change_LLM.prompt +3093 -1
  101. pdd/prompts/detect_change_LLM.prompt +571 -14
  102. pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
  103. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
  104. pdd/prompts/generate_test_LLM.prompt +20 -1
  105. pdd/prompts/generate_test_from_example_LLM.prompt +115 -0
  106. pdd/prompts/insert_includes_LLM.prompt +262 -252
  107. pdd/prompts/prompt_code_diff_LLM.prompt +119 -0
  108. pdd/prompts/prompt_diff_LLM.prompt +82 -0
  109. pdd/remote_session.py +876 -0
  110. pdd/server/__init__.py +52 -0
  111. pdd/server/app.py +335 -0
  112. pdd/server/click_executor.py +587 -0
  113. pdd/server/executor.py +338 -0
  114. pdd/server/jobs.py +661 -0
  115. pdd/server/models.py +241 -0
  116. pdd/server/routes/__init__.py +31 -0
  117. pdd/server/routes/architecture.py +451 -0
  118. pdd/server/routes/auth.py +364 -0
  119. pdd/server/routes/commands.py +929 -0
  120. pdd/server/routes/config.py +42 -0
  121. pdd/server/routes/files.py +603 -0
  122. pdd/server/routes/prompts.py +1322 -0
  123. pdd/server/routes/websocket.py +473 -0
  124. pdd/server/security.py +243 -0
  125. pdd/server/terminal_spawner.py +209 -0
  126. pdd/server/token_counter.py +222 -0
  127. pdd/summarize_directory.py +236 -237
  128. pdd/sync_animation.py +8 -4
  129. pdd/sync_determine_operation.py +329 -47
  130. pdd/sync_main.py +272 -28
  131. pdd/sync_orchestration.py +136 -75
  132. pdd/template_expander.py +161 -0
  133. pdd/templates/architecture/architecture_json.prompt +41 -46
  134. pdd/trace.py +1 -1
  135. pdd/track_cost.py +0 -13
  136. pdd/unfinished_prompt.py +2 -1
  137. pdd/update_main.py +23 -5
  138. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/METADATA +15 -10
  139. pdd_cli-0.0.118.dist-info/RECORD +227 -0
  140. pdd_cli-0.0.90.dist-info/RECORD +0 -153
  141. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/WHEEL +0 -0
  142. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/entry_points.txt +0 -0
  143. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/licenses/LICENSE +0 -0
  144. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,3099 @@
15
15
  </inputs_outputs_definitions>
16
16
 
17
17
  <change_prompt_examples>
18
- [File not found: ../prompts/xml/change_example_partial_processed.prompt]
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>