|
10 | 10 |
|
11 | 11 | import yaml |
12 | 12 | from liquid2.exceptions import TemplateNotFoundError |
13 | | -from requests.exceptions import RequestException |
14 | 13 |
|
15 | 14 | import codeplain_REST_api as codeplain_api |
16 | 15 | import file_utils |
|
23 | 22 | from plain2code_events import RenderFailed |
24 | 23 | from plain2code_exceptions import ( |
25 | 24 | ConflictingRequirements, |
26 | | - InternalClientError, |
27 | | - InternalServerError, |
28 | 25 | InvalidAPIKey, |
29 | 26 | InvalidFridArgument, |
30 | | - LLMInternalError, |
31 | 27 | MissingAPIKey, |
32 | 28 | MissingPreviousFunctionalitiesError, |
33 | 29 | MissingResource, |
|
58 | 54 | def print_exit_summary( |
59 | 55 | run_state: RunState, |
60 | 56 | spec_filename: str, |
| 57 | + error_message: Optional[str] = None, |
61 | 58 | ) -> None: |
62 | 59 | console.quiet = False |
63 | 60 | """Print render outcome after the TUI exits (terminal restored).""" |
| 61 | + |
64 | 62 | msg = "\n[#79FC96]✓ rendering completed\n\n" if run_state.render_succeeded else "[#FF6B6B]✗ rendering failed\n\n" |
65 | 63 | msg += f" [#8E8F91]render id:\t\t\t[#FFFFFF]{run_state.render_id}\n" |
66 | 64 | msg += f" [#8E8F91]input file:\t\t\t[#FFFFFF]{spec_filename}\n" |
67 | 65 | msg += f" [#8E8F91]generated code folder:\t[#FFFFFF]{run_state.render_generated_code_path or '-'}\n\n" |
68 | 66 | msg += f"[#8E8F91]functionalities [#FFFFFF]{run_state.rendered_functionalities} [#8E8F91]used credits [#FFFFFF]{run_state.rendered_functionalities} [#8E8F91]render time [#FFFFFF]{format_duration_hms(run_state.render_time_accumulated)}\n" |
69 | 67 | console.info(msg) |
| 68 | + |
| 69 | + if not run_state.render_succeeded and error_message: |
| 70 | + console.error(error_message) |
70 | 71 | console.quiet = True |
71 | 72 |
|
72 | 73 |
|
@@ -104,20 +105,20 @@ def _get_frids_range(plain_source, start, end=None): |
104 | 105 | start = str(start) |
105 | 106 |
|
106 | 107 | if start not in frids: |
107 | | - raise InvalidFridArgument(f"Invalid start functionality ID: {start}. Valid IDs are: {frids}.") |
| 108 | + raise InvalidFridArgument(f"Invalid start functionality ID: {start}. Valid IDs are: {frids}.\n") |
108 | 109 |
|
109 | 110 | if end is not None: |
110 | 111 | end = str(end) |
111 | 112 | if end not in frids: |
112 | | - raise InvalidFridArgument(f"Invalid end functionality ID: {end}. Valid IDs are: {frids}.") |
| 113 | + raise InvalidFridArgument(f"Invalid end functionality ID: {end}. Valid IDs are: {frids}.\n") |
113 | 114 |
|
114 | 115 | end_idx = frids.index(end) + 1 |
115 | 116 | else: |
116 | 117 | end_idx = len(frids) |
117 | 118 |
|
118 | 119 | start_idx = frids.index(start) |
119 | 120 | if start_idx >= end_idx: |
120 | | - raise InvalidFridArgument(f"Start functionality ID: {start} must be before end functionality ID: {end}.") |
| 121 | + raise InvalidFridArgument(f"Start functionality ID: {start} must be before end functionality ID: {end}.\n") |
121 | 122 |
|
122 | 123 | return frids[start_idx:end_idx] |
123 | 124 |
|
@@ -186,16 +187,17 @@ def _check_connection(codeplainAPI: codeplain_api.CodeplainAPI): |
186 | 187 |
|
187 | 188 | if not response.get("api_key_valid", False): |
188 | 189 | raise InvalidAPIKey( |
189 | | - "Provided API key is invalid. Please provide a valid API key using the CODEPLAIN_API_KEY environment variable " |
190 | | - "or the --api-key argument." |
| 190 | + "The provided API key is invalid. Please provide a valid API key using the CODEPLAIN_API_KEY environment variable " |
| 191 | + "or the --api-key argument.\n" |
191 | 192 | ) |
192 | 193 |
|
193 | 194 | if not response.get("client_version_valid", False): |
194 | 195 | min_version = response.get("min_client_version", "unknown") |
195 | 196 | raise OutdatedClientVersion( |
| 197 | + "Outdated client version: " |
196 | 198 | f"Your client version ({system_config.client_version}) is outdated. Minimum required version is {min_version}. " |
197 | 199 | "Please update using:" |
198 | | - " uv tool upgrade codeplain" |
| 200 | + " uv tool upgrade codeplain\n" |
199 | 201 | ) |
200 | 202 |
|
201 | 203 |
|
@@ -317,84 +319,48 @@ def main(): # noqa: C901 |
317 | 319 | setup_logging(args, event_bus, run_state, args.log_to_file, args.log_file_name, args.filename, args.headless) |
318 | 320 |
|
319 | 321 | exc_info = None |
| 322 | + error_message = None |
| 323 | + |
320 | 324 | try: |
321 | 325 | # Validate API key is present |
322 | 326 | if not args.api_key: |
323 | 327 | raise MissingAPIKey( |
324 | | - "API key is required. Please set the CODEPLAIN_API_KEY environment variable or provide it with the --api-key argument." |
| 328 | + "Your API key is required. Please set the CODEPLAIN_API_KEY environment variable or provide it with the --api-key argument.\n" |
325 | 329 | ) |
326 | 330 | render(args, run_state, event_bus) |
327 | | - except InvalidFridArgument as e: |
328 | | - exc_info = sys.exc_info() |
329 | | - console.error(f"Invalid FRID argument: {str(e)}.\n") |
330 | | - except FileNotFoundError as e: |
331 | | - exc_info = sys.exc_info() |
332 | | - console.error(f"File not found: {str(e)}\n") |
333 | | - console.debug(f"Render ID: {run_state.render_id}") |
334 | | - except TemplateNotFoundError as e: |
335 | | - exc_info = sys.exc_info() |
336 | | - console.error(f"Template not found: {str(e)}\n") |
337 | | - console.error(system_config.get_error_message("template_not_found")) |
338 | | - except PlainSyntaxError as e: |
339 | | - exc_info = sys.exc_info() |
340 | | - console.error(f"Plain syntax error: {str(e)}\n") |
341 | | - except KeyboardInterrupt: |
342 | | - exc_info = sys.exc_info() |
343 | | - console.error("Keyboard interrupt") |
344 | | - console.debug(f"Render ID: {run_state.render_id}") |
345 | | - except RequestException as e: |
346 | | - exc_info = sys.exc_info() |
347 | | - console.error(f"Error rendering plain code: {str(e)}\n") |
348 | | - console.debug(f"Render ID: {run_state.render_id}") |
349 | | - except MissingPreviousFunctionalitiesError as e: |
350 | | - exc_info = sys.exc_info() |
351 | | - console.error(f"Error rendering plain code: {str(e)}\n") |
352 | | - console.debug(f"Render ID: {run_state.render_id}") |
353 | | - except MissingAPIKey as e: |
354 | | - console.error(f"Missing API key: {str(e)}\n") |
355 | | - except InvalidAPIKey as e: |
356 | | - console.error(f"Invalid API key: {str(e)}\n") |
357 | | - except OutdatedClientVersion as e: |
358 | | - console.error(f"Outdated client version: {str(e)}\n") |
359 | | - except (InternalServerError, InternalClientError): |
360 | | - exc_info = sys.exc_info() |
361 | | - console.error( |
362 | | - f"Internal server error.\n\nPlease report the error to support@codeplain.ai with the attached {args.log_file_name} file." |
363 | | - ) |
364 | | - console.debug(f"Render ID: {run_state.render_id}") |
365 | | - except ConflictingRequirements as e: |
366 | | - exc_info = sys.exc_info() |
367 | | - console.error(f"Conflicting requirements: {str(e)}\n") |
368 | | - console.debug(f"Render ID: {run_state.render_id}") |
369 | | - except RenderingCreditBalanceTooLow as e: |
370 | | - exc_info = sys.exc_info() |
371 | | - console.error(f"Credit balance too low: {str(e)}\n") |
372 | | - console.debug(f"Render ID: {run_state.render_id}") |
373 | | - except LLMInternalError as e: |
374 | | - exc_info = sys.exc_info() |
375 | | - console.error(f"LLM internal error: {str(e)}\n") |
376 | | - console.debug(f"Render ID: {run_state.render_id}") |
377 | | - except MissingResource as e: |
378 | | - exc_info = sys.exc_info() |
379 | | - console.error(f"Missing resource: {str(e)}\n") |
380 | | - console.debug(f"Render ID: {run_state.render_id}") |
381 | | - except NetworkConnectionError as e: |
382 | | - exc_info = sys.exc_info() |
383 | | - console.error(f"Connection error: {str(e)}\n") |
384 | | - console.error("Please check that your internet connection is working.") |
385 | | - except ModuleDoesNotExistError as e: |
386 | | - exc_info = sys.exc_info() |
387 | | - console.error(f"Module does not exist: {str(e)}\n") |
388 | | - console.debug(f"Render ID: {run_state.render_id}") |
389 | | - except Exception as e: |
390 | | - exc_info = sys.exc_info() |
391 | | - console.error(f"Error rendering plain code: {str(e)}\n") |
392 | | - console.debug(f"Render ID: {run_state.render_id}") |
| 331 | + except BaseException as e: |
| 332 | + if isinstance(e, KeyboardInterrupt): |
| 333 | + error_message = "Keyboard interrupt" |
| 334 | + else: |
| 335 | + error_message = str(e) if str(e) else repr(e) |
| 336 | + |
| 337 | + if not isinstance( |
| 338 | + e, |
| 339 | + ( |
| 340 | + InvalidFridArgument, |
| 341 | + FileNotFoundError, |
| 342 | + MissingResource, |
| 343 | + TemplateNotFoundError, |
| 344 | + PlainSyntaxError, |
| 345 | + MissingPreviousFunctionalitiesError, |
| 346 | + MissingAPIKey, |
| 347 | + InvalidAPIKey, |
| 348 | + OutdatedClientVersion, |
| 349 | + ConflictingRequirements, |
| 350 | + RenderingCreditBalanceTooLow, |
| 351 | + NetworkConnectionError, |
| 352 | + ModuleDoesNotExistError, |
| 353 | + ), |
| 354 | + ): |
| 355 | + exc_info = sys.exc_info() |
393 | 356 | finally: |
394 | | - print_exit_summary(run_state, args.filename) |
| 357 | + print_exit_summary( |
| 358 | + run_state, |
| 359 | + args.filename, |
| 360 | + error_message=error_message, |
| 361 | + ) |
395 | 362 | if exc_info: |
396 | | - # Log traceback using the logging system |
397 | | - logging.error("Render crashed with exception:", exc_info=exc_info) |
| 363 | + # Log traceback |
398 | 364 | dump_crash_logs(args) |
399 | 365 |
|
400 | 366 |
|
|
0 commit comments