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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. pdd/__init__.py +38 -6
  2. pdd/agentic_bug.py +323 -0
  3. pdd/agentic_bug_orchestrator.py +506 -0
  4. pdd/agentic_change.py +231 -0
  5. pdd/agentic_change_orchestrator.py +537 -0
  6. pdd/agentic_common.py +533 -770
  7. pdd/agentic_crash.py +2 -1
  8. pdd/agentic_e2e_fix.py +319 -0
  9. pdd/agentic_e2e_fix_orchestrator.py +582 -0
  10. pdd/agentic_fix.py +118 -3
  11. pdd/agentic_update.py +27 -9
  12. pdd/agentic_verify.py +3 -2
  13. pdd/architecture_sync.py +565 -0
  14. pdd/auth_service.py +210 -0
  15. pdd/auto_deps_main.py +63 -53
  16. pdd/auto_include.py +236 -3
  17. pdd/auto_update.py +125 -47
  18. pdd/bug_main.py +195 -23
  19. pdd/cmd_test_main.py +345 -197
  20. pdd/code_generator.py +4 -2
  21. pdd/code_generator_main.py +118 -32
  22. pdd/commands/__init__.py +6 -0
  23. pdd/commands/analysis.py +113 -48
  24. pdd/commands/auth.py +309 -0
  25. pdd/commands/connect.py +358 -0
  26. pdd/commands/fix.py +155 -114
  27. pdd/commands/generate.py +5 -0
  28. pdd/commands/maintenance.py +3 -2
  29. pdd/commands/misc.py +8 -0
  30. pdd/commands/modify.py +225 -163
  31. pdd/commands/sessions.py +284 -0
  32. pdd/commands/utility.py +12 -7
  33. pdd/construct_paths.py +334 -32
  34. pdd/context_generator_main.py +167 -170
  35. pdd/continue_generation.py +6 -3
  36. pdd/core/__init__.py +33 -0
  37. pdd/core/cli.py +44 -7
  38. pdd/core/cloud.py +237 -0
  39. pdd/core/dump.py +68 -20
  40. pdd/core/errors.py +4 -0
  41. pdd/core/remote_session.py +61 -0
  42. pdd/crash_main.py +219 -23
  43. pdd/data/llm_model.csv +4 -4
  44. pdd/docs/prompting_guide.md +864 -0
  45. pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
  46. pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
  47. pdd/fix_code_loop.py +208 -34
  48. pdd/fix_code_module_errors.py +6 -2
  49. pdd/fix_error_loop.py +291 -38
  50. pdd/fix_main.py +208 -6
  51. pdd/fix_verification_errors_loop.py +235 -26
  52. pdd/fix_verification_main.py +269 -83
  53. pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
  54. pdd/frontend/dist/assets/index-CUWd8al1.js +450 -0
  55. pdd/frontend/dist/index.html +376 -0
  56. pdd/frontend/dist/logo.svg +33 -0
  57. pdd/generate_output_paths.py +46 -5
  58. pdd/generate_test.py +212 -151
  59. pdd/get_comment.py +19 -44
  60. pdd/get_extension.py +8 -9
  61. pdd/get_jwt_token.py +309 -20
  62. pdd/get_language.py +8 -7
  63. pdd/get_run_command.py +7 -5
  64. pdd/insert_includes.py +2 -1
  65. pdd/llm_invoke.py +531 -97
  66. pdd/load_prompt_template.py +15 -34
  67. pdd/operation_log.py +342 -0
  68. pdd/path_resolution.py +140 -0
  69. pdd/postprocess.py +122 -97
  70. pdd/preprocess.py +68 -12
  71. pdd/preprocess_main.py +33 -1
  72. pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
  73. pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
  74. pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
  75. pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
  76. pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
  77. pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
  78. pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
  79. pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
  80. pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
  81. pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
  82. pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
  83. pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
  84. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +140 -0
  85. pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
  86. pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
  87. pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
  88. pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
  89. pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
  90. pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
  91. pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
  92. pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
  93. pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
  94. pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
  95. pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
  96. pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
  97. pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
  98. pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
  99. pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
  100. pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
  101. pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
  102. pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
  103. pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
  104. pdd/prompts/agentic_update_LLM.prompt +192 -338
  105. pdd/prompts/auto_include_LLM.prompt +22 -0
  106. pdd/prompts/change_LLM.prompt +3093 -1
  107. pdd/prompts/detect_change_LLM.prompt +571 -14
  108. pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
  109. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
  110. pdd/prompts/generate_test_LLM.prompt +19 -1
  111. pdd/prompts/generate_test_from_example_LLM.prompt +366 -0
  112. pdd/prompts/insert_includes_LLM.prompt +262 -252
  113. pdd/prompts/prompt_code_diff_LLM.prompt +123 -0
  114. pdd/prompts/prompt_diff_LLM.prompt +82 -0
  115. pdd/remote_session.py +876 -0
  116. pdd/server/__init__.py +52 -0
  117. pdd/server/app.py +335 -0
  118. pdd/server/click_executor.py +587 -0
  119. pdd/server/executor.py +338 -0
  120. pdd/server/jobs.py +661 -0
  121. pdd/server/models.py +241 -0
  122. pdd/server/routes/__init__.py +31 -0
  123. pdd/server/routes/architecture.py +451 -0
  124. pdd/server/routes/auth.py +364 -0
  125. pdd/server/routes/commands.py +929 -0
  126. pdd/server/routes/config.py +42 -0
  127. pdd/server/routes/files.py +603 -0
  128. pdd/server/routes/prompts.py +1347 -0
  129. pdd/server/routes/websocket.py +473 -0
  130. pdd/server/security.py +243 -0
  131. pdd/server/terminal_spawner.py +217 -0
  132. pdd/server/token_counter.py +222 -0
  133. pdd/summarize_directory.py +236 -237
  134. pdd/sync_animation.py +8 -4
  135. pdd/sync_determine_operation.py +329 -47
  136. pdd/sync_main.py +272 -28
  137. pdd/sync_orchestration.py +289 -211
  138. pdd/sync_order.py +304 -0
  139. pdd/template_expander.py +161 -0
  140. pdd/templates/architecture/architecture_json.prompt +41 -46
  141. pdd/trace.py +1 -1
  142. pdd/track_cost.py +0 -13
  143. pdd/unfinished_prompt.py +2 -1
  144. pdd/update_main.py +68 -26
  145. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/METADATA +15 -10
  146. pdd_cli-0.0.121.dist-info/RECORD +229 -0
  147. pdd_cli-0.0.90.dist-info/RECORD +0 -153
  148. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/WHEEL +0 -0
  149. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/entry_points.txt +0 -0
  150. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/licenses/LICENSE +0 -0
  151. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/top_level.txt +0 -0
