Visione: quota non prodotta salvata come NaN (value NULL, fail) invece del 422; migrazione 022
Refs #118 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -693,23 +693,13 @@ async def execute_task(
|
||||
)
|
||||
outputs = report["outputs"]
|
||||
|
||||
# Every quote or none. A half-measured piece read as a whole one is worse
|
||||
# than a piece not measured at all. A quote counts as produced only if its
|
||||
# output is present *and* a finite, non-bool number: the in-process runner
|
||||
# already drops NaN/inf itself (see src/vision/runner.py), but the worker
|
||||
# is reached over HTTP - a different implementation of it, or a malformed
|
||||
# response, could still hand back None or a non-finite float for a mapped
|
||||
# key instead of omitting it outright.
|
||||
missing = [q.vision_output for q in quotes if not _is_measurable(outputs.get(q.vision_output))]
|
||||
if missing:
|
||||
failed = ", ".join(f["tool_name"] for f in report["failures"])
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
||||
detail=(
|
||||
f"the graph did not produce {', '.join(missing)}"
|
||||
+ (f" (tools that failed: {failed})" if failed else "")
|
||||
),
|
||||
)
|
||||
# #118: una quota che il grafo non produce non blocca piu' l'esecuzione
|
||||
# (prima 422 "the graph did not produce"): viene salvata con value NULL,
|
||||
# mostrata NaN, sempre `fail`; i motivi restano nei `failures` del risultato.
|
||||
# Una quota conta come prodotta solo se l'uscita c'e' *ed* e' un numero
|
||||
# finito non-bool: il worker risponde via HTTP, un'altra implementazione o
|
||||
# una risposta malformata potrebbe dare None o un non finito per una chiave
|
||||
# mappata invece di ometterla.
|
||||
|
||||
overlay = report.get("overlay") or []
|
||||
image_path, overlay_path = save_result_files(task.id, image_bytes, overlay, image_ext)
|
||||
@@ -737,7 +727,11 @@ async def execute_task(
|
||||
subtask_id=quote.id,
|
||||
version_id=version_id,
|
||||
measured_by=measured_by,
|
||||
value=float(outputs[quote.vision_output]),
|
||||
# #118: None = quota non prodotta (NaN), salvata come fail
|
||||
value=(
|
||||
float(outputs[quote.vision_output])
|
||||
if _is_measurable(outputs.get(quote.vision_output)) else None
|
||||
),
|
||||
lot_number=lot_number,
|
||||
serial_number=serial_number,
|
||||
input_method="camera",
|
||||
@@ -762,8 +756,9 @@ async def check_task(
|
||||
VisionResult, niente file, niente misure. L'esito per quota usa
|
||||
calculate_pass_fail sulle quote con vision_output, nell'ordine di
|
||||
marker_number come execute. Le quote che il grafo non produce (pezzo non
|
||||
ancora in posizione) restano fuori da `measurements` e il perche' sta in
|
||||
`failures`: un'anteprima non deve dare 422 a ogni frame.
|
||||
ancora in posizione) entrano in `measurements` con `value` None (NaN) e
|
||||
`pass_fail` "fail" (#118, come execute); il perche' sta in `failures`:
|
||||
un'anteprima non deve dare 422 a ogni frame.
|
||||
"""
|
||||
if task.task_type not in VISION_TASK_TYPES:
|
||||
raise HTTPException(
|
||||
@@ -809,6 +804,7 @@ async def check_task(
|
||||
**worker_kwargs,
|
||||
)
|
||||
outputs = report["outputs"]
|
||||
# #118: una voce per ogni quota, in ordine di marker_number; non prodotta -> NaN/fail
|
||||
measurements = [
|
||||
{
|
||||
"subtask_id": q.id,
|
||||
@@ -816,8 +812,10 @@ async def check_task(
|
||||
"pass_fail": measurement_service.calculate_pass_fail(
|
||||
float(outputs[q.vision_output]), q,
|
||||
)[0],
|
||||
} if _is_measurable(outputs.get(q.vision_output)) else {
|
||||
"subtask_id": q.id, "value": None, "pass_fail": "fail",
|
||||
}
|
||||
for q in quotes if _is_measurable(outputs.get(q.vision_output))
|
||||
for q in quotes
|
||||
]
|
||||
return {
|
||||
"outcome": report.get("outcome"),
|
||||
|
||||
Reference in New Issue
Block a user