pdd-cli 0.0.18__py3-none-any.whl → 0.0.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pdd-cli might be problematic. Click here for more details.

pdd/auto_deps_main.py CHANGED
@@ -94,5 +94,4 @@ def auto_deps_main(
94
94
  except Exception as e:
95
95
  if not ctx.obj.get('quiet', False):
96
96
  rprint(f"[bold red]Error:[/bold red] {str(e)}")
97
- sys.exit(1)
98
- # Removed the "raise" line so that we only exit, satisfying the test.
97
+ sys.exit(1)
pdd/cli.py CHANGED
@@ -46,7 +46,7 @@ console = Console()
46
46
  @click.option("--review-examples", is_flag=True,
47
47
  help="Review and optionally exclude few-shot examples before command execution.")
48
48
  @click.option('--local', is_flag=True, help='Run commands locally instead of in the cloud.')
49
- @click.version_option(version="0.0.18")
49
+ @click.version_option(version="0.0.20")
50
50
  @click.pass_context
51
51
  def cli(
52
52
  ctx,
pdd/context_generator.py CHANGED
@@ -116,7 +116,7 @@ def context_generator(code_module: str, prompt: str, language: str = "python", s
116
116
  llm_output=final_llm_output,
117
117
  language=language,
118
118
  strength=0.97,
119
- temperature=temperature,
119
+ temperature=0,
120
120
  verbose=verbose
121
121
  )
122
122
  total_cost += postprocess_cost
pdd/crash_main.py CHANGED
@@ -51,10 +51,14 @@ def crash_main(
51
51
  "output": output,
52
52
  "output_program": output_program
53
53
  }
54
+
55
+ force = ctx.params.get("force", ctx.obj.get("force", False))
56
+ quiet = ctx.params.get("quiet", ctx.obj.get("quiet", False))
57
+
54
58
  input_strings, output_file_paths, _ = construct_paths(
55
59
  input_file_paths=input_file_paths,
56
- force=ctx.obj.get('force', False),
57
- quiet=ctx.obj.get('quiet', False),
60
+ force=force,
61
+ quiet=quiet,
58
62
  command="crash",
59
63
  command_options=command_options
60
64
  )
@@ -66,72 +70,62 @@ def crash_main(
66
70
  error_content = input_strings["error_file"]
67
71
 
68
72
  # Get model parameters from context
69
- strength = ctx.obj.get('strength', 0.97)
70
- temperature = ctx.obj.get('temperature', 0)
73
+ strength = ctx.obj.get("strength", 0.97)
74
+ temperature = ctx.obj.get("temperature", 0)
75
+
76
+ verbose = ctx.params.get("verbose", ctx.obj.get("verbose", False))
71
77
 
72
78
  if loop:
73
79
  # Use iterative fixing process
74
80
  success, final_code, final_program, attempts, cost, model = fix_code_loop(
75
- code_file=code_file,
76
- prompt=prompt_content,
77
- verification_program=program_file,
78
- strength=strength,
79
- temperature=temperature,
80
- max_attempts=max_attempts or 3,
81
- budget=budget or 5.0,
82
- error_log_file=error_file,
83
- verbose=not ctx.obj.get('verbose', False)
81
+ code_file, prompt_content, program_file, strength, temperature, max_attempts or 3, budget or 5.0, error_file, verbose
84
82
  )
85
83
  else:
86
84
  # Use single fix attempt
87
85
  from .fix_code_module_errors import fix_code_module_errors
88
86
  update_program, update_code, final_program, final_code, cost, model = fix_code_module_errors(
89
- program=program_content,
90
- prompt=prompt_content,
91
- code=code_content,
92
- errors=error_content,
93
- strength=strength,
94
- temperature=temperature,
95
- verbose=not ctx.obj.get('verbose', False)
87
+ program_content, prompt_content, code_content, error_content, strength, temperature, verbose
96
88
  )
97
89
  success = True
98
90
  attempts = 1
99
91
 
100
- # Determine if contents were actually updated
101
- if final_code != "":
102
- update_code = final_code != code_content
103
- else:
104
- update_code = False
105
- if final_program != "":
106
- update_program = final_program != program_content
107
- else:
108
- update_program = False
109
-
110
- # Save results if contents changed
111
- if update_code and output_file_paths.get("output"):
112
- with open(output_file_paths["output"], 'w') as f:
92
+ # Ensure we have content to write, falling back to original content if needed
93
+ if final_code == "":
94
+ final_code = code_content
95
+
96
+ if final_program == "":
97
+ final_program = program_content
98
+
99
+ # Determine whether to write the files based on whether paths are provided
100
+ should_write_code = output_file_paths.get("output") is not None
101
+ should_write_program = output_file_paths.get("output_program") is not None
102
+
103
+ # Write output files
104
+ if should_write_code:
105
+ with open(output_file_paths["output"], "w") as f:
113
106
  f.write(final_code)
114
- if update_program and output_file_paths.get("output_program"):
115
- with open(output_file_paths["output_program"], 'w') as f:
107
+
108
+ if should_write_program:
109
+ with open(output_file_paths["output_program"], "w") as f:
116
110
  f.write(final_program)
117
111
 
118
112
  # Provide user feedback
119
- if not ctx.obj.get('quiet', False):
113
+ if not quiet:
120
114
  if success:
121
115
  rprint("[bold green]Crash fix completed successfully.[/bold green]")
122
116
  else:
123
- rprint("[bold yellow]Crash fix completed with some issues.[/bold yellow]")
117
+ rprint("[bold yellow]Crash fix completed with issues.[/bold yellow]")
124
118
  rprint(f"[bold]Model used:[/bold] {model}")
125
119
  rprint(f"[bold]Total attempts:[/bold] {attempts}")
126
- rprint(f"[bold]Total cost:[/bold] ${cost:.6f}")
127
- if update_code and output:
120
+ rprint(f"[bold]Total cost:[/bold] ${cost:.2f}")
121
+ if should_write_code:
128
122
  rprint(f"[bold]Fixed code saved to:[/bold] {output_file_paths['output']}")
129
- if update_program and output_program:
123
+ if should_write_program:
130
124
  rprint(f"[bold]Fixed program saved to:[/bold] {output_file_paths['output_program']}")
131
125
 
132
126
  return success, final_code, final_program, attempts, cost, model
133
-
127
+
134
128
  except Exception as e:
135
- if not ctx.obj.get('quiet', False):
129
+ if not quiet:
136
130
  rprint(f"[bold red]Error:[/bold red] {str(e)}")
137
131
  sys.exit(1)
pdd/data/llm_model.csv CHANGED
@@ -1,17 +1,17 @@
1
1
  provider,model,input,output,coding_arena_elo,base_url,api_key,counter,encoder,max_tokens,max_completion_tokens,structured_output
2
- OpenAI,"gpt-4o-mini",0.15,0.60,1246,,OPENAI_API_KEY,tiktoken,o200k_base,16384,,True
2
+ OpenAI,"gpt-4o-mini",0.15,0.60,1246,,OPENAI_API_KEY,tiktoken,o200k_base,,16384,True
3
3
  OpenAI,"grok-2-1212",2,10,1255,"https://api.x.ai/v1",XAI_API_KEY,tiktoken,o200k_base,4096,,False
4
4
  Anthropic,"claude-3-5-haiku-20241022",1,5,1259,,ANTHROPIC_API_KEY,anthropic,claude-3-sonnet-20240229,8192,,False
5
5
  OpenAI,"deepseek-coder",0.14,0.28,1279,https://api.deepseek.com/beta,DEEPSEEK_API_KEY,autotokenizer,deepseek-coder-7b-instruct-v1.5,8192,,False
6
6
  Google,"gemini-2.0-flash-thinking-exp-01-21",.1,.4,1291,,GOOGLE_API_KEY,,,8192,,False
7
7
  GoogleVertexAI,"gemini-2.0-pro-exp-02-05",1.25,5,1299,,VERTEX_AI_API_KEY,,,8192,,False
8
- Anthropic,claude-3-7-sonnet-20250219,3,15,1312,,ANTHROPIC_API_KEY,anthropic,claude-3-sonnet-20240229,8192,,False
8
+ Anthropic,claude-3-7-sonnet-20250219,3,15,1312,,ANTHROPIC_API_KEY,anthropic,claude-3-sonnet-20240229,64000,,False
9
9
  Google,gemini-exp-1206,1.25,5,1313,,GOOGLE_API_KEY,,,8192,,False
10
10
  OpenAI,"deepseek-r1-distill-llama-70b-specdec",5,5,1314,https://api.groq.com/openai/v1,GROQ_API_KEY,autotokenizer,deepseek-coder-7b-instruct-v1.5,16384,,False
11
11
  Ollama,"deepseek-r1:70b-llama-distill-q8_0",0.0,0.0,1315,,PWD,,,,,False
12
12
  Ollama,deepseek-r1:32b-qwen-distill-fp16,0.0,0.0,1316,,PWD,,,,,False
13
13
  OpenAI,"o3-mini",1.1,4.4,1319,,OPENAI_API_KEY,tiktoken,o200k_base,,100000,True
14
14
  OpenAI,"o1-2024-12-17",15,60,1331,,OPENAI_API_KEY,tiktoken,o200k_base,,32768,True
15
- OpenAI,"gpt-4o-2024-11-20",2.5,10,1332,,OPENAI_API_KEY,tiktoken,o200k_base,16384,,True
15
+ OpenAI,"gpt-4o-2024-11-20",2.5,10,1332,,OPENAI_API_KEY,tiktoken,o200k_base,,16384,True
16
16
  OpenAI,"deepseek-reasoner",0.55,2.19,1336,https://api.deepseek.com/beta,DEEPSEEK_API_KEY,autotokenizer,deepseek-coder-7b-instruct-v1.5,8192,,False
17
17
  Fireworks,accounts/fireworks/models/deepseek-r1,3,8,1338,,FIREWORKS_API_KEY,,,8192,,False
pdd/fix_error_loop.py CHANGED
@@ -135,24 +135,32 @@ def fix_error_loop(unit_test_file: str,
135
135
 
136
136
  # We do up to max_attempts fix attempts or until budget is exceeded
137
137
  iteration = 0
138
+ # Run an initial test to determine starting state
139
+ try:
140
+ fails, errors, warnings, pytest_output = run_pytest_on_file(unit_test_file)
141
+ except Exception as e:
142
+ rprint(f"[red]Error running initial pytest:[/red] {e}")
143
+ return False, "", "", fix_attempts, total_cost, model_name
144
+
138
145
  while fix_attempts < max_attempts and total_cost < budget:
139
146
  iteration += 1
140
- iteration_header = f"=== Attempt iteration {iteration} ==="
141
- rprint(f"[bold blue]{iteration_header}[/bold blue]")
142
- with open(error_log_file, "a") as elog:
143
- elog.write(f"\n{iteration_header}\n")
144
-
145
- # 1) Run the unit tests using pytest's API directly.
146
- try:
147
- fails, errors, warnings, pytest_output = run_pytest_on_file(unit_test_file)
148
- except Exception as e:
149
- rprint(f"[red]Error running pytest:[/red] {e}")
150
- return False, "", "", fix_attempts, total_cost, model_name
151
147
 
152
148
  # Append to error log:
153
149
  with open(error_log_file, "a") as elog:
150
+ elog.write(f"<pytest_output iteration={iteration}>\n")
154
151
  elog.write(pytest_output + "\n")
155
-
152
+ elog.write("</pytest_output>\n")
153
+
154
+ # If tests pass initially, no need to fix anything
155
+ if fails == 0 and errors == 0 and warnings == 0:
156
+ rprint("[green]All tests already pass with no warnings! No fixes needed.[/green]")
157
+ return True, "", "", 0, 0.0, ""
158
+
159
+ iteration_header = f"=== Attempt iteration {iteration} ==="
160
+ rprint(f"[bold blue]{iteration_header}[/bold blue]")
161
+ with open(error_log_file, "a") as elog:
162
+ elog.write(f"\n{iteration_header}\n\n")
163
+ elog.write(f"<fix_attempt iteration={iteration}>\n")
156
164
  # Print to console (escaped):
157
165
  rprint(f"[magenta]Pytest output:[/magenta]\n{escape_brackets(pytest_output)}")
158
166
  if verbose:
@@ -271,8 +279,10 @@ def fix_error_loop(unit_test_file: str,
271
279
  verify_output = f"Verification program error: {e}"
272
280
 
273
281
  with open(error_log_file, "a") as elog:
274
- elog.write(f"\n[Verification attempt at iteration {iteration}]\n")
275
- elog.write(verify_output + "\n")
282
+ elog.write(f"</fix_attempt>\n\n")
283
+ elog.write(f"\n[Verification attempt at iteration {iteration}]\n<verification_output iteration={iteration}>\n")
284
+ elog.write(verify_output )
285
+ elog.write("</verification_output>\n")
276
286
 
277
287
  rprint(f"[blue]Verification program output:[/blue]\n{escape_brackets(verify_output)}")
278
288
 
@@ -281,40 +291,18 @@ def fix_error_loop(unit_test_file: str,
281
291
  try:
282
292
  shutil.copy(code_backup, code_file)
283
293
  with open(error_log_file, "a") as elog:
284
- elog.write(f"Restored code file from backup: {code_backup}\n")
294
+ elog.write(f"Restored code file from backup: {code_backup}, because verification program failed to run.\n")
285
295
  except Exception as e:
286
296
  rprint(f"[red]Error restoring backup code file:[/red] {e}")
287
297
  break
288
298
 
289
- # Re-run the tests in the same iteration:
299
+ # Run pytest for the next iteration
290
300
  try:
291
- fails2, errors2, warnings2, second_run_output = run_pytest_on_file(unit_test_file)
301
+ fails, errors, warnings, pytest_output = run_pytest_on_file(unit_test_file)
292
302
  except Exception as e:
293
- rprint(f"[red]Error running second pytest attempt in iteration {iteration}:[/red] {e}")
303
+ rprint(f"[red]Error running pytest for next iteration:[/red] {e}")
294
304
  return False, "", "", fix_attempts, total_cost, model_name
295
305
 
296
- with open(error_log_file, "a") as elog:
297
- elog.write("\n=== Second Pytest Check (same iteration) ===\n")
298
- elog.write(second_run_output + "\n")
299
-
300
- rprint(f"[magenta]Second pytest check:[/magenta]\n{escape_brackets(second_run_output)}")
301
-
302
- if fails2 == 0 and errors2 == 0 and warnings2 == 0:
303
- rprint("[green]All tests passed on the second run of this iteration! Exiting loop.[/green]")
304
- break
305
- else:
306
- if (errors2 < best_iteration_info["errors"] or
307
- (errors2 == best_iteration_info["errors"] and fails2 < best_iteration_info["fails"]) or
308
- (errors2 == best_iteration_info["errors"] and fails2 == best_iteration_info["fails"] and warnings2 < best_iteration_info["warnings"])):
309
- best_iteration_info = {
310
- "attempt": iteration,
311
- "fails": fails2,
312
- "errors": errors2,
313
- "warnings": warnings2,
314
- "unit_test_backup": unit_test_backup,
315
- "code_backup": code_backup
316
- }
317
-
318
306
  # Final test run:
319
307
  try:
320
308
  final_fails, final_errors, final_warnings, final_output = run_pytest_on_file(unit_test_file)
@@ -154,15 +154,21 @@ def fix_errors_from_unit_tests(
154
154
  processed_prompt = preprocess(
155
155
  prompt,
156
156
  recursive=False,
157
+ double_curly_brackets=True
158
+ )
159
+
160
+ processed_fix_errors_prompt = preprocess(
161
+ fix_errors_prompt,
162
+ recursive=False,
157
163
  double_curly_brackets=True,
158
- exclude_keys=['unit_test', 'code', 'unit_test_fix']
164
+ exclude_keys=['unit_test', 'code', 'errors', 'prompt']
159
165
  )
160
166
 
161
167
  if verbose:
162
168
  console.print(Panel("[bold green]Running fix_errors_from_unit_tests...[/bold green]"))
163
169
 
164
170
  response1 = llm_invoke(
165
- prompt=fix_errors_prompt,
171
+ prompt=processed_fix_errors_prompt,
166
172
  input_json={
167
173
  "unit_test": unit_test,
168
174
  "code": code,
pdd/fix_main.py CHANGED
@@ -3,9 +3,17 @@ from typing import Tuple, Optional
3
3
  import click
4
4
  from rich import print as rprint
5
5
 
6
+ import requests
7
+ import asyncio
8
+ import os
9
+
10
+ from .preprocess import preprocess
11
+
6
12
  from .construct_paths import construct_paths
7
13
  from .fix_errors_from_unit_tests import fix_errors_from_unit_tests
8
14
  from .fix_error_loop import fix_error_loop
15
+ from .get_jwt_token import get_jwt_token
16
+ from .get_language import get_language
9
17
 
10
18
  def fix_main(
11
19
  ctx: click.Context,
@@ -130,6 +138,105 @@ def fix_main(
130
138
  if output_file_paths.get("output_results"):
131
139
  rprint(f" Results file: {output_file_paths['output_results']}")
132
140
 
141
+ # Auto-submit example if requested and successful
142
+ if auto_submit:
143
+ try:
144
+ # Get JWT token for cloud authentication
145
+ jwt_token = asyncio.run(get_jwt_token(
146
+ firebase_api_key=os.environ.get("REACT_APP_FIREBASE_API_KEY"),
147
+ github_client_id=os.environ.get("GITHUB_CLIENT_ID"),
148
+ app_name="PDD Code Generator"
149
+ ))
150
+ processed_prompt = preprocess(
151
+ input_strings["prompt_file"],
152
+ recursive=False,
153
+ double_curly_brackets=True
154
+ )
155
+ # Prepare the submission payload
156
+ payload = {
157
+ "command": "fix",
158
+ "input": {
159
+ "prompts": [{
160
+ "content": processed_prompt,
161
+ "filename": os.path.basename(prompt_file)
162
+ }],
163
+ "code": [{
164
+ "content": input_strings["code_file"],
165
+ "filename": os.path.basename(code_file)
166
+ }],
167
+ "test": [{
168
+ "content": input_strings["unit_test_file"],
169
+ "filename": os.path.basename(unit_test_file)
170
+ }]
171
+ },
172
+ "output": {
173
+ "code": [{
174
+ "content": fixed_code,
175
+ "filename": os.path.basename(output_file_paths["output_code"])
176
+ }],
177
+ "test": [{
178
+ "content": fixed_unit_test,
179
+ "filename": os.path.basename(output_file_paths["output_test"])
180
+ }]
181
+ },
182
+ "metadata": {
183
+ "title": f"Auto-submitted fix for {os.path.basename(code_file)}",
184
+ "description": "Automatically submitted successful code fix",
185
+ "language": get_language(os.path.splitext(code_file)[1]), # Detect language from file extension
186
+ "framework": "",
187
+ "tags": ["auto-fix", "example"],
188
+ "isPublic": True,
189
+ "price": 0.0
190
+ }
191
+ }
192
+
193
+ # Add verification program if specified
194
+ if verification_program:
195
+ with open(verification_program, 'r') as f:
196
+ verifier_content = f.read()
197
+ payload["input"]["example"] = [{
198
+ "content": verifier_content,
199
+ "filename": os.path.basename(verification_program)
200
+ }]
201
+
202
+ # Add error logs if available
203
+ if "error_file" in input_strings:
204
+ payload["input"]["error"] = [{
205
+ "content": input_strings["error_file"],
206
+ "filename": os.path.basename(error_file)
207
+ }]
208
+
209
+ # Add analysis if available
210
+ if output_file_paths.get("output_results"):
211
+ with open(output_file_paths["output_results"], 'r') as f:
212
+ analysis_content = f.read()
213
+ payload["output"]["analysis"] = [{
214
+ "content": analysis_content,
215
+ "filename": os.path.basename(output_file_paths["output_results"])
216
+ }]
217
+
218
+ # Submit the example to Firebase Cloud Function
219
+ headers = {
220
+ "Authorization": f"Bearer {jwt_token}",
221
+ "Content-Type": "application/json"
222
+ }
223
+ response = requests.post(
224
+ 'https://us-central1-prompt-driven-development.cloudfunctions.net/submitExample',
225
+ json=payload,
226
+ headers=headers
227
+ )
228
+
229
+ if response.status_code == 200:
230
+ if not ctx.obj.get('quiet', False):
231
+ rprint("[bold green]Successfully submitted example[/bold green]")
232
+ else:
233
+ if not ctx.obj.get('quiet', False):
234
+ rprint(f"[bold red]Failed to submit example: {response.text}[/bold red]")
235
+
236
+ except Exception as e:
237
+ if not ctx.obj.get('quiet', False):
238
+ rprint(f"[bold red]Error submitting example: {str(e)}[/bold red]")
239
+
133
240
  return success, fixed_unit_test, fixed_code, attempts, total_cost, model_name
134
241
 
135
242
  except Exception as e:
pdd/insert_includes.py CHANGED
@@ -60,7 +60,8 @@ def insert_includes(
60
60
  processed_prompt = preprocess(
61
61
  insert_includes_prompt,
62
62
  recursive=False,
63
- double_curly_brackets=False
63
+ double_curly_brackets=True,
64
+ exclude_keys=["actual_prompt_to_update", "actual_dependencies_to_insert"]
64
65
  )
65
66
 
66
67
  if verbose:
pdd/llm_invoke.py CHANGED
@@ -226,7 +226,16 @@ def create_llm_instance(selected_model, temperature, handler):
226
226
  llm = ChatOpenAI(model=model_name, temperature=temperature,
227
227
  openai_api_key=api_key, callbacks=[handler])
228
228
  elif provider == 'anthropic':
229
- llm = ChatAnthropic(model=model_name, temperature=temperature, callbacks=[handler])
229
+ # Special case for Claude 3.7 Sonnet with thinking token budget
230
+ if 'claude-3-7-sonnet' in model_name:
231
+ llm = ChatAnthropic(
232
+ model=model_name,
233
+ temperature=temperature,
234
+ callbacks=[handler],
235
+ thinking={"type": "enabled", "budget_tokens": 4000} # 32K thinking token budget
236
+ )
237
+ else:
238
+ llm = ChatAnthropic(model=model_name, temperature=temperature, callbacks=[handler])
230
239
  elif provider == 'google':
231
240
  llm = ChatGoogleGenerativeAI(model=model_name, temperature=temperature, callbacks=[handler])
232
241
  elif provider == 'googlevertexai':