weco 0.2.22__py3-none-any.whl → 0.2.24__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/api.py CHANGED
@@ -1,27 +1,10 @@
1
1
  import sys
2
2
  from typing import Dict, Any, Optional, Union, Tuple, List
3
-
4
3
  import requests
5
- from requests.adapters import HTTPAdapter
6
- from urllib3.util.retry import Retry
7
4
  from rich.console import Console
8
5
 
9
6
  from weco import __pkg_version__, __base_url__
10
-
11
-
12
- # --- Session Configuration ---
13
- def _get_weco_session() -> requests.Session:
14
- session = requests.Session()
15
- retry_strategy = Retry(
16
- total=3,
17
- status_forcelist=[429, 500, 502, 503, 504], # Retry on these server errors and rate limiting
18
- allowed_methods=["HEAD", "GET", "PUT", "POST", "DELETE", "OPTIONS"], # Case-insensitive
19
- backoff_factor=1, # e.g., sleep for 0s, 2s, 4s between retries (factor * (2 ** ({number of total retries} - 1)))
20
- )
21
- adapter = HTTPAdapter(max_retries=retry_strategy)
22
- session.mount("http://", adapter)
23
- session.mount("https://", adapter)
24
- return session
7
+ from .constants import DEFAULT_API_TIMEOUT
25
8
 
26
9
 
27
10
  def handle_api_error(e: requests.exceptions.HTTPError, console: Console) -> None:
@@ -48,13 +31,12 @@ def start_optimization_run(
48
31
  additional_instructions: str = None,
49
32
  api_keys: Dict[str, Any] = {},
50
33
  auth_headers: dict = {},
51
- timeout: Union[int, Tuple[int, int]] = 800,
34
+ timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
52
35
  ) -> Dict[str, Any]:
53
36
  """Start the optimization run."""
54
37
  with console.status("[bold green]Starting Optimization..."):
55
38
  try:
