Skip to content

Commit c83a091

Browse files
committed
Improve pipeline import error reporting
1 parent 0646ea0 commit c83a091

1 file changed

Lines changed: 73 additions & 22 deletions

File tree

evaluation_function/evaluation.py

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import os
4+
import traceback
45
from typing import Any, Dict, List, Optional, Tuple
56
from urllib.parse import urlparse, unquote
67

@@ -19,16 +20,22 @@
1920
class ImageUploadError(Exception): # type: ignore
2021
pass
2122

22-
PIPELINE_IMPORT_ERROR = None
23+
24+
# Pipeline import guard
25+
PIPELINE_IMPORT_ERROR: Optional[Dict[str, str]] = None
2326
run_yolo_pipeline = None
2427

2528
try:
2629
from .yolo_pipeline import run_yolo_pipeline # type: ignore
2730
except Exception as e:
28-
PIPELINE_IMPORT_ERROR = f"{type(e).__name__}: {e}"
31+
PIPELINE_IMPORT_ERROR = {
32+
"stage": "IMPORT",
33+
"error_code": "E_PIPELINE_IMPORT",
34+
"exc_type": type(e).__name__,
35+
"message": str(e),
36+
"traceback": traceback.format_exc(),
37+
}
2938
run_yolo_pipeline = None
30-
#from .yolo_pipeline import run_yolo_pipeline
31-
3239

3340
# URL / path helpers
3441
def file_url_to_local_path(url: str) -> str:
@@ -98,8 +105,9 @@ def _pget(params: Params, key: str, default: Any) -> Any:
98105
except Exception:
99106
return default
100107

101-
def _items_to_feedback_html(items):
102-
lines = []
108+
109+
def _items_to_feedback_html(items: List[Tuple[Any, Any]]) -> str:
110+
lines: List[str] = []
103111
for k, v in items:
104112
k = str(k).strip() if k is not None else ""
105113
v = str(v).strip() if v is not None else ""
@@ -110,33 +118,68 @@ def _items_to_feedback_html(items):
110118
return "<br>".join(lines)
111119

112120

121+
def _escape_html(s: str) -> str:
122+
# minimal safe escaping for traceback readability
123+
return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
124+
125+
126+
def _error_dict_to_items(err: Dict[str, str]) -> List[Tuple[str, str]]:
127+
"""
128+
Convert a structured error dict into readable feedback_items.
129+
Includes two traceback renderings:
130+
- <pre> (nice if allowed)
131+
- <br> version (if <pre> is stripped by sanitizer)
132+
"""
133+
items: List[Tuple[str, str]] = []
134+
items.append(("Stage", err.get("stage", "UNKNOWN")))
135+
items.append(("ErrorCode", err.get("error_code", "E_UNKNOWN")))
136+
items.append(("ExceptionType", err.get("exc_type", "")))
137+
items.append(("Message", err.get("message", "")))
138+
139+
tb = err.get("traceback", "")
140+
if tb:
141+
safe_tb = _escape_html(tb)
142+
items.append(("Traceback", f"<pre>{safe_tb}</pre>"))
143+
items.append(("Traceback(html)", safe_tb.replace("\n", "<br>")))
144+
145+
return items
146+
113147
# Main entry
114148
def evaluation_function(response: Any, answer: Any, params: Params) -> Result:
115149
try:
116150
# 0) Pipeline import guard (MOST IMPORTANT)
117151
if run_yolo_pipeline is None:
118-
items = [("Error", f"Pipeline import failed: {PIPELINE_IMPORT_ERROR}")]
152+
if isinstance(PIPELINE_IMPORT_ERROR, dict):
153+
items = _error_dict_to_items(PIPELINE_IMPORT_ERROR)
154+
else:
155+
items = [
156+
("Stage", "IMPORT"),
157+
("ErrorCode", "E_PIPELINE_IMPORT"),
158+
("Message", f"Pipeline import failed: {PIPELINE_IMPORT_ERROR}"),
159+
]
160+
119161
feedback_html = _items_to_feedback_html(items)
120162
try:
121163
return Result(is_correct=False, feedback=feedback_html, feedback_items=items)
122164
except TypeError:
123165
return Result(is_correct=False, feedback_items=items)
124-
# 1) Validate input
125166

167+
# 1) Validate input
126168
if not isinstance(response, list) or len(response) == 0:
127169
items = [("Response", "Please upload at least one image.")]
128170
feedback_html = _items_to_feedback_html(items)
129171
try:
130172
return Result(is_correct=False, feedback=feedback_html, feedback_items=items)
131173
except TypeError:
132174
return Result(is_correct=False, feedback_items=items)
133-
# 2) Optional controls
134175

176+
# 2) Optional controls
135177
return_images: bool = bool(_pget(params, "return_images", False))
136178
debug: bool = bool(_pget(params, "debug", False))
137179

