3636from in2lambda_agent import package , pair
3737from in2lambda_agent .fix import RoundResult , fix_round , summary , unrepaired
3838from in2lambda_agent .mathpix import MathpixClient
39- from in2lambda_agent .model import Backend , ModelUnavailable , Usage , choose_backend
39+ from in2lambda_agent .model import (
40+ Backend ,
41+ ModelError ,
42+ ModelUnavailable ,
43+ Usage ,
44+ choose_backend ,
45+ )
4046from in2lambda_agent .ocr import MEDIA_NAME , cached , ocr_pdf
4147from in2lambda_agent .review import RECORD , Question , Review , choose
4248from in2lambda_agent .settings import Settings
@@ -176,6 +182,8 @@ def run(
176182 them when a conversion is needed and the run has no Mathpix
177183 credentials. A PDF already in the cache needs none.
178184 ModelUnavailable: If a spec must be written and no backend can run.
185+ ModelError: If a call did not finish, with `stage` naming which — the
186+ spec call or a fixing round.
179187 BadSpec: If what the model answers with is not a spec.
180188 SpecRejected: If in2lambda will not run the spec.
181189 SourceError: If in2lambda cannot freeze or check the source.
@@ -274,18 +282,24 @@ def run(
274282 if (reason := backend .unavailable ()) is not None :
275283 raise ModelUnavailable (reason )
276284 result .second = _second (source , cache_dir , solutions )
277- draft , coverage , report , result .tries = iterate_spec (
278- frozen ,
279- saved ,
280- backend ,
281- tries = tries ,
282- on_stage = result .add_stage ,
283- second = result .second ,
284- previous = previous ,
285- solutions = frozen_solutions ,
286- solutions_name = solutions .name if solutions is not None else "" ,
287- solutions_only = alone is not None ,
288- )
285+ try :
286+ draft , coverage , report , result .tries = iterate_spec (
287+ frozen ,
288+ saved ,
289+ backend ,
290+ tries = tries ,
291+ on_stage = result .add_stage ,
292+ second = result .second ,
293+ previous = previous ,
294+ solutions = frozen_solutions ,
295+ solutions_name = solutions .name if solutions is not None else "" ,
296+ solutions_only = alone is not None ,
297+ )
298+ except ModelError as error :
299+ # Which call did not finish, for a caller that names it: a spec call
300+ # and a fixing round both go to the same backend.
301+ error .stage = "spec"
302+ raise
289303 result .draft = draft
290304 result .coverage = coverage
291305 for one in result .tries :
@@ -295,7 +309,11 @@ def run(
295309
296310 # Layers 3 and 4, a round at a time. Reached only with a spec this run
297311 # wrote, so the backend is the one that wrote it.
298- report = _fix_rounds (draft , report , backend , rounds , result )
312+ try :
313+ report = _fix_rounds (draft , report , backend , rounds , result )
314+ except ModelError as error :
315+ error .stage = "fix"
316+ raise
299317 # What the corpus harness reads off the result rather than off the
300318 # record: set here so that a run that stops for a review carries them
301319 # too, since that return is above the record this run never writes.
@@ -406,6 +424,7 @@ def resume(
406424 Raises:
407425 ReviewError: no review is waiting, or none of its questions is `key`.
408426 ModelUnavailable: a rejection has no backend to answer its note with.
427+ ModelError: a rejection's fixing round did not finish.
409428 CommandRefused: in2lambda would not make the reviewer's edit.
410429 """
411430 cache_dir = Path (cache_dir ).resolve ()
@@ -488,14 +507,18 @@ def resume(
488507 raise ModelUnavailable (reason )
489508 # The note is a finding of its own: the checks are quiet, and it is
490509 # what the round is for. Rounds after it answer what they leave.
491- report = _fix_rounds (
492- draft ,
493- package .validate (draft ),
494- backend ,
495- waiting .limit ,
496- result ,
497- instruction = f"The reviewer rejected { key } : { note } " ,
498- )
510+ try :
511+ report = _fix_rounds (
512+ draft ,
513+ package .validate (draft ),
514+ backend ,
515+ waiting .limit ,
516+ result ,
517+ instruction = f"The reviewer rejected { key } : { note } " ,
518+ )
519+ except ModelError as error :
520+ error .stage = "fix"
521+ raise
499522 relisted = [key ]
500523 else :
501524 package .command (
0 commit comments