weco 0.3.4__py3-none-any.whl → 0.3.5__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.
weco/optimizer.py CHANGED
@@ -144,17 +144,25 @@ def execute_optimization(
144
144
  """
145
145
  if console is None:
146
146
  console = Console()
147
-
148
147
  # Global variables for this optimization run
149
148
  heartbeat_thread = None
150
149
  stop_heartbeat_event = threading.Event()
151
150
  current_run_id_for_heartbeat = None
152
151
  current_auth_headers_for_heartbeat = {}
152
+ live_ref = None # Reference to the Live object for the optimization run
153
+
154
+ best_solution_code = None
155
+ original_source_code = None
153
156
 
154
157
  # --- Signal Handler for this optimization run ---
155
158
  def signal_handler(signum, frame):
159
+ nonlocal live_ref
160
+
161
+ if live_ref is not None:
162
+ live_ref.stop() # Stop the live update loop so that messages are printed to the console
163
+
156
164
  signal_name = signal.Signals(signum).name
157
- console.print(f"\n[bold yellow]Termination signal ({signal_name}) received. Shutting down...[/]")
165
+ console.print(f"\n[bold yellow]Termination signal ({signal_name}) received. Shutting down...[/]\n")
158
166
 
159
167
  # Stop heartbeat thread
160
168
  stop_heartbeat_event.set()
@@ -170,7 +178,7 @@ def execute_optimization(
170
178
  details=f"Process terminated by signal {signal_name} ({signum}).",
171
179
  auth_headers=current_auth_headers_for_heartbeat,
172
180
  )
173
- console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {current_run_id_for_heartbeat}[/]")
181
+ console.print(f"[cyan]To resume this run, use:[/] [bold cyan]weco resume {current_run_id_for_heartbeat}[/]\n")
174
182
 
175
183
  # Exit gracefully
176
184
  sys.exit(0)
@@ -183,8 +191,6 @@ def execute_optimization(
183
191
  optimization_completed_normally = False
184
192
  user_stop_requested_flag = False
185
193
 
186
- best_solution_code = None
187
- original_source_code = None # Make available to the finally block
188
194
  try:
189
195
  # --- Login/Authentication Handling (now mandatory) ---
190
196
  weco_api_key, auth_headers = handle_authentication(console)
@@ -256,6 +262,7 @@ def execute_optimization(
256
262
  # --- Live Update Loop ---
257
263
  refresh_rate = 4
258
264
  with Live(layout, refresh_per_second=refresh_rate) as live:
265
+ live_ref = live
259
266
  # Define the runs directory (.runs/<run-id>) to store logs and results
260
267
  runs_dir = pathlib.Path(log_dir) / run_id
261
268
  runs_dir.mkdir(parents=True, exist_ok=True)
@@ -387,6 +394,16 @@ def execute_optimization(
387
394
  status_response=status_response, solution_id=eval_and_next_solution_response["solution_id"]
388
395
  )
389
396
 
397
+ # Set best solution and save optimization results
398
+ try:
399
+ best_solution_code = best_solution_node.code
400
+ except AttributeError:
401
+ # Can happen if the code was buggy
402
+ best_solution_code = read_from_path(fp=runs_dir / f"step_0{source_fp.suffix}", is_json=False)
403
+
404
+ # Save best solution to .runs/<run-id>/best.<extension>
405
+ write_to_path(fp=runs_dir / f"best{source_fp.suffix}", content=best_solution_code)
406
+
390
407
  # Update the solution panels with the current and best solution
391
408
  solution_panels.update(current_node=current_solution_node, best_node=best_solution_node)
392
409
  current_solution_panel, best_solution_panel = solution_panels.get_display(current_step=step)
@@ -443,10 +460,13 @@ def execute_optimization(
443
460
  )
444
461
  # No need to set any solution to unevaluated since we have finished the optimization
445
462
  # and all solutions have been evaluated
446
- # No neeed to update the current solution panel since we have finished the optimization
463
+ # No need to update the current solution panel since we have finished the optimization
447
464
  # We only need to update the best solution panel
448
465
  # Figure out if we have a best solution so far
449
466
  best_solution_node = get_best_node_from_status(status_response=status_response)
467
+ best_solution_code = best_solution_node.code
468
+ # Save best solution to .runs/<run-id>/best.<extension>
469
+ write_to_path(fp=runs_dir / f"best{source_fp.suffix}", content=best_solution_code)
450
470
  solution_panels.update(current_node=None, best_node=best_solution_node)
451
471
  _, best_solution_panel = solution_panels.get_display(current_step=steps)
452
472
  # Update the end optimization layout
@@ -459,17 +479,6 @@ def execute_optimization(
459
479
  end_optimization_layout["tree"].update(tree_panel.get_display(is_done=True))
460
480
  end_optimization_layout["best_solution"].update(best_solution_panel)
461
481
 
462
- # Save optimization results
463
- # If the best solution does not exist or is has not been measured at the end of the optimization
464
- # save the original solution as the best solution
465
- try:
466
- best_solution_code = best_solution_node.code
467
- except AttributeError:
468
- best_solution_code = read_from_path(fp=runs_dir / f"step_0{source_fp.suffix}", is_json=False)
469
-
470
- # Save best solution to .runs/<run-id>/best.<extension>
471
- write_to_path(fp=runs_dir / f"best{source_fp.suffix}", content=best_solution_code)
472
-
473
482
  # Mark as completed normally for the finally block
474
483
  optimization_completed_normally = True
475
484
  live.update(end_optimization_layout)
@@ -481,7 +490,7 @@ def execute_optimization(
481
490
  except Exception:
482
491
  error_message = str(e)
483
492
  console.print(Panel(f"[bold red]Error: {error_message}", title="[bold red]Optimization Error", border_style="red"))
484
- console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {run_id}[/]")
493
+ console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {run_id}[/]\n")
485
494
  # Ensure optimization_completed_normally is False
486
495
  optimization_completed_normally = False
487
496
  finally:
@@ -508,20 +517,16 @@ def execute_optimization(
508
517
  else "CLI terminated unexpectedly without a specific exception captured."
509
518
  )
510
519
 
511
- # raise Exception(best_solution_code, original_source_code)
512
520
  if best_solution_code and best_solution_code != original_source_code:
513
521
  # Determine whether to apply: automatically if --apply-change is set, otherwise ask user
514
- should_apply = apply_change or summary_panel.ask_user_feedback(
515
- live=live,
516
- layout=end_optimization_layout,
517
- question="Would you like to apply the best solution to the source file?",
518
- default=True,
522
+ should_apply = apply_change or Confirm.ask(
523
+ "Would you like to apply the best solution to the source file?", default=True
519
524
  )
520
525
  if should_apply:
521
526
  write_to_path(fp=source_fp, content=best_solution_code)
522
- console.print("[green]Best solution applied to the source file.[/]\n")
527
+ console.print("\n[green]Best solution applied to the source file.[/]\n")
523
528
  else:
524
- console.print("[green]A better solution was not found. No changes to apply.[/]\n")
529
+ console.print("\n[green]A better solution was not found. No changes to apply.[/]\n")
525
530
 
526
531
  report_termination(
527
532
  run_id=run_id,
@@ -534,7 +539,7 @@ def execute_optimization(
534
539
  # Handle exit
535
540
  if user_stop_requested_flag:
536
541
  console.print("[yellow]Run terminated by user request.[/]")
537
- console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {run_id}[/]")
542
+ console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {run_id}[/]\n")
538
543
 
539
544
  return optimization_completed_normally or user_stop_requested_flag
540
545
 
@@ -549,11 +554,19 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
549
554
  stop_heartbeat_event = threading.Event()
550
555
  current_run_id_for_heartbeat = None
551
556
  current_auth_headers_for_heartbeat = {}
557
+ live_ref = None # Reference to the Live object for the optimization run
558
+
559
+ best_solution_code = None
560
+ original_source_code = None
552
561
 
553
562
  # Signal handler for this optimization run
554
563
  def signal_handler(signum, frame):
564
+ nonlocal live_ref
565
+ if live_ref is not None:
566
+ live_ref.stop() # Stop the live update loop so that messages are printed to the console
567
+
555
568
  signal_name = signal.Signals(signum).name
556
- console.print(f"\n[bold yellow]Termination signal ({signal_name}) received. Shutting down...[/]")
569
+ console.print(f"\n[bold yellow]Termination signal ({signal_name}) received. Shutting down...[/]\n")
557
570
  stop_heartbeat_event.set()
558
571
  if heartbeat_thread and heartbeat_thread.is_alive():
559
572
  heartbeat_thread.join(timeout=2)
@@ -565,7 +578,7 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
565
578
  details=f"Process terminated by signal {signal_name} ({signum}).",
566
579
  auth_headers=current_auth_headers_for_heartbeat,
567
580
  )
568
- console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {current_run_id_for_heartbeat}[/]")
581
+ console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {current_run_id_for_heartbeat}[/]\n")
569
582
  sys.exit(0)
570
583
 
571
584
  # Set up signal handlers for this run
@@ -575,9 +588,6 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
575
588
  optimization_completed_normally = False
576
589
  user_stop_requested_flag = False
577
590
 
578
- best_solution_code = None
579
- original_source_code = None
580
-
581
591
  try:
582
592
  # --- Login/Authentication Handling (now mandatory) ---
583
593
  weco_api_key, auth_headers = handle_authentication(console)
@@ -659,9 +669,8 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
659
669
  source_fp.parent.mkdir(parents=True, exist_ok=True)
660
670
  # Store the original content to restore after each evaluation
661
671
  original_source_code = read_from_path(fp=source_fp, is_json=False) if source_fp.exists() else ""
662
-
672
+ # The code to restore is the code from the last step of the previous run
663
673
  code_to_restore = resume_resp.get("code") or resume_resp.get("source_code") or ""
664
- write_to_path(fp=source_fp, content=code_to_restore)
665
674
 
666
675
  # Prepare UI panels
667
676
  summary_panel = SummaryPanel(
@@ -687,12 +696,25 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
687
696
  best_solution_node = get_best_node_from_status(status_response=status)
688
697
  current_solution_node = get_node_from_status(status_response=status, solution_id=resume_resp.get("solution_id"))
689
698
 
699
+ # If there's no best solution yet (baseline evaluation didn't complete),
700
+ # mark the current node as unevaluated so the tree renders correctly
701
+ if best_solution_node is None:
702
+ tree_panel.set_unevaluated_node(node_id=resume_resp.get("solution_id"))
703
+
690
704
  # Ensure runs dir exists
691
705
  runs_dir = pathlib.Path(log_dir) / resume_resp["run_id"]
692
706
  runs_dir.mkdir(parents=True, exist_ok=True)
693
707
  # Persist last step's code into logs as step_<current_step>
694
708
  write_to_path(fp=runs_dir / f"step_{current_step}{source_fp.suffix}", content=code_to_restore)
695
709
 
710
+ # Initialize best solution code
711
+ try:
712
+ best_solution_code = best_solution_node.code
713
+ except AttributeError:
714
+ # Edge case: best solution node is not available.
715
+ # This can happen if the user has cancelled the run before even running the baseline solution
716
+ pass # Leave best solution code as None
717
+
696
718
  # Start Heartbeat Thread
697
719
  stop_heartbeat_event.clear()
698
720
  heartbeat_thread = HeartbeatSender(resume_resp["run_id"], auth_headers, stop_heartbeat_event)
@@ -705,6 +727,7 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
705
727
  # --- Live UI ---
706
728
  refresh_rate = 4
707
729
  with Live(layout, refresh_per_second=refresh_rate) as live:
730
+ live_ref = live
708
731
  # Initial panels
709
732
  current_solution_panel, best_solution_panel = solution_panels.get_display(current_step=current_step)
710
733
  # Use backend-provided execution output only (no fallback)
@@ -788,6 +811,16 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
788
811
  status_response=status_response, solution_id=eval_and_next_solution_response["solution_id"]
789
812
  )
790
813
 
814
+ # Set best solution and save optimization results
815
+ try:
816
+ best_solution_code = best_solution_node.code
817
+ except AttributeError:
818
+ # Can happen if the code was buggy
819
+ best_solution_code = read_from_path(fp=runs_dir / f"step_0{source_fp.suffix}", is_json=False)
820
+
821
+ # Save best solution to .runs/<run-id>/best.<extension>
822
+ write_to_path(fp=runs_dir / f"best{source_fp.suffix}", content=best_solution_code)
823
+
791
824
  solution_panels.update(current_node=current_solution_node, best_node=best_solution_node)
792
825
  current_solution_panel, best_solution_panel = solution_panels.get_display(current_step=step)
793
826
  eval_output_panel.clear()
@@ -839,6 +872,10 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
839
872
  tree_panel.build_metric_tree(nodes=nodes_final)
840
873
  # Best solution panel and final message
841
874
  best_solution_node = get_best_node_from_status(status_response=status_response)
875
+ best_solution_code = best_solution_node.code
876
+ # Save best solution to .runs/<run-id>/best.<extension>
877
+ write_to_path(fp=runs_dir / f"best{source_fp.suffix}", content=best_solution_code)
878
+
842
879
  solution_panels.update(current_node=None, best_node=best_solution_node)
843
880
  _, best_solution_panel = solution_panels.get_display(current_step=total_steps)
844
881
  final_message = (
@@ -850,14 +887,6 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
850
887
  end_optimization_layout["tree"].update(tree_panel.get_display(is_done=True))
851
888
  end_optimization_layout["best_solution"].update(best_solution_panel)
852
889
 
853
- # Save best
854
- try:
855
- best_solution_code = best_solution_node.code
856
- except AttributeError:
857
- best_solution_code = read_from_path(fp=runs_dir / f"step_0{source_fp.suffix}", is_json=False)
858
-
859
- write_to_path(fp=runs_dir / f"best{source_fp.suffix}", content=best_solution_code)
860
-
861
890
  optimization_completed_normally = True
862
891
  live.update(end_optimization_layout)
863
892
 
@@ -867,7 +896,7 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
867
896
  except Exception:
868
897
  error_message = str(e)
869
898
  console.print(Panel(f"[bold red]Error: {error_message}", title="[bold red]Optimization Error", border_style="red"))
870
- console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {run_id}[/]")
899
+ console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {run_id}[/]\n")
871
900
  optimization_completed_normally = False
872
901
  finally:
873
902
  signal.signal(signal.SIGINT, original_sigint_handler)
@@ -896,17 +925,14 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
896
925
  )
897
926
 
898
927
  if best_solution_code and best_solution_code != original_source_code:
899
- should_apply = apply_change or summary_panel.ask_user_feedback(
900
- live=live,
901
- layout=end_optimization_layout,
902
- question="Would you like to apply the best solution to the source file?",
903
- default=True,
928
+ should_apply = apply_change or Confirm.ask(
929
+ "Would you like to apply the best solution to the source file?", default=True
904
930
  )
905
931
  if should_apply:
906
932
  write_to_path(fp=source_fp, content=best_solution_code)
907
- console.print("[green]Best solution applied to the source file.[/]\n")
933
+ console.print("\n[green]Best solution applied to the source file.[/]\n")
908
934
  else:
909
- console.print("[green]A better solution was not found. No changes to apply.[/]\n")
935
+ console.print("\n[green]A better solution was not found. No changes to apply.[/]\n")
910
936
 
911
937
  report_termination(
912
938
  run_id=run_id,
@@ -917,5 +943,5 @@ def resume_optimization(run_id: str, console: Optional[Console] = None, apply_ch
917
943
  )
918
944
  if user_stop_requested_flag:
919
945
  console.print("[yellow]Run terminated by user request.[/]")
920
- console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {run_id}[/]")
946
+ console.print(f"\n[cyan]To resume this run, use:[/] [bold cyan]weco resume {run_id}[/]\n")
921
947
  return optimization_completed_normally or user_stop_requested_flag
weco/panels.py CHANGED
@@ -6,9 +6,7 @@ from rich.panel import Panel
6
6
  from rich.syntax import Syntax
7
7
  from rich import box
8
8
  from rich.console import Console
9
- from rich.live import Live
10
9
 
11
- from rich.prompt import Confirm
12
10
  from typing import Dict, List, Optional, Union, Tuple
13
11
  from pathlib import Path
14
12
  from .__init__ import __dashboard_url__
@@ -74,25 +72,6 @@ class SummaryPanel:
74
72
  """Clear the thinking content."""
75
73
  self.thinking_content = ""
76
74
 
77
- def ask_user_feedback(self, live: Live, layout: Layout, question: str, default: bool = True) -> bool:
78
- """
79
- Ask a yes/no question while keeping the main layout fixed.
80
- Uses Rich's Confirm for a clean user experience.
81
- """
82
- # Stop live updates temporarily to prevent layout from moving
83
- live.stop()
84
-
85
- try:
86
- # Use Rich's built-in Confirm
87
- result = Confirm.ask(question, default=default)
88
- except (KeyboardInterrupt, EOFError):
89
- result = default
90
- finally:
91
- # Resume live updates
92
- live.start()
93
-
94
- return result
95
-
96
75
  def get_display(self, final_message: Optional[str] = None) -> Panel:
97
76
  """Return a Rich panel summarising the current run."""
98
77
  # ───────────────────── summary grid ──────────────────────
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weco
3
- Version: 0.3.4
3
+ Version: 0.3.5
4
4
  Summary: Documentation for `weco`, a CLI for using Weco AI's code optimizer.
5
5
  Author-email: Weco AI Team <contact@weco.ai>
6
6
  License:
@@ -5,12 +5,12 @@ weco/chatbot.py,sha256=EIK2WaOul9gn_yHLThjsZV7RnE8t3XQPwgRkO5tybSU,38415
5
5
  weco/cli.py,sha256=7Q6ZwkD8N2x_PRovaFkF9OvtKkKX3hs_CobvS8xKXkQ,11535
6
6
  weco/constants.py,sha256=V6yFugTznKm5EC2_jr4I_whd7sqI80HiPggRn0az580,406
7
7
  weco/credits.py,sha256=C08x-TRcLg3ccfKqMGNRY7zBn7t3r7LZ119bxgfztaI,7629
8
- weco/optimizer.py,sha256=fvxpKHeET84vIBWEM_50iCInbxMYs0e44sJqe7zVzK0,43848
9
- weco/panels.py,sha256=OLss2PLgJqdo9w-Gq0TozyE42lsJvgjtqmfyCHpHGtA,16966
8
+ weco/optimizer.py,sha256=wG8_ZMF-SszWK-9rPXyaWvl60Q7EMTJbksSpKxf2pUk,45217
9
+ weco/panels.py,sha256=POHt0MdRKDykwUJYXcry92O41lpB9gxna55wFI9abWU,16272
10
10
  weco/utils.py,sha256=SMFh7ngo6gsjb2g20wpPe0O0T8xR7U_7X4-kqxJHkGc,8308
11
- weco-0.3.4.dist-info/licenses/LICENSE,sha256=9LUfoGHjLPtak2zps2kL2tm65HAZIICx_FbLaRuS4KU,11337
12
- weco-0.3.4.dist-info/METADATA,sha256=TGIKIIAl_0xQ9UBIjr3opE45ni_ZImzhLe29aBWQo3Q,29244
13
- weco-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- weco-0.3.4.dist-info/entry_points.txt,sha256=ixJ2uClALbCpBvnIR6BXMNck8SHAab8eVkM9pIUowcs,39
15
- weco-0.3.4.dist-info/top_level.txt,sha256=F0N7v6e2zBSlsorFv-arAq2yDxQbzX3KVO8GxYhPUeE,5
16
- weco-0.3.4.dist-info/RECORD,,
11
+ weco-0.3.5.dist-info/licenses/LICENSE,sha256=9LUfoGHjLPtak2zps2kL2tm65HAZIICx_FbLaRuS4KU,11337
12
+ weco-0.3.5.dist-info/METADATA,sha256=OvCAH5Rg2rTFlUkqBkHYEAYx09ZTErUdL4npETqQdnk,29244
13
+ weco-0.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ weco-0.3.5.dist-info/entry_points.txt,sha256=ixJ2uClALbCpBvnIR6BXMNck8SHAab8eVkM9pIUowcs,39
15
+ weco-0.3.5.dist-info/top_level.txt,sha256=F0N7v6e2zBSlsorFv-arAq2yDxQbzX3KVO8GxYhPUeE,5
16
+ weco-0.3.5.dist-info/RECORD,,
File without changes