138180
gear_model_rel = str(_pget(params, "gear_model_rel", "gear_model.pt"))
139181
shaft_model_rel = str(_pget(params, "shaft_model_rel", "shaft_model.pt"))
182+
140183
# 3) Process images
141184
merged_errors: List[Dict[str, str]] = []
142185
merged_summaries: List[Dict[str, Any]] = []
@@ -146,13 +189,13 @@ def evaluation_function(response: Any, answer: Any, params: Params) -> Result:
146189
for idx, item in enumerate(response):
147190
url = item.get("url") if isinstance(item, dict) else None
148191
if not url:
149-
merged_errors.append({"code": "NO_URL", "message": f"Image [{idx}] has no 'url' field."})
192+
merged_errors.append({"code": "E_NO_URL", "message": f"Image [{idx}] has no 'url' field."})
150193
continue
151194

152195
img_bgr, err = _load_bgr_image_from_url(url)
153196
if img_bgr is None:
154197
merged_errors.append({
155-
"code": "LOAD_FAIL",
198+
"code": "E_LOAD_FAIL",
156199
"message": f"Failed to load image [{idx}] from URL. ({err})"
157200
})
158201
if debug:
@@ -168,9 +211,12 @@ def evaluation_function(response: Any, answer: Any, params: Params) -> Result:
168211
return_images=return_images,
169212
)
170213
except Exception as e:
214+
msg = f"Pipeline failed on image[{idx}]: {type(e).__name__}: {e}"
215+
if debug:
216+
msg += "\n" + traceback.format_exc()
171217
merged_errors.append({
172-
"code": "PIPELINE_RUNTIME_FAIL",
173-
"message": f"Pipeline failed on image[{idx}]: {type(e).__name__}: {e}"
218+
"code": "E_PIPELINE_RUNTIME",
219+
"message": msg
174220
})
175221
if debug:
176222
feedback_items.append((f"Input URL [{idx}]", str(url)))
@@ -202,12 +248,12 @@ def evaluation_function(response: Any, answer: Any, params: Params) -> Result:
202248
)
203249
except ImageUploadError as e:
204250
merged_errors.append({
205-
"code": "UPLOAD_FAIL",
251+
"code": "E_UPLOAD_FAIL",
206252
"message": f"Failed to upload {key} for image[{idx}]: {e}"
207253
})
208254
except Exception as e:
209255
merged_errors.append({
210-
"code": "UPLOAD_FAIL",
256+
"code": "E_UPLOAD_FAIL",
211257
"message": f"Failed to encode/upload {key} for image[{idx}]: {e}"
212258
})
213259
elif upload_image is None and debug:
@@ -216,23 +262,19 @@ def evaluation_function(response: Any, answer: Any, params: Params) -> Result:
216262
if debug:
217263
feedback_items.append((f"Input URL [{idx}]", str(url)))
218264

219-
# ---------------------------
220265
# 4) Decide correctness
221-
# ---------------------------
222266
has_E = any(str(e.get("code", "")).startswith("E_") for e in merged_errors)
223267
is_correct = (not has_E)
224268

225-
# ---------------------------
226269
# 5) Text feedback
227-
# ---------------------------
228270
if merged_summaries:
229271
feedback_items.append(("Summary", str(merged_summaries[-1])))
230272

231273
if merged_ratios:
232274
feedback_items.append(("Ratio", str(merged_ratios[-1])))
233275

234276
if merged_errors:
235-
lines = [f"- {e.get('code', 'ERR')}: {e.get('message', '')}" for e in merged_errors]
277+
lines = [f"- {e.get('code', 'E_ERR')}: {e.get('message', '')}" for e in merged_errors]
236278
feedback_items.append(("Issues", "\n".join(lines)))
237279

238280
if not feedback_items:
@@ -247,9 +289,18 @@ def evaluation_function(response: Any, answer: Any, params: Params) -> Result:
247289

248290
except Exception as e:
249291
# Absolute last-resort: never crash the platform UI
250-
items = [("Error", f"{type(e).__name__}: {e}")]
292+
tb = traceback.format_exc()
293+
safe_tb = _escape_html(tb)
294+
items = [
295+
("Stage", "UNHANDLED"),
296+
("ErrorCode", "E_UNHANDLED"),
297+
("ExceptionType", type(e).__name__),
298+
("Message", str(e)),
299+
("Traceback", f"<pre>{safe_tb}</pre>"),
300+
("Traceback(html)", safe_tb.replace("\n", "<br>")),
301+
]
251302
feedback_html = _items_to_feedback_html(items)
252303
try:
253304
return Result(is_correct=False, feedback=feedback_html, feedback_items=items)
254305
except TypeError:
255-
return Result(is_correct=False, feedback_items=items)
306+
return Result(is_correct=False, feedback_items=items)

0 commit comments

Comments
 (0)