main
py 96 lines 3.06 KB
Raw
1 import re
2 import traceback
3 import asyncio
4 from typing import Literal
5
6
7 def handle_error(e: Exception):
8 # if asyncio.CancelledError, re-raise
9 if isinstance(e, asyncio.CancelledError):
10 raise e
11
12
13 def error_text(e: Exception):
14 return str(e)
15
16
17 def format_error(
18 e: Exception,
19 start_entries=20,
20 end_entries=15,
21 error_message_position: Literal["top", "bottom", "none"] = "top",
22 ):
23 # format traceback from the provided exception instead of the most recent one
24 traceback_text = "".join(traceback.format_exception(type(e), e, e.__traceback__))
25 # Split the traceback into lines
26 lines = traceback_text.split("\n")
27
28 if not start_entries and not end_entries:
29 trimmed_lines = []
30 else:
31
32 # Find all "File" lines
33 file_indices = [
34 i for i, line in enumerate(lines) if line.strip().startswith("File ")
35 ]
36
37 # If we found at least one "File" line, trim the middle if there are more than start_entries+end_entries lines
38 if len(file_indices) > start_entries + end_entries:
39 start_index = max(0, len(file_indices) - start_entries - end_entries)
40 trimmed_lines = (
41 lines[: file_indices[start_index]]
42 + [
43 f"\n>>> {len(file_indices) - start_entries - end_entries} stack lines skipped <<<\n"
44 ]
45 + lines[file_indices[start_index + end_entries] :]
46 )
47 else:
48 # If no "File" lines found, or not enough to trim, just return the original traceback
49 trimmed_lines = lines
50
51 # Find the error message at the end
52 error_message = ""
53 for line in reversed(lines):
54 # match both simple errors and module.path.Error patterns
55 if re.match(r"[\w\.]+Error:\s*", line):
56 error_message = line
57 break
58
59 if error_message and error_message_position in ("top", "bottom", "none"):
60 for i in range(len(trimmed_lines) - 1, -1, -1):
61 if trimmed_lines[i].strip() == error_message.strip():
62 trimmed_lines = trimmed_lines[:i] + trimmed_lines[i + 1 :]
63 break
64
65 # Combine the trimmed traceback with the error message
66 if not trimmed_lines:
67 result = "" if error_message_position == "none" else error_message
68 else:
69 result = "Traceback (most recent call last):\n" + "\n".join(trimmed_lines)
70
71 if error_message and error_message_position == "top":
72 result = f"{error_message}\n\n{result}" if result else error_message
73 elif error_message and error_message_position == "bottom":
74 result = f"{result}\n\n{error_message}" if result else error_message
75
76 # at least something
77 if not result:
78 result = str(e)
79
80 return result
81
82
83 class RepairableException(Exception):
84 """An exception type indicating errors that can be surfaced to the LLM for potential self-repair."""
85
86 pass
87
88
89 class InterventionException(Exception):
90 """An exception type raised on user intervention, skipping rest of message loop iteration."""
91
92 pass
93
94
95 class HandledException(Exception):
96 pass