@@ -3,14 +3,18 @@ import os
3
3
  import subprocess
4
4
  import click
5
5
  import logging
6
+ import json
6
7
  from pathlib import Path
7
8
  from typing import Optional, Tuple, List, Dict, Any
8
9
 
10
+ import requests
11
+
9
12
  # Use Rich for pretty printing to the console
10
13
  from rich import print as rich_print
11
14
  from rich.panel import Panel
12
15
  from rich.syntax import Syntax
13
16
  from rich.text import Text
17
+ from rich.console import Console
14
18
 
15
19
  # Internal imports using relative paths
16
20
  from .construct_paths import construct_paths
@@ -19,14 +23,27 @@ from .fix_verification_errors_loop import fix_verification_errors_loop
19
23
  # Import DEFAULT_STRENGTH from the main package
20
24
  from . import DEFAULT_STRENGTH, DEFAULT_TIME
21
25
  from .python_env_detector import detect_host_python_executable
26
+ from .core.cloud import CloudConfig
22
27
 
23
28
  # Default values from the README
24
29
  DEFAULT_TEMPERATURE = 0.0
25
30
  DEFAULT_MAX_ATTEMPTS = 3
26
31
  DEFAULT_BUDGET = 5.0
27
32
 
33
+ # Cloud request timeout
34
+ CLOUD_REQUEST_TIMEOUT = 400 # seconds
35
+
28
36
  # Configure logging
29
37
  logger = logging.getLogger(__name__)
38
+ console = Console()
39
+
40
+
41
+ def _env_flag_enabled(name: str) -> bool:
42
+ """Return True when an env var is set to a truthy value."""
43
+ value = os.environ.get(name)
44
+ if value is None:
45
+ return False
46
+ return str(value).strip().lower() in {"1", "true", "yes", "on"}
30
47
 
