pdd-cli 0.0.30__py3-none-any.whl → 0.0.32__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.

@@ -507,44 +507,70 @@ def fix_verification_errors_loop(
507
507
  # FIX: Restructured logic for success check and secondary verification
508
508
  secondary_verification_passed = True # Assume pass unless changes made and verification fails
509
509
  changes_applied_this_iteration = False
510
+ verify_ret_code = 0 # Default for skipped verification
511
+ verify_output = "Secondary verification not run." # Default for skipped
510
512
 
511
- # Run secondary verification ONLY if code was updated
512
513
  if code_updated:
513
514
  if verbose:
514
- console.print("Code change suggested, running secondary verification...")
515
- try:
516
- # Temporarily write the proposed code change
517
- code_path.write_text(fixed_code, encoding="utf-8")
515
+ console.print("Code change suggested, attempting secondary verification...")
516
+
517
+ if verification_program is not None and verification_program_path.is_file():
518
+ try:
519
+ # Temporarily write the proposed code change
520
+ code_path.write_text(fixed_code, encoding="utf-8")
518
521
 
519
- # Run verification program
520
- verify_ret_code, verify_output = _run_program(verification_program_path)
522
+ # Run verification program
523
+ # Consider if verification_program_path needs arguments or specific env vars
524
+ # For now, assuming it can run directly or uses env vars set externally
525
+ current_verify_ret_code, current_verify_output = _run_program(verification_program_path)
521
526
 
522
- # Determine pass/fail (simple: exit code 0 = pass)
523
- secondary_verification_passed = (verify_ret_code == 0)
527
+ # Determine pass/fail (simple: exit code 0 = pass)
528
+ secondary_verification_passed = (current_verify_ret_code == 0)
529
+ verify_ret_code = current_verify_ret_code
530
+ verify_output = current_verify_output
524
531
 
532
+ if verbose:
533
+ console.print(f"Secondary verification ran. Exit code: {verify_ret_code}")
534
+ console.print(f"Secondary verification passed: {secondary_verification_passed}")
535
+ # console.print(f"Secondary verification output:\\n{verify_output}")
536
+
537
+ if not secondary_verification_passed:
538
+ console.print("[yellow]Secondary verification failed. Restoring code file from memory.[/yellow]")
539
+ code_path.write_text(code_contents, encoding="utf-8") # Restore from memory state before this attempt
540
+
541
+ except IOError as e:
542
+ console.print(f"[bold red]Error during secondary verification I/O: {e}[/bold red]")
543
+ verify_output = f"Error during secondary verification I/O: {str(e)}"
544
+ secondary_verification_passed = False # Treat I/O error as failure
545
+ verify_ret_code = -1 # Indicate error
546
+ try:
547
+ code_path.write_text(code_contents, encoding="utf-8")
548
+ except IOError:
549
+ console.print(f"[bold red]Failed to restore code file after I/O error.[/bold red]")
550
+ else:
551
+ # No valid verification program provided, or it's not a file
552
+ secondary_verification_passed = True # Effectively skipped, so it doesn't block progress
553
+ verify_ret_code = 0
554
+ if verification_program is None:
555
+ verify_output = "Secondary verification skipped: No verification program provided."
556
+ else:
557
+ verify_output = f"Secondary verification skipped: Verification program '{verification_program}' not found or is not a file at '{verification_program_path}'."
525
558
  if verbose:
526
- console.print(f"Secondary verification exit code: {verify_ret_code}")
527
- console.print(f"Secondary verification passed: {secondary_verification_passed}")
528
- # console.print(f"Secondary verification output:\n{verify_output}")
529
-
530
- passed_str = str(secondary_verification_passed).lower()
531
- iteration_log_xml += f' <SecondaryVerification passed="{passed_str}">\n'
532
- iteration_log_xml += f' <ExitCode>{verify_ret_code}</ExitCode>\n'
533
- iteration_log_xml += f' <Output>{escape(verify_output)}</Output>\n'
534
- iteration_log_xml += f' </SecondaryVerification>\n'
535
-
536
- if not secondary_verification_passed:
537
- console.print("[yellow]Secondary verification failed. Restoring code file.[/yellow]")
538
- code_path.write_text(code_contents, encoding="utf-8") # Restore from memory state before this attempt
539
-
540
- except IOError as e:
541
- console.print(f"[bold red]Error during secondary verification I/O: {e}[/bold red]")
542
- iteration_log_xml += f' <Status>Error during secondary verification I/O: {escape(str(e))}</Status>\n'
543
- secondary_verification_passed = False # Treat I/O error as failure
544
- try:
545
- code_path.write_text(code_contents, encoding="utf-8")
546
- except IOError:
547
- console.print(f"[bold red]Failed to restore code file after I/O error.[/bold red]")
559
+ console.print(f"[dim]{verify_output}[/dim]")
560
+ else:
561
+ # Code was not updated by the fixer, so secondary verification is not strictly needed
562
+ secondary_verification_passed = True # No changes to verify
563
+ verify_ret_code = 0
564
+ verify_output = "Secondary verification not needed: Code was not modified by the fixer."
565
+ if verbose:
566
+ console.print(f"[dim]{verify_output}[/dim]")
567
+
568
+ # Always log the SecondaryVerification block
569
+ passed_str = str(secondary_verification_passed).lower()
570
+ iteration_log_xml += f' <SecondaryVerification passed="{passed_str}">\n'
571
+ iteration_log_xml += f' <ExitCode>{verify_ret_code}</ExitCode>\n'
572
+ iteration_log_xml += f' <Output>{escape(verify_output)}</Output>\n'
573
+ iteration_log_xml += f' </SecondaryVerification>\n'
548
574
 
549
575
  # Now, decide outcome based on issue count and verification status
550
576
  if secondary_verification_passed:
@@ -2,12 +2,14 @@ import sys
2
2
  import os
3
3
  import subprocess
4
4
  import click
5
+ import logging
5
6
  from typing import Optional, Tuple, List, Dict, Any
6
7
 
7
8
  # Use Rich for pretty printing to the console
8
9
  from rich import print as rich_print
9
10
  from rich.panel import Panel
10
11
  from rich.syntax import Syntax
12
+ from rich.text import Text
11
13
 
12
14
  # Internal imports using relative paths
13
15
  from .construct_paths import construct_paths
@@ -17,9 +19,15 @@ from .fix_verification_errors_loop import fix_verification_errors_loop
17
19
  from . import DEFAULT_STRENGTH
18
20
 
19
21
  # Default values from the README
22
+ DEFAULT_TEMPERATURE = 0.0
20
23
  DEFAULT_MAX_ATTEMPTS = 3
21
24
  DEFAULT_BUDGET = 5.0
22
- DEFAULT_TEMPERATURE = 0.0
25
+
26
+ # Configure logging
27
+ logger = logging.getLogger(__name__)
28
+
29
+ # Define a constant for the verification program name
30
+ VERIFICATION_PROGRAM_NAME = "verification_program.py" # Example, adjust if needed
23
31
 
24
32
  def run_program(program_path: str, args: List[str] = []) -> Tuple[bool, str, str]:
25
33
  """
@@ -78,8 +86,8 @@ def run_program(program_path: str, args: List[str] = []) -> Tuple[bool, str, str
78
86
  rich_print(f"[bold red]Error:[/bold red] Program execution timed out: '{program_path}'")
79
87
  return False, "", f"Program execution timed out: {program_path}"
80
88
  except Exception as e:
81
- rich_print(f"[bold red]Error:[/bold red] Failed to run program '{program_path}': {e}")
82
- return False, "", f"Failed to run program: {e}"
89
+ logger.error(f"An unexpected error occurred while running {program_path}: {e}")
90
+ return False, "", f"An unexpected error occurred: {e}"
83
91
 
84
92
  def fix_verification_main(
85
93
  ctx: click.Context,
@@ -122,10 +130,9 @@ def fix_verification_main(
122
130
  - model_name (str): Name of the LLM used.
123
131
  """
124
132
  # Extract global options from context
125
- # params = ctx.params # We need obj for global flags
126
- strength: float = ctx.obj.get('strength', DEFAULT_STRENGTH) # Get globals from obj
133
+ strength: float = ctx.obj.get('strength', DEFAULT_STRENGTH)
127
134
  temperature: float = ctx.obj.get('temperature', DEFAULT_TEMPERATURE)
128
- force: bool = ctx.obj.get('force', False) # <<< FIX: Get force from ctx.obj
135
+ force: bool = ctx.obj.get('force', False)
129
136
  quiet: bool = ctx.obj.get('quiet', False)
130
137
  verbose: bool = ctx.obj.get('verbose', False)
131
138
 
@@ -220,14 +227,9 @@ def fix_verification_main(
220
227
  base_prog, ext_prog = os.path.splitext(program_file)
221
228
  output_program_path = f"{base_prog}_verified{ext_prog}"
222
229
 
223
- # Best‑effort language guess
224
- if program_file.endswith(".py"):
225
- language = "python"
226
- elif program_file.endswith(".js"):
227
- language = "javascript"
228
- elif program_file.endswith(".sh"):
229
- language = "bash"
230
-
230
+ if program_file.endswith(".py"): language = "python"
231
+ elif program_file.endswith(".js"): language = "javascript"
232
+ elif program_file.endswith(".sh"): language = "bash"
231
233
  else:
232
234
  # Some other error – re‑raise / abort
233
235
  rich_print(f"[bold red]Error:[/bold red] Failed during path construction: {e}")
@@ -238,12 +240,12 @@ def fix_verification_main(
238
240
 
239
241
  # --- Core Logic ---
240
242
  success: bool = False
241
- final_program: str = input_strings.get("program_file", "") # Initialize with input content
242
- final_code: str = input_strings.get("code_file", "") # Initialize with input content
243
+ final_program: str = input_strings.get("program_file", "")
244
+ final_code: str = input_strings.get("code_file", "")
243
245
  attempts: int = 0
244
246
  total_cost: float = 0.0
245
247
  model_name: str = "N/A"
246
- results_log_content: str = "" # To store content for the results file
248
+ results_log_content: str = ""
247
249
 
248
250
  try:
249
251
  if loop:
@@ -258,19 +260,19 @@ def fix_verification_main(
258
260
  loop_results = fix_verification_errors_loop(
259
261
  program_file=program_file,
260
262
  code_file=code_file,
261
- prompt=input_strings["prompt_file"], # Prompt content is needed
262
- verification_program=verification_program, # Path is needed
263
+ prompt=input_strings["prompt_file"],
264
+ verification_program=verification_program,
263
265
  strength=strength,
264
266
  temperature=temperature,
265
267
  max_attempts=max_attempts,
266
268
  budget=budget,
267
- verification_log_file=output_results_path, # Pass path for internal logging
269
+ verification_log_file=output_results_path,
268
270
  verbose=verbose,
269
271
  program_args=program_args
270
272
  )
271
273
  success = loop_results['success']
272
- final_program = loop_results['final_program'] # Loop function returns final content
273
- final_code = loop_results['final_code'] # Loop function returns final content
274
+ final_program = loop_results['final_program']
275
+ final_code = loop_results['final_code']
274
276
  attempts = loop_results['total_attempts']
275
277
  total_cost = loop_results['total_cost']
276
278
  model_name = loop_results['model_name']
@@ -300,12 +302,11 @@ def fix_verification_main(
300
302
  # 2. Call fix_verification_errors with content and program output
301
303
  if not quiet:
302
304
  rich_print("Calling LLM to verify program output against prompt...")
303
-
304
305
  fix_results = fix_verification_errors(
305
306
  program=input_strings["program_file"],
306
307
  prompt=input_strings["prompt_file"],
307
308
  code=input_strings["code_file"],
308
- output=program_output, # Pass the captured output
309
+ output=program_output,
309
310
  strength=strength,
310
311
  temperature=temperature,
311
312
  verbose=verbose
@@ -319,19 +320,15 @@ def fix_verification_main(
319
320
 
320
321
  if not issues_found:
321
322
  success = True
322
- if not quiet:
323
- rich_print("[green]Verification Passed:[/green] LLM found no discrepancies.")
323
+ if not quiet: rich_print("[green]Verification Passed:[/green] LLM found no discrepancies.")
324
324
  elif code_updated or program_updated:
325
325
  # If issues were found AND fixes were made, assume success for this single pass.
326
326
  # A more robust check might re-run the program with fixed code, but that's the loop's job.
327
327
  success = True
328
- if not quiet:
329
- rich_print("[yellow]Verification Issues Found:[/yellow] LLM proposed fixes.")
328
+ if not quiet: rich_print("[yellow]Verification Issues Found:[/yellow] LLM proposed fixes.")
330
329
  else:
331
- success = False # Issues found, but no fixes proposed
332
- if not quiet:
333
- rich_print("[red]Verification Failed:[/red] LLM found discrepancies but proposed no fixes.")
334
-
330
+ success = False
331
+ if not quiet: rich_print("[red]Verification Failed:[/red] LLM found discrepancies but proposed no fixes.")
335
332
 
336
333
  final_program = fix_results['fixed_program']
337
334
  final_code = fix_results['fixed_code']
@@ -340,7 +337,7 @@ def fix_verification_main(
340
337
 
341
338
  # Build results log content for single pass
342
339
  results_log_content = "PDD Verify Results (Single Pass)\n"
343
- results_log_content += f"Timestamp: {os.path.getmtime(prompt_file)}\n" # Use prompt timestamp as reference
340
+ results_log_content += f"Timestamp: {os.path.getmtime(prompt_file)}\n"
344
341
  results_log_content += f"Prompt File: {prompt_file}\n"
345
342
  results_log_content += f"Code File: {code_file}\n"
346
343
  results_log_content += f"Program File: {program_file}\n"
@@ -371,27 +368,46 @@ def fix_verification_main(
371
368
  saved_results_path: Optional[str] = None
372
369
  saved_program_path: Optional[str] = None
373
370
 
374
- if success and output_code_path:
371
+ if verbose:
372
+ rich_print(f"[cyan bold DEBUG] In fix_verification_main, BEFORE save attempt for CODE:")
373
+ rich_print(f" success: {success}")
374
+ rich_print(f" output_code_path: {output_code_path!r}")
375
+ rich_print(f" final_code is None: {final_code is None}")
376
+ if final_code is not None:
377
+ rich_print(f" len(final_code): {len(final_code)}")
378
+
379
+ if success and output_code_path and final_code is not None:
375
380
  try:
381
+ if verbose:
382
+ rich_print(f"[cyan bold DEBUG] In fix_verification_main, ATTEMPTING to write code to: {output_code_path!r}")
376
383
  with open(output_code_path, "w") as f:
377
384
  f.write(final_code)
378
385
  saved_code_path = output_code_path
379
386
  if not quiet:
380
387
  rich_print(f"Successfully verified code saved to: [green]{output_code_path}[/green]")
381
- except IOError as e:
382
- rich_print(f"[bold red]Error:[/bold red] Failed to write verified code file '{output_code_path}': {e}")
383
-
384
- if success and output_program_path:
388
+ except Exception as e:
389
+ rich_print(f"[bold red]Error:[/bold red] Failed to write code file '{output_code_path}': {type(e).__name__} - {e}")
390
+
391
+ if verbose:
392
+ rich_print(f"[cyan bold DEBUG] In fix_verification_main, BEFORE save attempt for PROGRAM:")
393
+ rich_print(f" success: {success}")
394
+ rich_print(f" output_program_path: {output_program_path!r}")
395
+ rich_print(f" final_program is None: {final_program is None}")
396
+ if final_program is not None:
397
+ rich_print(f" len(final_program): {len(final_program)}")
398
+
399
+ if success and output_program_path and final_program is not None:
385
400
  try:
401
+ if verbose:
402
+ rich_print(f"[cyan bold DEBUG] In fix_verification_main, ATTEMPTING to write program to: {output_program_path!r}")
386
403
  with open(output_program_path, "w") as f:
387
404
  f.write(final_program)
388
405
  saved_program_path = output_program_path
389
406
  if not quiet:
390
407
  rich_print(f"Successfully verified program saved to: [green]{output_program_path}[/green]")
391
- except IOError as e:
392
- rich_print(f"[bold red]Error:[/bold red] Failed to write verified program file '{output_program_path}': {e}")
408
+ except Exception as e:
409
+ rich_print(f"[bold red]Error:[/bold red] Failed to write program file '{output_program_path}': {type(e).__name__} - {e}")
393
410
 
394
- # Write results log (only for single pass, loop writes its own)
395
411
  if not loop and output_results_path:
396
412
  try:
397
413
  with open(output_results_path, "w") as f:
@@ -405,28 +421,40 @@ def fix_verification_main(
405
421
  # For loop, just confirm the path where the loop function *should* have saved the log
406
422
  saved_results_path = output_results_path
407
423
  if not quiet:
408
- rich_print(f"Verification results log (from loop) should be at: [green]{output_results_path}[/green]")
424
+ # We assume fix_verification_errors_loop handles its own logging confirmation.
425
+ # This message confirms the path was passed.
426
+ rich_print(f"Verification results log (from loop) expected at: [green]{output_results_path}[/green]")
427
+
409
428
 
410
429
  # --- Final User Feedback ---
430
+ if verbose:
431
+ rich_print(f"[cyan bold DEBUG] Before summary - saved_code_path: {saved_code_path!r}, output_code_path: {output_code_path!r}[/cyan bold DEBUG]")
432
+ rich_print(f"[cyan bold DEBUG] Before summary - saved_program_path: {saved_program_path!r}, output_program_path: {output_program_path!r}[/cyan bold DEBUG]")
433
+
411
434
  if not quiet:
412
435
  rich_print("\n" + "="*40)
413
436
  title = "[bold green]Verification Complete[/bold green]" if success else "[bold red]Verification Failed[/bold red]"
414
- summary_panel = Panel(
437
+ summary_panel_content = (
415
438
  f"Status: {'[green]Success[/green]' if success else '[red]Failure[/red]'}\n"
416
439
  f"Attempts: {attempts}\n"
417
440
  f"Total Cost: ${total_cost:.6f}\n"
418
441
  f"Model Used: {model_name}\n"
419
- f"Verified Code Saved: {saved_code_path or 'N/A'}\n"
420
- f"Verified Program Saved: {saved_program_path or 'N/A'}\n"
421
- f"Results Log Saved: {saved_results_path or 'N/A'}",
442
+ f"Verified Code Saved: {saved_code_path or 'N/A (Not saved on failure or no path)'}\n"
443
+ f"Verified Program Saved: {saved_program_path or 'N/A (Not saved on failure or no path)'}\n"
444
+ f"Results Log Saved: {saved_results_path or 'N/A'}"
445
+ )
446
+ summary_panel = Panel(
447
+ summary_panel_content,
422
448
  title=title,
423
449
  border_style="green" if success else "red"
424
450
  )
425
451
  rich_print(summary_panel)
426
452
 
427
- if verbose and not success and not loop:
428
- rich_print("[bold yellow]Final Code (after failed single pass):[/bold yellow]")
453
+ if verbose and not success and not loop: # Only show final code if verbose, failed, and single pass
454
+ rich_print("[bold yellow]Final Code (after failed single pass, not saved):[/bold yellow]")
429
455
  rich_print(Syntax(final_code, language or "python", theme="default", line_numbers=True))
456
+ rich_print("[bold yellow]Final Program (after failed single pass, not saved):[/bold yellow]")
457
+ rich_print(Syntax(final_program, language or "python", theme="default", line_numbers=True))
430
458
 
431
459
 
432
460
  return success, final_program, final_code, attempts, total_cost, model_name
@@ -0,0 +1,198 @@
1
+ from typing import Optional, Tuple
2
+ from pydantic import BaseModel, Field
3
+ from rich.console import Console
4
+ from rich.markdown import Markdown
5
+
6
+ from .llm_invoke import llm_invoke
7
+ from .load_prompt_template import load_prompt_template
8
+ from .preprocess import preprocess
9
+ from . import DEFAULT_STRENGTH # Removed unused EXTRACTION_STRENGTH
10
+
11
+ console = Console()
12
+
13
+ # Pydantic models for structured output
14
+ class DiffAnalysis(BaseModel):
15
+ is_big_change: bool = Field(description="Whether the change is considered significant enough to require full regeneration")
16
+ change_description: str = Field(description="A description of the changes between the original and new prompts")
17
+ analysis: str = Field(description="Detailed analysis of the differences and recommendation")
18
+
19
+ class CodePatchResult(BaseModel):
20
+ patched_code: str = Field(description="The updated code with incremental patches applied")
21
+ analysis: str = Field(description="Analysis of the patching process")
22
+ planned_modifications: str = Field(description="Description of the modifications planned and applied")
23
+
24
+ def incremental_code_generator(
25
+ original_prompt: str,
26
+ new_prompt: str,
27
+ existing_code: str,
28
+ language: str,
29
+ strength: float = DEFAULT_STRENGTH,
30
+ temperature: float = 0.0,
31
+ time: float = 0.25,
32
+ force_incremental: bool = False,
33
+ verbose: bool = False,
34
+ preprocess_prompt: bool = True
35
+ ) -> Tuple[Optional[str], bool, float, str]:
36
+ """
37
+ Analyzes changes to a prompt and either incrementally patches existing code or suggests full regeneration.
38
+
39
+ Args:
40
+ original_prompt (str): The original prompt used to generate the existing code.
41
+ new_prompt (str): The updated prompt that needs to be processed.
42
+ existing_code (str): The existing code generated from the original prompt.
43
+ language (str): The programming language of the output code (e.g., 'python', 'bash').
44
+ strength (float): Strength parameter for the LLM model (0 to 1). Defaults to DEFAULT_STRENGTH.
45
+ temperature (float): Temperature parameter for randomness in LLM output (0 to 1). Defaults to 0.0.
46
+ time (float): Thinking time or reasoning effort for the LLM model (0 to 1). Defaults to 0.25.
47
+ force_incremental (bool): Forces incremental patching even if full regeneration is suggested. Defaults to False.
48
+ verbose (bool): If True, prints detailed information about the process. Defaults to False.
49
+ preprocess_prompt (bool): If True, preprocesses the prompt before invocation. Defaults to True.
50
+
51
+ Returns:
52
+ Tuple[Optional[str], bool, float, str]: A tuple containing:
53
+ - updated_code (Optional[str]): The updated code if incremental patching is applied, None if full regeneration is needed.
54
+ - is_incremental (bool): True if incremental patching was applied, False if full regeneration is needed.
55
+ - total_cost (float): The total cost of all LLM invocations.
56
+ - model_name (str): The name of the LLM model used for the main operation.
57
+ """
58
+ # Validate inputs (moved outside the main try-except block)
59
+ if not original_prompt or not new_prompt or not existing_code or not language:
60
+ raise ValueError("All required inputs (original_prompt, new_prompt, existing_code, language) must be provided.")
61
+
62
+ if not 0 <= strength <= 1 or not 0 <= temperature <= 1 or not 0 <= time <= 1:
63
+ raise ValueError("Strength, temperature, and time must be between 0 and 1.")
64
+
65
+ try:
66
+ total_cost = 0.0
67
+ model_name = ""
68
+
69
+ # Step 1: Load and preprocess the diff_analyzer_LLM prompt template
70
+ diff_analyzer_template = load_prompt_template("diff_analyzer_LLM")
71
+ if preprocess_prompt:
72
+ diff_analyzer_template = preprocess(
73
+ diff_analyzer_template,
74
+ recursive=False,
75
+ double_curly_brackets=True,
76
+ exclude_keys=["ORIGINAL_PROMPT", "NEW_PROMPT", "EXISTING_CODE"]
77
+ )
78
+
79
+ if verbose:
80
+ console.print("[bold cyan]Step 1: Loaded diff_analyzer_LLM template[/bold cyan]")
81
+
82
+ # Step 2: Run diff_analyzer_LLM through llm_invoke
83
+ input_json = {
84
+ "ORIGINAL_PROMPT": original_prompt,
85
+ "NEW_PROMPT": new_prompt,
86
+ "EXISTING_CODE": existing_code
87
+ }
88
+ diff_response = llm_invoke(
89
+ prompt=diff_analyzer_template,
90
+ input_json=input_json,
91
+ strength=strength,
92
+ temperature=temperature,
93
+ time=time,
94
+ verbose=verbose,
95
+ output_pydantic=DiffAnalysis
96
+ )
97
+ diff_result: DiffAnalysis = diff_response['result']
98
+ total_cost += diff_response['cost']
99
+ model_name = diff_response['model_name'] # Initial model name
100
+
101
+ if verbose:
102
+ console.print("[bold green]Diff Analyzer Results:[/bold green]")
103
+ console.print(f"Is Big Change: {diff_result.is_big_change}")
104
+ console.print(Markdown(f"**Analysis:**\n{diff_result.analysis}"))
105
+ console.print(f"Cost so far: ${total_cost:.6f}")
106
+
107
+ # Step 3: Determine whether to use incremental patching or full regeneration
108
+ should_regenerate = not force_incremental and diff_result.is_big_change
109
+
110
+ if verbose and force_incremental and diff_result.is_big_change:
111
+ console.print("[bold yellow]Forcing incremental patching despite major change detection[/bold yellow]")
112
+
113
+ # Step 4: Handle regeneration or incremental patching
114
+ if should_regenerate:
115
+ if verbose:
116
+ console.print("[bold red]Major change detected. Recommending full regeneration.[/bold red]")
117
+ return None, False, total_cost, model_name
118
+ else:
119
+ # Load and preprocess the code_patcher_LLM prompt template
120
+ patcher_template = load_prompt_template("code_patcher_LLM")
121
+ if preprocess_prompt:
122
+ patcher_template = preprocess(
123
+ patcher_template,
124
+ recursive=False,
125
+ double_curly_brackets=True,
126
+ exclude_keys=["ORIGINAL_PROMPT", "NEW_PROMPT", "EXISTING_CODE", "CHANGE_DESCRIPTION"]
127
+ )
128
+
129
+ if verbose:
130
+ console.print("[bold cyan]Step 4: Loaded code_patcher_LLM template for incremental patching[/bold cyan]")
131
+
132
+ # Run code_patcher_LLM through llm_invoke
133
+ patch_input_json = {
134
+ "ORIGINAL_PROMPT": original_prompt,
135
+ "NEW_PROMPT": new_prompt,
136
+ "EXISTING_CODE": existing_code,
137
+ "CHANGE_DESCRIPTION": diff_result.change_description
138
+ }
139
+ patch_response = llm_invoke(
140
+ prompt=patcher_template,
141
+ input_json=patch_input_json,
142
+ strength=strength,
143
+ temperature=temperature,
144
+ time=time,
145
+ verbose=verbose,
146
+ output_pydantic=CodePatchResult
147
+ )
148
+ patch_result: CodePatchResult = patch_response['result']
149
+ total_cost += patch_response['cost']
150
+ model_name = patch_response['model_name'] # Update model_name to patcher's model
151
+
152
+ if verbose:
153
+ console.print("[bold green]Code Patcher Results:[/bold green]")
154
+ console.print(Markdown(f"**Analysis:**\n{patch_result.analysis}"))
155
+ console.print(Markdown(f"**Planned Modifications:**\n{patch_result.planned_modifications}"))
156
+ console.print(f"Total Cost: ${total_cost:.6f}")
157
+
158
+ return patch_result.patched_code, True, total_cost, model_name
159
+
160
+ except Exception as e:
161
+ # This will now catch errors from LLM calls or other unexpected runtime issues,
162
+ # not the initial input validation ValueErrors.
163
+ console.print(f"[bold red]Error in incremental_code_generator: {str(e)}[/bold red]")
164
+ raise RuntimeError(f"Failed to process incremental code generation: {str(e)}")
165
+
166
+ if __name__ == "__main__":
167
+ # Example usage for testing purposes
168
+ try:
169
+ original_prompt = "Write a Python function to calculate the factorial of a number."
170
+ new_prompt = "Write a Python function to calculate the factorial of a number with input validation."
171
+ existing_code = """
172
+ def factorial(n):
173
+ if n == 0 or n == 1:
174
+ return 1
175
+ return n * factorial(n - 1)
176
+ """
177
+ language = "python"
178
+ updated_code, is_incremental, total_cost, model_name = incremental_code_generator(
179
+ original_prompt=original_prompt,
180
+ new_prompt=new_prompt,
181
+ existing_code=existing_code,
182
+ language=language,
183
+ strength=DEFAULT_STRENGTH,
184
+ temperature=0.0,
185
+ time=0.25,
186
+ force_incremental=False,
187
+ verbose=True
188
+ )
189
+ console.print("[bold magenta]Final Results:[/bold magenta]")
190
+ if is_incremental:
191
+ console.print("[bold green]Incremental Patch Applied[/bold green]")
192
+ console.print(Markdown(f"**Updated Code:**\n```python\n{updated_code}\n```"))
193
+ else:
194
+ console.print("[bold red]Full Regeneration Recommended[/bold red]")
195
+ console.print(f"Total Cost: ${total_cost:.6f}")
196
+ console.print(f"Model Used: {model_name}")
197
+ except Exception as e:
198
+ console.print(f"[bold red]Test Error: {str(e)}[/bold red]")
@@ -14,7 +14,11 @@
14
14
  </output>
15
15
  </inputs_outputs_definitions>
16
16
 
17
- ```<./prompts/xml/change_example_partial_processed.prompt>```
17
+ <change_prompt_examples>
18
+ <include>
19
+ ../prompts/xml/change_example_partial_processed.prompt
20
+ </include>
21
+ </change_prompt_examples>
18
22
 
19
23
  <context>
20
24
  Here is the input_prompt to change: <input_prompt>{input_prompt}</input_prompt>
@@ -0,0 +1,63 @@
1
+ % You are an expert code patcher, tasked with incrementally updating existing code based on changes between two prompts.
2
+
3
+ % Given:
4
+ - The original prompt used to generate code
5
+ - A new, updated prompt
6
+ - The existing code generated from the original prompt
7
+ - A description of the changes needed to update the code
8
+ % Your task is to:
9
+ 1. Apply the necessary changes to the existing code while maintaining its overall structure
10
+ 2. Make only the modifications required to address the changes between the prompts
11
+ 3. Ensure the updated code is complete, correct, and meets all requirements in the new prompt
12
+ 4. Preserve the style, variable naming conventions, and organization of the original code
13
+
14
+ % Output a JSON object with:
15
+ - analysis: string - a detailed analysis of the changes needed
16
+ - planned_modifications: string - a detailed description of the modifications needed and why this change should not break the existing code
17
+ - patched_code: string - the complete updated code after applying all necessary changes
18
+
19
+ ORIGINAL_PROMPT:
20
+ <original_prompt>
21
+ {ORIGINAL_PROMPT}
22
+ </original_prompt>
23
+
24
+ NEW_PROMPT:
25
+ <new_prompt>
26
+ {NEW_PROMPT}
27
+ </new_prompt>
28
+
29
+ EXISTING_CODE:
30
+ <existing_code>
31
+ {EXISTING_CODE}
32
+ </existing_code>
33
+
34
+ CHANGE_DESCRIPTION:
35
+ <change_description>
36
+ {CHANGE_DESCRIPTION}
37
+ </change_description>
38
+
39
+ Based on the change description and differences between the prompts, carefully update the existing code.
40
+
41
+ Remember to:
42
+ 1. Only modify what's necessary to implement the changes
43
+ 2. Maintain consistency with the existing code style and structure
44
+ 3. Ensure all functionality from the original code still works correctly
45
+ 4. Implement all new requirements from the updated prompt
46
+ 5. Test the logic of your changes to ensure correctness
47
+ 6. Include appropriate error handling for any new code
48
+
49
+ First, analyze the existing code to understand its structure and functionality:
50
+ [Your code structure analysis]
51
+
52
+ Next, determine specific modifications needed based on the change description:
53
+ [Your planned modifications]
54
+
55
+ Now, implement those changes to create the updated code:
56
+
57
+ <output_json_example>
58
+ {{
59
+ "analysis": "A detailed analysis of the changes needed,
60
+ "planned_modifications": "A detailed description of the modifications needed and why this change should not break the existing code",
61
+ "patched_code": "YOUR COMPLETE UPDATED CODE HERE"
62
+ }}
63
+ </output_json_example>