56
- session = _get_weco_session()
57
- response = session.post(
39
+ response = requests.post(
58
40
  f"{__base_url__}/runs",
59
41
  json={
60
42
  "source_code": source_code,
@@ -72,27 +54,33 @@ def start_optimization_run(
72
54
  timeout=timeout,
73
55
  )
74
56
  response.raise_for_status()
75
- return response.json()
76
- except requests.exceptions.HTTPError as e:
57
+ result = response.json()
58
+ # Handle None values for code and plan fields
59
+ if result.get("plan") is None:
60
+ result["plan"] = ""
61
+ if result.get("code") is None:
62
+ result["code"] = ""
63
+ return result
64
+ except Exception as e:
77
65
  handle_api_error(e, console)
78
66
  sys.exit(1)
79
- except requests.exceptions.RequestException as e:
80
- console.print(f"[bold red]Network Error starting run: {e}[/]")
67
+ except Exception as e:
68
+ console.print(f"[bold red]Error starting run: {e}[/]")
81
69
  sys.exit(1)
82
70
 
83
71
 
84
72
  def evaluate_feedback_then_suggest_next_solution(
73
+ console: Console,
85
74
  run_id: str,
86
75
  execution_output: str,
87
76
  additional_instructions: str = None,
88
77
  api_keys: Dict[str, Any] = {},
89
78
  auth_headers: dict = {},
90
- timeout: Union[int, Tuple[int, int]] = 800,
79
+ timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
91
80
  ) -> Dict[str, Any]:
92
81
  """Evaluate the feedback and suggest the next solution."""
93
82
  try:
94
- session = _get_weco_session()
95
- response = session.post(
83
+ response = requests.post(
96
84
  f"{__base_url__}/runs/{run_id}/suggest",
97
85
  json={
98
86
  "execution_output": execution_output,
@@ -103,50 +91,73 @@ def evaluate_feedback_then_suggest_next_solution(
103
91
  timeout=timeout,
104
92
  )
105
93
  response.raise_for_status()
106
- return response.json()
94
+ result = response.json()
95
+ # Handle None values for code and plan fields
96
+ if result.get("plan") is None:
97
+ result["plan"] = ""
98
+ if result.get("code") is None:
99
+ result["code"] = ""
100
+
101
+ return result
107
102
  except requests.exceptions.HTTPError as e:
108
103
  # Allow caller to handle suggest errors, maybe retry or terminate
109
- handle_api_error(e, Console()) # Use default console if none passed
104
+ handle_api_error(e, console) # Use default console if none passed
110
105
  raise # Re-raise the exception
111
- except requests.exceptions.RequestException as e:
112
- print(f"Network Error during suggest: {e}") # Use print as console might not be available
106
+ except Exception as e:
107
+ print(f"Error: {e}") # Use print as console might not be available
113
108
  raise # Re-raise the exception
114
109
 
115
110
 
116
111
  def get_optimization_run_status(
117
- run_id: str, include_history: bool = False, auth_headers: dict = {}, timeout: Union[int, Tuple[int, int]] = 800
112
+ console: Console,
113
+ run_id: str,
114
+ include_history: bool = False,
115
+ auth_headers: dict = {},
116
+ timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
118
117
  ) -> Dict[str, Any]:
119
118
  """Get the current status of the optimization run."""
120
119
  try:
121
- session = _get_weco_session()
122
- response = session.get(
120
+ response = requests.get(
123
121
  f"{__base_url__}/runs/{run_id}", params={"include_history": include_history}, headers=auth_headers, timeout=timeout
124
122
  )
125
123
  response.raise_for_status()
126
- return response.json()
124
+ result = response.json()
125
+ # Handle None values for code and plan fields in best_result and nodes
126
+ if result.get("best_result"):
127
+ if result["best_result"].get("code") is None:
128
+ result["best_result"]["code"] = ""
129
+ if result["best_result"].get("plan") is None:
130
+ result["best_result"]["plan"] = ""
131
+ # Handle None values for code and plan fields in nodes array
132
+ if result.get("nodes"):
133
+ for i, node in enumerate(result["nodes"]):
134
+ if node.get("plan") is None:
135
+ result["nodes"][i]["plan"] = ""
136
+ if node.get("code") is None:
137
+ result["nodes"][i]["code"] = ""
138
+ return result
127
139
  except requests.exceptions.HTTPError as e:
128
- handle_api_error(e, Console()) # Use default console
140
+ handle_api_error(e, console) # Use default console
129
141
  raise # Re-raise
130
- except requests.exceptions.RequestException as e:
131
- print(f"Network Error getting status: {e}")
142
+ except Exception as e:
143
+ print(f"Error getting run status: {e}")
132
144
  raise # Re-raise
133
145
 
134
146
 
135
- def send_heartbeat(run_id: str, auth_headers: dict = {}, timeout: Union[int, Tuple[int, int]] = 10) -> bool:
147
+ def send_heartbeat(run_id: str, auth_headers: dict = {}, timeout: Union[int, Tuple[int, int]] = (10, 10)) -> bool:
136
148
  """Send a heartbeat signal to the backend."""
137
149
  try:
138
- session = _get_weco_session()
139
- response = session.put(f"{__base_url__}/runs/{run_id}/heartbeat", headers=auth_headers, timeout=timeout)
150
+ response = requests.put(f"{__base_url__}/runs/{run_id}/heartbeat", headers=auth_headers, timeout=timeout)
140
151
  response.raise_for_status()
141
152
  return True
142
153
  except requests.exceptions.HTTPError as e:
143
154
  if e.response.status_code == 409:
144
- print(f"Heartbeat ignored: Run {run_id} is not running.", file=sys.stderr)
155
+ print("Polling ignore: Run {run_id} is not running.", file=sys.stderr)
145
156
  else:
146
- print(f"Heartbeat failed for run {run_id}: HTTP {e.response.status_code}", file=sys.stderr)
157
+ print(f"Polling failed for run {run_id}: HTTP {e.response.status_code}", file=sys.stderr)
147
158
  return False
148
- except requests.exceptions.RequestException as e:
149
- print(f"Heartbeat network error for run {run_id}: {e}", file=sys.stderr)
159
+ except Exception as e:
160
+ print(f"Error sending heartbeat for run {run_id}: {e}", file=sys.stderr)
150
161
  return False
151
162
 
152
163
 
@@ -156,12 +167,11 @@ def report_termination(
156
167
  reason: str,
157
168
  details: Optional[str] = None,
158
169
  auth_headers: dict = {},
159
- timeout: Union[int, Tuple[int, int]] = 30,
170
+ timeout: Union[int, Tuple[int, int]] = (10, 30),
160
171
  ) -> bool:
161
172
  """Report the termination reason to the backend."""
162
173
  try:
163
- session = _get_weco_session()
164
- response = session.post(
174
+ response = requests.post(
165
175
  f"{__base_url__}/runs/{run_id}/terminate",
166
176
  json={"status_update": status_update, "termination_reason": reason, "termination_details": details},
167
177
  headers=auth_headers,
@@ -169,7 +179,7 @@ def report_termination(
169
179
  )
170
180
  response.raise_for_status()
171
181
  return True
172
- except requests.exceptions.RequestException as e:
182
+ except Exception as e:
173
183
  print(f"Warning: Failed to report termination to backend for run {run_id}: {e}", file=sys.stderr)
174
184
  return False
175
185
 
@@ -195,24 +205,24 @@ def _determine_model_and_api_key() -> tuple[str, dict[str, str]]:
195
205
  api_key_dict = {"GEMINI_API_KEY": llm_api_keys["GEMINI_API_KEY"]}
196
206
  else:
197
207
  # This should never happen if determine_default_model works correctly
198
- raise ValueError(f"Unknown model returned: {model}")
208
+ raise ValueError(f"Unknown default model choice: {model}")
199
209
 
200
210
  return model, api_key_dict
201
211
 
202
212
 
203
213
  def get_optimization_suggestions_from_codebase(
214
+ console: Console,
204
215
  gitingest_summary: str,
205
216
  gitingest_tree: str,
206
217
  gitingest_content_str: str,
207
- console: Console,
208
218
  auth_headers: dict = {},
209
- timeout: Union[int, Tuple[int, int]] = 800,
219
+ timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
210
220
  ) -> Optional[List[Dict[str, Any]]]:
211
221
  """Analyze codebase and get optimization suggestions using the model-agnostic backend API."""
222
+ model, api_key_dict = _determine_model_and_api_key()
212
223
  try:
213
224
  model, api_key_dict = _determine_model_and_api_key()
214
- session = _get_weco_session()
215
- response = session.post(
225
+ response = requests.post(
216
226
  f"{__base_url__}/onboard/analyze-codebase",
217
227
  json={
218
228
  "gitingest_summary": gitingest_summary,
@@ -228,30 +238,27 @@ def get_optimization_suggestions_from_codebase(
228
238
  result = response.json()
229
239
  return [option for option in result.get("options", [])]
230
240
 
231
- except requests.exceptions.HTTPError as e:
241
+ except Exception as e:
232
242
  handle_api_error(e, console)
233
243
  return None
234
- except requests.exceptions.RequestException as e:
235
- console.print(f"[bold red]Network Error getting optimization suggestions: {e}[/]")
236
- return None
237
244
  except Exception as e:
238
- console.print(f"[bold red]Error calling backend API: {e}[/]")
245
+ console.print(f"[bold red]Error: {e}[/]")
239
246
  return None
240
247
 
241
248
 
242
249
  def generate_evaluation_script_and_metrics(
250
+ console: Console,
243
251
  target_file: str,
244
252
  description: str,
245
253
  gitingest_content_str: str,
246
- console: Console,
247
254
  auth_headers: dict = {},
248
- timeout: Union[int, Tuple[int, int]] = 800,
255
+ timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
249
256
  ) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str]]:
250
257
  """Generate evaluation script and determine metrics using the model-agnostic backend API."""
258
+ model, api_key_dict = _determine_model_and_api_key()
251
259
  try:
252
260
  model, api_key_dict = _determine_model_and_api_key()
253
- session = _get_weco_session()
254
- response = session.post(
261
+ response = requests.post(
255
262
  f"{__base_url__}/onboard/generate-script",
256
263
  json={
257
264
  "target_file": target_file,
@@ -266,33 +273,29 @@ def generate_evaluation_script_and_metrics(
266
273
  response.raise_for_status()
267
274
  result = response.json()
268
275
  return result.get("script_content"), result.get("metric_name"), result.get("goal"), result.get("reasoning")
269
-
270
276
  except requests.exceptions.HTTPError as e:
271
277
  handle_api_error(e, console)
272
278
  return None, None, None, None
273
- except requests.exceptions.RequestException as e:
274
- console.print(f"[bold red]Network Error generating evaluation script: {e}[/]")
275
- return None, None, None, None
276
279
  except Exception as e:
277
- console.print(f"[bold red]Error calling backend API: {e}[/]")
280
+ console.print(f"[bold red]Error: {e}[/]")
278
281
  return None, None, None, None
279
282
 
280
283
 
281
284
  def analyze_evaluation_environment(
285
+ console: Console,
282
286
  target_file: str,
283
287
  description: str,
284
288
  gitingest_summary: str,
285
289
  gitingest_tree: str,
286
290
  gitingest_content_str: str,
287
- console: Console,
288
291
  auth_headers: dict = {},
289
- timeout: Union[int, Tuple[int, int]] = 800,
292
+ timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
290
293
  ) -> Optional[Dict[str, Any]]:
291
294
  """Analyze existing evaluation scripts and environment using the model-agnostic backend API."""
295
+ model, api_key_dict = _determine_model_and_api_key()
292
296
  try:
293
297
  model, api_key_dict = _determine_model_and_api_key()
294
- session = _get_weco_session()
295
- response = session.post(
298
+ response = requests.post(
296
299
  f"{__base_url__}/onboard/analyze-environment",
297
300
  json={
298
301
  "target_file": target_file,
@@ -309,30 +312,27 @@ def analyze_evaluation_environment(
309
312
  response.raise_for_status()
310
313
  return response.json()
311
314
 
312
- except requests.exceptions.HTTPError as e:
315
+ except Exception as e:
313
316
  handle_api_error(e, console)
314
317
  return None
315
- except requests.exceptions.RequestException as e:
316
- console.print(f"[bold red]Network Error analyzing evaluation environment: {e}[/]")
317
- return None
318
318
  except Exception as e:
319
- console.print(f"[bold red]Error calling backend API: {e}[/]")
319
+ console.print(f"[bold red]Error: {e}[/]")
320
320
  return None
321
321
 
322
322
 
323
323
  def analyze_script_execution_requirements(
324
+ console: Console,
324
325
  script_content: str,
325
326
  script_path: str,
326
327
  target_file: str,
327
- console: Console,
328
328
  auth_headers: dict = {},
329
- timeout: Union[int, Tuple[int, int]] = 800,
329
+ timeout: Union[int, Tuple[int, int]] = DEFAULT_API_TIMEOUT,
330
330
  ) -> Optional[str]:
331
331
  """Analyze script to determine proper execution command using the model-agnostic backend API."""
332
+ model, api_key_dict = _determine_model_and_api_key()
332
333
  try:
333
334
  model, api_key_dict = _determine_model_and_api_key()
334
- session = _get_weco_session()
335
- response = session.post(
335
+ response = requests.post(
336
336
  f"{__base_url__}/onboard/analyze-script",
337
337
  json={
338
338
  "script_content": script_content,
@@ -348,12 +348,9 @@ def analyze_script_execution_requirements(
348
348
  result = response.json()
349
349
  return result.get("command", f"python {script_path}")
350
350
 
351
- except requests.exceptions.HTTPError as e:
351
+ except Exception as e:
352
352
  handle_api_error(e, console)
353
353
  return f"python {script_path}"
354
- except requests.exceptions.RequestException as e:
355
- console.print(f"[bold red]Network Error analyzing script execution: {e}[/]")
356
- return f"python {script_path}"
357
354
  except Exception as e:
358
- console.print(f"[bold red]Error calling backend API: {e}[/]")
355
+ console.print(f"[bold red]Error: {e}[/]")
359
356
  return f"python {script_path}"
weco/auth.py CHANGED
@@ -35,7 +35,7 @@ def save_api_key(api_key: str):
35
35
  # Set file permissions to read/write for owner only (600)
36
36
  os.chmod(CREDENTIALS_FILE, stat.S_IRUSR | stat.S_IWUSR)
37
37
  except OSError as e:
38
- print(f"Error: Could not write credentials file or set permissions on {CREDENTIALS_FILE}: {e}")
38
+ print(f"Error: Unable to save credentials file or set permissions on {CREDENTIALS_FILE}: {e}")
39
39
 
40
40
 
41
41
  def load_weco_api_key() -> str | None:
@@ -53,7 +53,7 @@ def load_weco_api_key() -> str | None:
53
53
  credentials = json.load(f)
54
54
  return credentials.get("api_key")
55
55
  except (IOError, json.JSONDecodeError, OSError) as e:
56
- print(f"Warning: Could not read or parse credentials file at {CREDENTIALS_FILE}: {e}")
56
+ print(f"Warning: Unable to read credentials file at {CREDENTIALS_FILE}: {e}")
57
57
  return None
58
58
 
59
59
 
@@ -64,7 +64,7 @@ def clear_api_key():
64
64
  os.remove(CREDENTIALS_FILE)
65
65
  print("Logged out successfully.")
66
66
  except OSError as e:
67
- print(f"Error: Could not remove credentials file at {CREDENTIALS_FILE}: {e}")
67
+ print(f"Error: Unable to remove credentials file at {CREDENTIALS_FILE}: {e}")
68
68
  else:
69
69
  print("Already logged out.")
70
70
 
@@ -129,7 +129,9 @@ def perform_login(console: Console):
129
129
  continue # Continue polling
130
130
  else:
131
131
  # Unexpected 202 response format
132
- console.print(f"\n[bold red]Error:[/] Received unexpected 202 response: {token_data}")
132
+ console.print(
133
+ f"\n[bold red]Error:[/] Received unexpected response from authentication server: {token_data}"
134
+ )
133
135
  return False
134
136
  # Check for standard OAuth2 errors (often 400 Bad Request)
135
137
  elif token_response.status_code == 400:
@@ -146,7 +148,7 @@ def perform_login(console: Console):
146
148
  console.print("\n[bold red]Error:[/] Authorization denied by user.")
147
149
  return False
148
150
  else: # invalid_grant, etc.
149
- error_desc = token_data.get("error_description", "Unknown error during polling.")
151
+ error_desc = token_data.get("error_description", "Unknown authentication error occurred.")
150
152
  console.print(f"\n[bold red]Error:[/] {error_desc} ({error_code})")
151
153
  return False
152
154
 
weco/chatbot.py CHANGED
@@ -50,7 +50,7 @@ class UserInteractionHelper:
50
50
 
51
51
  if attempts >= max_retries:
52
52
  self.console.print(f"[red]Maximum retry attempts ({max_retries}) reached. Exiting.[/]")
53
- raise Exception("Maximum retry attempts exceeded")
53
+ raise Exception("Maximum retry attempts exceeded. Please try again.")
54
54
 
55
55
  # Show available options without the full prompt
56
56
  if choices:
@@ -66,7 +66,7 @@ class UserInteractionHelper:
66
66
  continue
67
67
 
68
68
  # This should never be reached due to the exception above, but just in case
69
- raise Exception("Unexpected error in choice selection")
69
+ raise Exception("Unexpected error while selecting a choice")
70
70
 
71
71
  def get_choice_numeric(self, prompt: str, max_number: int, default: int = None, max_retries: int = 5) -> int:
72
72
  """Get numeric choice with validation and error handling."""
@@ -87,7 +87,7 @@ class UserInteractionHelper:
87
87
 
88
88
  if attempts >= max_retries:
89
89
  self.console.print(f"[red]Maximum retry attempts ({max_retries}) reached. Exiting.[/]")
90
- raise Exception("Maximum retry attempts exceeded")
90
+ raise Exception("Maximum retry attempts exceeded. Please try again.")
91
91
 
92
92
  # Show valid range
93
93
  self.console.print(f"Please enter a number between [bold]1[/] and [bold]{max_number}[/]")
@@ -115,7 +115,7 @@ class UserInteractionHelper:
115
115
 
116
116
  if attempts >= max_retries:
117
117
  self.console.print(f"[red]Maximum retry attempts ({max_retries}) reached. Exiting.[/]")
118
- raise Exception("Maximum retry attempts exceeded")
118
+ raise Exception("Maximum retry attempts exceeded. Please try again.")
119
119
 
120
120
  self.console.print("Valid options: [bold]y[/] / [bold]n[/]")
121
121
  if default:
@@ -123,7 +123,7 @@ class UserInteractionHelper:
123
123
 
124
124
  continue
125
125
 
126
- raise Exception("Unexpected error in yes/no selection")
126
+ raise Exception("Unexpected error while selecting an option")
127
127
 
128
128
  def display_optimization_options_table(self, options: List[Dict[str, str]]) -> None:
129
129
  """Display optimization options in a formatted table."""
@@ -215,7 +215,10 @@ class Chatbot:
215
215
 
216
216
  with self.console.status("[bold green]Generating optimization suggestions...[/]"):
217
217
  result = get_optimization_suggestions_from_codebase(
218
- self.gitingest_summary, self.gitingest_tree, self.gitingest_content_str, self.console
218
+ console=self.console,
219
+ gitingest_summary=self.gitingest_summary,
220
+ gitingest_tree=self.gitingest_tree,
221
+ gitingest_content_str=self.gitingest_content_str,
219
222
  )
220
223
 
221
224
  if result and isinstance(result, list):
@@ -224,7 +227,7 @@ class Chatbot:
224
227
  options = None
225
228
 
226
229
  if not options or not isinstance(options, list):
227
- self.console.print("[red]Failed to get valid optimization options.[/]")
230
+ self.console.print("[red]Unable to retrieve valid optimization options from the backend.[/]")
228
231
  return None
229
232
 
230
233
  if not options:
@@ -324,17 +327,19 @@ class Chatbot:
324
327
  elif action == "g" or action == "r":
325
328
  with self.console.status("[bold green]Generating evaluation script and determining metrics...[/]"):
326
329
  result = generate_evaluation_script_and_metrics(
327
- selected_option["target_file"],
328
- selected_option["description"],
329
- self.gitingest_content_str,
330
- self.console,
330
+ console=self.console,
331
+ target_file=selected_option["target_file"],
332
+ description=selected_option["description"],
333
+ gitingest_content_str=self.gitingest_content_str,
331
334
  )
332
335
  if result and result[0]:
333
336
  eval_script_content, metric_name, goal, reasoning = result
334
337
  if reasoning:
335
338
  self.console.print(f"[dim]Reasoning: {reasoning}[/]")
336
339
  else:
337
- self.console.print("[red]Failed to generate an evaluation script.[/]")
340
+ self.console.print(
341
+ "[red]Unable to generate an evaluation script. Please try providing a custom script path instead.[/]"
342
+ )
338
343
  eval_script_content = None
339
344
  metric_name = None
340
345
  goal = None
@@ -371,7 +376,10 @@ class Chatbot:
371
376
  # Analyze the script to determine the proper execution command
372
377
  with self.console.status("[bold green]Analyzing script execution requirements...[/]"):
373
378
  eval_command = analyze_script_execution_requirements(
374
- eval_script_content, eval_script_path_str, selected_option["target_file"], self.console
379
+ console=self.console,
380
+ script_content=eval_script_content,
381
+ script_path=eval_script_path_str,
382
+ target_file=selected_option["target_file"],
375
383
  )
376
384
 
377
385
  return {
@@ -386,16 +394,16 @@ class Chatbot:
386
394
  """Get or create evaluation script configuration using intelligent conversation-guided approach."""
387
395
  with self.console.status("[bold green]Analyzing evaluation environment...[/]"):
388
396
  analysis = analyze_evaluation_environment(
389
- selected_option["target_file"],
390
- selected_option["description"],
391
- self.gitingest_summary,
392
- self.gitingest_tree,
393
- self.gitingest_content_str,
394
- self.console,
397
+ console=self.console,
398
+ target_file=selected_option["target_file"],
399
+ description=selected_option["description"],
400
+ gitingest_summary=self.gitingest_summary,
401
+ gitingest_tree=self.gitingest_tree,
402
+ gitingest_content_str=self.gitingest_content_str,
395
403
  )
396
404
 
397
405
  if not analysis:
398
- self.console.print("[yellow]Failed to analyze evaluation environment. Falling back to generation.[/]")
406
+ self.console.print("[yellow]Unable to analyze evaluation environment. Falling back to script generation.[/]")
399
407
  return self.handle_script_generation_workflow(selected_option)
400
408
 
401
409
  self.evaluation_analysis = analysis
@@ -529,7 +537,10 @@ class Chatbot:
529
537
  if not eval_command or eval_command == f"python {script_path}":
530
538
  with self.console.status("[bold green]Analyzing script execution requirements...[/]"):
531
539
  eval_command = analyze_script_execution_requirements(
532
- script_content, script_path, selected_option["target_file"], self.console
540
+ console=self.console,
541
+ script_content=script_content,
542
+ script_path=script_path,
543
+ target_file=selected_option["target_file"],
533
544
  )
534
545
 
535
546
  self.current_step = "confirmation"
@@ -711,7 +722,7 @@ class Chatbot:
711
722
  """Setup evaluation environment for the selected optimization."""
712
723
  eval_config = self.get_evaluation_configuration(selected_option)
713
724
  if not eval_config:
714
- self.console.print("[red]Evaluation script setup failed.[/]")
725
+ self.console.print("[red]Evaluation script setup failed. Please check your script configuration and try again.[/]")
715
726
  return None
716
727
 
717
728
  eval_config = self.confirm_and_finalize_evaluation_config(eval_config)
@@ -791,7 +802,7 @@ def run_onboarding_chatbot(
791
802
  chatbot = Chatbot(project_path, console, run_parser, model)
792
803
  chatbot.start()
793
804
  except Exception as e:
794
- console.print(f"[bold red]An unexpected error occurred in the chatbot: {e}[/]")
805
+ console.print(f"[bold red]An unexpected error occurred: {e}[/]")
795
806
  import traceback
796
807
 
797
808
  traceback.print_exc()
weco/cli.py CHANGED
@@ -61,6 +61,12 @@ def configure_run_parser(run_parser: argparse.ArgumentParser) -> None:
61
61
  type=str,
62
62
  help="Description of additional instruction or path to a file containing additional instructions. Defaults to None.",
63
63
  )
64
+ run_parser.add_argument(
65
+ "--eval-timeout",
66
+ type=int,
67
+ default=None,
68
+ help="Timeout in seconds for each evaluation. No timeout by default. Example: --eval-timeout 3600",
69
+ )
64
70
 
65
71
 
66
72
  def execute_run_command(args: argparse.Namespace) -> None:
@@ -77,6 +83,7 @@ def execute_run_command(args: argparse.Namespace) -> None:
77
83
  log_dir=args.log_dir,
78
84
  additional_instructions=args.additional_instructions,
79
85
  console=console,
86
+ eval_timeout=args.eval_timeout,
80
87
  )
81
88
  exit_code = 0 if success else 1
82
89
  sys.exit(exit_code)
@@ -177,7 +184,9 @@ def main() -> None:
177
184
 
178
185
  project_path = pathlib.Path(filtered_args[0]) if filtered_args else pathlib.Path.cwd()
179
186
  if not project_path.is_dir():
180
- console.print(f"[bold red]Error:[/] Path '{project_path}' is not a valid directory.")
187
+ console.print(
188
+ f"[bold red]Error:[/] The path '{project_path}' is not a valid directory. Please provide a valid directory path."
189
+ )
181
190
  sys.exit(1)
182
191
 
183
192
  # Pass the run_parser and model to the chatbot
weco/constants.py ADDED
@@ -0,0 +1,7 @@
1
+ # weco/constants.py
2
+ """
3
+ Constants for the Weco CLI package.
4
+ """
5
+
6
+ # API timeout configuration (connect_timeout, read_timeout) in seconds
7
+ DEFAULT_API_TIMEOUT = (10, 800)