31
48
  # Define a constant for the verification program name
32
49
  VERIFICATION_PROGRAM_NAME = "verification_program.py" # Example, adjust if needed
@@ -274,30 +291,55 @@ def fix_verification_main(
274
291
  model_name: str = "N/A"
275
292
  results_log_content: str = ""
276
293
 
294
+ # Determine cloud vs local execution preference
295
+ is_local_execution_preferred = ctx.obj.get('local', False)
296
+ cloud_only = _env_flag_enabled("PDD_CLOUD_ONLY") or _env_flag_enabled("PDD_NO_LOCAL_FALLBACK")
297
+ current_execution_is_local = is_local_execution_preferred and not cloud_only
298
+
299
+ # Cloud execution tracking
300
+ cloud_execution_attempted = False
301
+ cloud_execution_succeeded = False
302
+
277
303
  try:
278
304
  if loop:
305
+ # Determine if loop should use cloud for LLM calls (hybrid mode)
306
+ # Local verification execution stays local, but LLM fix calls can go to cloud
307
+ use_cloud_for_loop = not is_local_execution_preferred and not cloud_only
308
+
309
+ # If cloud_only is set but we're in loop mode, we still use hybrid approach
310
+ if cloud_only and not is_local_execution_preferred:
311
+ use_cloud_for_loop = True
312
+
313
+ if verbose:
314
+ mode_desc = "hybrid (local execution + cloud LLM)" if use_cloud_for_loop else "local"
315
+ console.print(Panel(f"Performing {mode_desc} verification loop...", title="[blue]Mode[/blue]", expand=False))
316
+
279
317
  if not quiet:
280
318
  rich_print("[dim]Running Iterative Verification (fix_verification_errors_loop)...[/dim]")
281
319
  try:
320
+ # Build kwargs for fix_verification_errors_loop
321
+ loop_kwargs = {
322
+ "program_file": program_file,
323
+ "code_file": code_file,
324
+ "prompt": input_strings["prompt_file"],
325
+ "prompt_file": prompt_file,
326
+ "verification_program": verification_program,
327
+ "strength": strength,
328
+ "temperature": temperature,
329
+ "llm_time": time,
330
+ "max_attempts": max_attempts,
331
+ "budget": budget,
332
+ "verification_log_file": output_results_path,
333
+ "verbose": verbose,
334
+ "program_args": [],
335
+ "agentic_fallback": agentic_fallback,
336
+ }
337
+ # Only pass use_cloud when explicitly True (cloud not ready for prod yet)
338
+ if use_cloud_for_loop:
339
+ loop_kwargs["use_cloud"] = True
340
+
282
341
  # Call fix_verification_errors_loop for iterative fixing
283
- loop_results = fix_verification_errors_loop(
284
- program_file=program_file, # Changed to pass the program_file path
285
- code_file=code_file, # Changed to pass the code_file path
286
- prompt=input_strings["prompt_file"], # Correctly passing prompt content
287
- prompt_file=prompt_file,
288
- verification_program=verification_program, # Path to the verifier program
289
- strength=strength,
290
- temperature=temperature,
291
- llm_time=time, # Changed 'time' to 'llm_time'
292
- max_attempts=max_attempts,
293
- budget=budget,
294
- verification_log_file=output_results_path, # Use resolved output_results_path
295
- # output_code_path should not be passed here
296
- # output_program_path should not be passed here
297
- verbose=verbose,
298
- program_args=[], # Pass an empty list for program_args
299
- agentic_fallback=agentic_fallback,
300
- )
342
+ loop_results = fix_verification_errors_loop(**loop_kwargs)
301
343
  success = loop_results.get('success', False)
302
344
  final_program = loop_results.get('final_program', "") # Use .get for safety
303
345
  final_code = loop_results.get('final_code', "") # Use .get for safety
@@ -317,7 +359,7 @@ def fix_verification_main(
317
359
  rich_print("\n[bold blue]Running Single Pass Verification (fix_verification_errors)...[/bold blue]")
318
360
  attempts = 1 # Single pass is one attempt
319
361
 
320
- # 1. Run the program file to get its output
362
+ # 1. Run the program file to get its output (always local)
321
363
  if not quiet:
322
364
  rich_print(f"Executing program: [cyan]{program_file}[/cyan]")
323
365
  run_success, program_stdout, program_stderr = run_program(program_file)
@@ -328,71 +370,215 @@ def fix_verification_main(
328
370
  rich_print(Panel(program_output if program_output else "[No Output]", border_style="dim"))
329
371
  rich_print("[dim]--- End Program Output ---[/dim]")
330
372
 
331
- # Check if program ran successfully before calling LLM (optional, but good practice)
332
- # if not run_success:
333
- # rich_print("[yellow]Warning:[/yellow] Program execution failed. LLM verification might be less effective.")
334
-
335
- # 2. Call fix_verification_errors with content and program output
336
- if not quiet:
337
- rich_print("Calling LLM to verify program output against prompt...")
338
- fix_results = fix_verification_errors(
339
- program=input_strings["program_file"],
340
- prompt=input_strings["prompt_file"],
341
- code=input_strings["code_file"],
342
- output=program_output,
343
- strength=strength,
344
- temperature=temperature,
345
- verbose=verbose,
346
- time=time # Pass time to single pass function
347
- )
348
-
349
- # Determine success: If no issues were found OR if fixes were applied
350
- # The definition of 'success' here means the *final* state is verified.
351
- issues_found = fix_results['verification_issues_count'] > 0
352
- code_updated = fix_results['fixed_code'] != input_strings["code_file"]
353
- program_updated = fix_results['fixed_program'] != input_strings["program_file"]
354
-
355
- if not issues_found:
356
- success = True
357
- if not quiet: rich_print("[green]Verification Passed:[/green] LLM found no discrepancies.")
358
- elif code_updated or program_updated:
359
- # If issues were found AND fixes were made, assume success for this single pass.
360
- # A more robust check might re-run the program with fixed code, but that's the loop's job.
361
- success = True
362
- if not quiet: rich_print("[yellow]Verification Issues Found:[/yellow] LLM proposed fixes.")
363
- else:
364
- success = False
365
- if not quiet: rich_print("[red]Verification Failed:[/red] LLM found discrepancies but proposed no fixes.")
366
-
367
- final_program = fix_results['fixed_program']
368
- final_code = fix_results['fixed_code']
369
- total_cost = fix_results['total_cost']
370
- model_name = fix_results['model_name']
371
-
372
- # Build results log content for single pass
373
- results_log_content = "PDD Verify Results (Single Pass)\n"
374
- results_log_content += f"Timestamp: {os.path.getmtime(prompt_file)}\n"
375
- results_log_content += f"Prompt File: {prompt_file}\n"
376
- results_log_content += f"Code File: {code_file}\n"
377
- results_log_content += f"Program File: {program_file}\n"
378
- results_log_content += f"Success: {success}\n"
379
- results_log_content += f"Issues Found Count: {fix_results['verification_issues_count']}\n"
380
- results_log_content += f"Code Updated: {code_updated}\n"
381
- results_log_content += f"Program Updated: {program_updated}\n"
382
- results_log_content += f"Model Used: {model_name}\n"
383
- results_log_content += f"Total Cost: ${total_cost:.6f}\n"
384
- results_log_content += "\n--- LLM Explanation ---\n"
385
- # The original code here was:
386
- # results_log_content += "\n".join(fix_results.get('explanation', ['N/A']))
387
- # This was incorrect because fix_results['explanation'] is a single string.
388
- # The list constructor would then iterate through it character-by-character,
389
- # causing the single-character-per-line output.
390
- # The fix is to just append the string directly, using a default value if it is None.
391
- results_log_content += fix_results.get('explanation') or 'N/A'
392
- results_log_content += "\n\n--- Program Output Used for Verification ---\n"
393
- results_log_content += program_output
394
-
373
+ # 2. Attempt cloud verification first if not local preferred
374
+ if not current_execution_is_local:
375
+ if verbose:
376
+ console.print(Panel("Attempting cloud verification execution...", title="[blue]Mode[/blue]", expand=False))
377
+
378
+ jwt_token = CloudConfig.get_jwt_token(verbose=verbose)
379
+
380
+ if not jwt_token:
381
+ if cloud_only:
382
+ console.print("[red]Cloud authentication failed.[/red]")
383
+ raise click.UsageError("Cloud authentication failed")
384
+ console.print("[yellow]Cloud authentication failed. Falling back to local execution.[/yellow]")
385
+ current_execution_is_local = True
386
+
387
+ if jwt_token and not current_execution_is_local:
388
+ cloud_execution_attempted = True
389
+ # Build cloud payload
390
+ payload = {
391
+ "programContent": input_strings["program_file"],
392
+ "promptContent": input_strings["prompt_file"],
393
+ "codeContent": input_strings["code_file"],
394
+ "outputContent": program_output,
395
+ "language": language,
396
+ "strength": strength,
397
+ "temperature": temperature,
398
+ "time": time if time is not None else 0.25,
399
+ "verbose": verbose,
400
+ }
401
+
402
+ headers = {
403
+ "Authorization": f"Bearer {jwt_token}",
404
+ "Content-Type": "application/json"
405
+ }
406
+ cloud_url = CloudConfig.get_endpoint_url("verifyCode")
407
+
408
+ try:
409
+ response = requests.post(
410
+ cloud_url,
411
+ json=payload,
412
+ headers=headers,
413
+ timeout=CLOUD_REQUEST_TIMEOUT
414
+ )
415
+ response.raise_for_status()
416
+
417
+ response_data = response.json()
418
+ fixed_code = response_data.get("fixedCode", "")
419
+ fixed_program = response_data.get("fixedProgram", "")
420
+ explanation = response_data.get("explanation", "")
421
+ issues_count = response_data.get("issuesCount", 0)
422
+ total_cost = float(response_data.get("totalCost", 0.0))
423
+ model_name = response_data.get("modelName", "cloud_model")
424
+
425
+ cloud_execution_succeeded = True
426
+
427
+ # Determine success based on issues count
428
+ code_updated = fixed_code != input_strings["code_file"]
429
+ program_updated = fixed_program != input_strings["program_file"]
430
+
431
+ if issues_count == 0:
432
+ success = True
433
+ if not quiet: rich_print("[green]Verification Passed:[/green] Cloud found no discrepancies.")
434
+ elif code_updated or program_updated:
435
+ success = True
436
+ if not quiet: rich_print("[yellow]Verification Issues Found:[/yellow] Cloud proposed fixes.")
437
+ else:
438
+ success = False
439
+ if not quiet: rich_print("[red]Verification Failed:[/red] Cloud found discrepancies but proposed no fixes.")
440
+
441
+ final_program = fixed_program
442
+ final_code = fixed_code
443
+
444
+ # Build results log content for cloud execution
445
+ results_log_content = "PDD Verify Results (Cloud Single Pass)\n"
446
+ results_log_content += f"Prompt File: {prompt_file}\n"
447
+ results_log_content += f"Code File: {code_file}\n"
448
+ results_log_content += f"Program File: {program_file}\n"
449
+ results_log_content += f"Success: {success}\n"
450
+ results_log_content += f"Issues Found Count: {issues_count}\n"
451
+ results_log_content += f"Code Updated: {code_updated}\n"
452
+ results_log_content += f"Program Updated: {program_updated}\n"
453
+ results_log_content += f"Model Used: {model_name}\n"
454
+ results_log_content += f"Total Cost: ${total_cost:.6f}\n"
455
+ results_log_content += "\n--- LLM Explanation ---\n"
456
+ results_log_content += explanation or 'N/A'
457
+ results_log_content += "\n\n--- Program Output Used for Verification ---\n"
458
+ results_log_content += program_output
459
+
460
+ if verbose:
461
+ console.print(Panel(
462
+ f"Cloud verification completed. Model: {model_name}, Cost: ${total_cost:.6f}",
463
+ title="[green]Cloud Success[/green]",
464
+ expand=False
465
+ ))
466
+
467
+ except requests.exceptions.Timeout:
468
+ if cloud_only:
469
+ console.print(f"[red]Cloud execution timed out ({CLOUD_REQUEST_TIMEOUT}s).[/red]")
470
+ raise click.UsageError("Cloud execution timed out")
471
+ console.print(f"[yellow]Cloud execution timed out ({CLOUD_REQUEST_TIMEOUT}s). Falling back to local.[/yellow]")
472
+ current_execution_is_local = True
473
+
474
+ except requests.exceptions.HTTPError as e:
475
+ status_code = e.response.status_code if e.response else 0
476
+ err_content = e.response.text[:200] if e.response else "No response content"
477
+
478
+ # Non-recoverable errors: do NOT fall back to local
479
+ if status_code == 402: # Insufficient credits
480
+ try:
481
+ error_data = e.response.json()
482
+ current_balance = error_data.get("currentBalance", "unknown")
483
+ estimated_cost = error_data.get("estimatedCost", "unknown")
484
+ console.print(f"[red]Insufficient credits. Current balance: {current_balance}, estimated cost: {estimated_cost}[/red]")
485
+ except Exception:
486
+ console.print(f"[red]Insufficient credits: {err_content}[/red]")
487
+ raise click.UsageError("Insufficient credits for cloud verification")
488
+ elif status_code == 401: # Authentication error
489
+ console.print(f"[red]Authentication failed: {err_content}[/red]")
490
+ raise click.UsageError("Cloud authentication failed")
491
+ elif status_code == 403: # Authorization error (not approved)
492
+ console.print(f"[red]Access denied: {err_content}[/red]")
493
+ raise click.UsageError("Access denied - user not approved")
494
+ elif status_code == 400: # Validation error
495
+ console.print(f"[red]Invalid request: {err_content}[/red]")
496
+ raise click.UsageError(f"Invalid request: {err_content}")
497
+ else:
498
+ # Recoverable errors (5xx, unexpected errors): fall back to local
499
+ if cloud_only:
500
+ console.print(f"[red]Cloud HTTP error ({status_code}): {err_content}[/red]")
501
+ raise click.UsageError(f"Cloud HTTP error ({status_code}): {err_content}")
502
+ console.print(f"[yellow]Cloud HTTP error ({status_code}): {err_content}. Falling back to local.[/yellow]")
503
+ current_execution_is_local = True
504
+
505
+ except requests.exceptions.RequestException as e:
506
+ if cloud_only:
507
+ console.print(f"[red]Cloud network error: {e}[/red]")
508
+ raise click.UsageError(f"Cloud network error: {e}")
509
+ console.print(f"[yellow]Cloud network error: {e}. Falling back to local.[/yellow]")
510
+ current_execution_is_local = True
511
+
512
+ except json.JSONDecodeError:
513
+ if cloud_only:
514
+ console.print("[red]Cloud returned invalid JSON.[/red]")
515
+ raise click.UsageError("Cloud returned invalid JSON")
516
+ console.print("[yellow]Cloud returned invalid JSON. Falling back to local.[/yellow]")
517
+ current_execution_is_local = True
518
+
519
+ # Local execution path (when cloud failed/skipped or local preferred)
520
+ if not cloud_execution_succeeded:
521
+ if verbose:
522
+ console.print(Panel("Performing local verification...", title="[blue]Mode[/blue]", expand=False))
523
+
524
+ # Call fix_verification_errors with content and program output
525
+ if not quiet:
526
+ rich_print("Calling LLM to verify program output against prompt...")
527
+ fix_results = fix_verification_errors(
528
+ program=input_strings["program_file"],
529
+ prompt=input_strings["prompt_file"],
530
+ code=input_strings["code_file"],
531
+ output=program_output,
532
+ strength=strength,
533
+ temperature=temperature,
534
+ verbose=verbose,
535
+ time=time # Pass time to single pass function
536
+ )
395
537
 
538
+ # Determine success: If no issues were found OR if fixes were applied
539
+ # The definition of 'success' here means the *final* state is verified.
540
+ issues_found = fix_results['verification_issues_count'] > 0
541
+ code_updated = fix_results['fixed_code'] != input_strings["code_file"]
542
+ program_updated = fix_results['fixed_program'] != input_strings["program_file"]
543
+
544
+ if not issues_found:
545
+ success = True
546
+ if not quiet: rich_print("[green]Verification Passed:[/green] LLM found no discrepancies.")
547
+ elif code_updated or program_updated:
548
+ # If issues were found AND fixes were made, assume success for this single pass.
549
+ # A more robust check might re-run the program with fixed code, but that's the loop's job.
550
+ success = True
551
+ if not quiet: rich_print("[yellow]Verification Issues Found:[/yellow] LLM proposed fixes.")
552
+ else:
553
+ success = False
554
+ if not quiet: rich_print("[red]Verification Failed:[/red] LLM found discrepancies but proposed no fixes.")
555
+
556
+ final_program = fix_results['fixed_program']
557
+ final_code = fix_results['fixed_code']
558
+ total_cost = fix_results['total_cost']
559
+ model_name = fix_results['model_name']
560
+
561
+ # Build results log content for single pass
562
+ results_log_content = "PDD Verify Results (Single Pass)\n"
563
+ results_log_content += f"Timestamp: {os.path.getmtime(prompt_file)}\n"
564
+ results_log_content += f"Prompt File: {prompt_file}\n"
565
+ results_log_content += f"Code File: {code_file}\n"
566
+ results_log_content += f"Program File: {program_file}\n"
567
+ results_log_content += f"Success: {success}\n"
568
+ results_log_content += f"Issues Found Count: {fix_results['verification_issues_count']}\n"
569
+ results_log_content += f"Code Updated: {code_updated}\n"
570
+ results_log_content += f"Program Updated: {program_updated}\n"
571
+ results_log_content += f"Model Used: {model_name}\n"
572
+ results_log_content += f"Total Cost: ${total_cost:.6f}\n"
573
+ results_log_content += "\n--- LLM Explanation ---\n"
574
+ results_log_content += fix_results.get('explanation') or 'N/A'
575
+ results_log_content += "\n\n--- Program Output Used for Verification ---\n"
576
+ results_log_content += program_output
577
+
578
+
579
+ except click.UsageError:
580
+ # Re-raise UsageError for proper CLI handling (e.g., cloud auth failures, insufficient credits)
581
+ raise
396
582
  except Exception as e:
397
583
  success = False
398
584
  rich_print(f"[bold red]Error during verification process:[/bold red] {e}")
@@ -0,0 +1 @@
1
+ .react-flow{direction:ltr}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;cursor:-webkit-grab;cursor:grab}.react-flow__pane.selection{cursor:pointer}.react-flow__pane.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow .react-flow__edges{pointer-events:none;overflow:visible}.react-flow__edge-path,.react-flow__connection-path{stroke:#b1b1b7;stroke-width:1;fill:none}.react-flow__edge{pointer-events:visibleStroke;cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;-webkit-animation:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge:focus .react-flow__edge-path,.react-flow__edge:focus-visible .react-flow__edge-path{stroke:#555}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge-textbg{fill:#fff}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;-webkit-animation:dashdraw .5s linear infinite;animation:dashdraw .5s linear infinite}.react-flow__connectionline{z-index:1001}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:-webkit-grab;cursor:grab}.react-flow__node.dragging{cursor:-webkit-grabbing;cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:-webkit-grab;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background:#1a192b;border:1px solid white;border-radius:100%}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:-4px;transform:translate(-50%)}.react-flow__handle-top{left:50%;top:-4px;transform:translate(-50%)}.react-flow__handle-left{top:50%;left:-4px;transform:translateY(-50%)}.react-flow__handle-right{right:-4px;top:50%;transform:translateY(-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.center{left:50%;transform:translate(-50%)}.react-flow__attribution{font-size:10px;background:#ffffff80;padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@-webkit-keyframes dashdraw{0%{stroke-dashoffset:10}}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-default,.react-flow__node-input,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:3px;width:150px;font-size:12px;color:#222;text-align:center;border-width:1px;border-style:solid;border-color:#1a192b;background-color:#fff}.react-flow__node-default.selectable:hover,.react-flow__node-input.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:0 1px 4px 1px #00000014}.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:0 0 0 .5px #1a192b}.react-flow__node-group{background-color:#f0f0f040}.react-flow__nodesselection-rect,.react-flow__selection{background:#0059dc14;border:1px dotted rgba(0,89,220,.8)}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls{box-shadow:0 0 2px 1px #00000014}.react-flow__controls-button{border:none;background:#fefefe;border-bottom:1px solid #eee;box-sizing:content-box;display:flex;justify-content:center;align-items:center;width:16px;height:16px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:5px}.react-flow__controls-button:hover{background:#f4f4f4}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__minimap{background-color:#fff}.react-flow__minimap svg{display:block}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:#3367d9;transform:translate(-50%,-50%)}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:#3367d9;border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}