Repository navigation
fix(task): stop retrying a model key the endpoint refuses (budget or auth) - #698
Draft
andrewpark-scaleai wants to merge 1 commit into
Draft
andrewpark-scaleai wants to merge 1 commit into
andrewpark-scaleai wants to merge 1 commit into
Conversation
…udget or unauthorized ModelKeyUnusable(cause, where) is raised by the rubrics judge (direct LiteLLM and A2A) and by prompt_agent on model_key_budget / model_key_auth. The judge loop makes one call, and a step's retry_config does not rerun it.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #695 (base is that branch, not
main). #695 adds themodel_key_budgetandmodel_key_autherror types this PR reads.Why
Goal: give agentenv enough signal to debug every failure and heal itself where code can. This PR stops runs from spending tokens on a model key that cannot work.
Today nothing knows a budget or auth refusal is permanent. The rubrics judge retries a refused LiteLLM call
DEFAULT_MAX_RETRIES(3) times, so each grader step spends about 2.7 judge calls on a dead key. A step withretry_configrolls back and runs the same agent turn again with the same key. Each retry also redeploys sandboxes.What
ModelKeyUnusable(cause, where)intask_step/context.py, next toVerifierNotPassingError.causeis"budget"or"auth". The message is fixed text with no key material, and it drops the original exception chain, because a 401 body can echo the key._prompt_llm_judgeclassifies a LiteLLM error once in_model_key_refusal: a budget-exceeded error or message marker givesbudget, a 401 givesauth. It raisesModelKeyUnusableon the first such error. Every other error, including 429 rate limits and 5xx, retries as before._invoke_judge_a2araisesModelKeyUnusablewhen the judge task failed witherror_typemodel_key_budgetormodel_key_auth.prompt_agentrecords the failed turn, then raisesModelKeyUnusablefor those twoerror_typevalues instead of the plainRuntimeError.ModelKeyUnusable, the same way it skipsVerifierNotPassingError.ModelKeyUnusablesubclassesRuntimeError, so callers that catchRuntimeErrorstill see it. Behavior changes only for a refused key.Design decisions and tradeoffs
One attempt, even if the key gets fixed mid-run. A key can be topped up or rotated while a run is in flight, and one attempt gives that up. I chose it because a budget or auth refusal does not clear within a retry window of seconds, and the cost of being wrong is one failed step that a person can rerun. The cost of the old behavior is every step in every run burning its full retry budget. Waiting out a key reset belongs to a later breaker, not to blind retries.
Parse at the boundary. The A2A paths branch on the typed
error_typefrom #695 and never read message text. A direct LiteLLM call has no typed field, so_model_key_refusalparses the exception once at the call site, and the retry loop sees only a cause. A message that merely mentions "budget" in another context can match the marker, so the markers are LiteLLM's own budget phrases. The auth case uses the 401 status orAuthenticationError, and a 403 or 429 does not count.Not done here. The hub breaker, preflight checks and the pass@k pause are separate work. This PR only stops the retries.
How to verify
Before the change (with only the exception class present), 6 tests fail: a refused judge key is called 3 times and raises
RuntimeError, the A2A judge andprompt_agentraiseRuntimeError, and the step withretry_configruns 4 times. After, all 52 pass. The transient-error tests (503 then 429 then success, and a plainagent_error) pass both before and after, which pins that nothing else changed.Follow-up for downstream workers
A worker that retries activities in place needs to treat
ModelKeyUnusableas non-retryable once this ships in a release. In the worker that consumes this package, add it to theretry_cannot_helpexception set, next toVerifierNotPassingError.