Repository navigation
feat(llm): opt-in Nace Drex Max decision model for bounded-choice prompts - #9090
Open
saruar-dev wants to merge 2 commits into
Open
saruar-dev wants to merge 2 commits into
saruar-dev wants to merge 2 commits into
Conversation
…mpts Drex Max (console.nace.ai) is a decision model: one POST /v1/systemone request carries a state text, up to eight images and bounded questions (noul yes/no, choice over fixed options) and returns a probability per option instead of generated text. Several Skyvern prompts are bounded decisions already, so this adds an opt-in caller that rewrites them into System One questions and rebuilds the JSON their parsers expect: - check-user-goal / check-user-goal-after-click: noul -> user_goal_achieved - check-user-goal-with-termination: choice complete/terminate/continue (+ failure category when terminating) - decisive-criterion-validate: choice COMPLETE/TERMINATE -> actions[] - page-classify: choice over the options rendered in the prompt - infer-action-type: choice over the action types, ranked by probability - auto-completion-choose-option: noul attempt + choice over the element ids rendered in the prompt (+ direct_searching for search bars) Settings ENABLE_NACE / NACE_API_KEY / NACE_API_BASE / NACE_MODEL register the NACE_DREX_MAX LLM key; LLMCaller dispatches it to _call_nace_decision (like UI-TARS and Yutori Navigator) so artifacts, token accounting and error classification are unchanged. forge_app builds DECISION_LLM_API_HANDLER only when enabled, and the bounded call sites prefer it through get_decision_llm_api_handler(default), which returns the default handler when the opt-in is off. Organization defaults and per-run llm_key overrides still wrap the result. The action-planning loop is untouched; any other prompt sent to the key is rejected before a request is made. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Unit tests with a mocked HTTP transport: System One request shapes (noul and choice only, string instructions, choice criteria as an object of strings, images as a top-level list capped at eight), answer decoding for every supported prompt, HTTP error classification (4xx deterministic, 429/5xx/529 retryable), the chat-shaped ModelResponse handed back to LLMCaller, factory routing of the NACE_DREX_MAX key away from litellm, input-token-only cost, and the ENABLE_NACE fallback helper. Also anchors the fenced-block lookup to whole heading lines, since the auto-completion template mentions its own headings inside the reply-format comment, and narrows a usage type that mypy flagged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
Preview approval requiredThis pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit |
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.
Description
Adds Nace Drex Max as an opt-in decision model for Skyvern's bounded-choice prompts. Drex Max is not a chat model: one
POST /v1/systemonerequest carries astatetext, up to eight images and a few bounded questions (noulyes/no,choiceover fixed options), and the reply is a probability per option instead of generated text. Several Skyvern prompts are bounded decisions already, so this PR rewrites them into System One questions and rebuilds the JSON object their existing parsers expect.LLM_KEYkeeps doing the action planning and extraction; the main action-planning loop is untouched.Disclosure: I work at nace.ai, which serves Drex Max.
What's included
skyvern/forge/sdk/api/llm/nace_decision_llm_caller.py(new) — the System One recipes and a smallhttpxclient, following the shape of the UI-TARS / Yutori Navigator callers (Add Yutori Navigator as a CUA engine #5513):check-user-goal,check-user-goal-after-click→ onenoul→user_goal_achievedcheck-user-goal-with-termination→choicecomplete/terminate/continue (+ a failure category when terminating)decisive-criterion-validate→choiceCOMPLETE/TERMINATE →actions[]page-classify→choiceover the options rendered in the prompt (includingUNKNOWN)infer-action-type→choiceover the action types,inferred_actionsranked by probabilityauto-completion-choose-option→noulfor the attempt +choiceover the element ids rendered in the prompt (restricted to the emerging ids when present,NONEoption) +direct_searchingfor search barsimagesarray (data URLs, first 8 kept); the prompt text is thestate; choicecriteriais an object of strings. Any other prompt is rejected before a request is made.thoughts/reasoningfields carry the decision and its probability, since the model generates no text.LLMCaller._dispatch_llm_callsends theNACE_DREX_MAXkey to_call_nace_decision(like_call_ui_tars/_call_yutori_navigator), andget_llm_api_handlerbuilds that key as anLLMCallerso it never reacheslitellm.acompletion. Artifacts, token accounting (get_call_statsprices input tokens only, as billed) and error classification (4xx deterministic, 429/5xx/529 retryable) stay in the base class.ENABLE_NACE,NACE_API_KEY,NACE_API_BASE(defaulthttps://console.nace.ai),NACE_MODEL(defaultdrex-max) inconfig.py,.env.example; registry entry underif settings.ENABLE_NACE.forge_app.py—NACE_CLIENTandDECISION_LLM_API_HANDLER, bothNoneunless enabled (a missing key logs a warning and keeps the defaults).get_decision_llm_api_handler(default), which returnsdefaultunlessENABLE_NACEis on: complete verification andinfer-action-typeinagent.py, the after-click check and bothauto-completion-choose-optioncalls inwebeye/actions/handler.py,page-classifyandinfer-action-typeinreal_skyvern_page_ai.py. Organization defaults and per-runllm_keyoverrides still wrap the result, so they win over the deployment-wide opt-in.decisive-criterion-validateis rendered inside the planning path, so it is only answered by Drex Max when a run selects theNACE_DREX_MAXkey through itsllm_keyoverride.docs/reference/configuration.mdx.Nothing changes for deployments that do not set
ENABLE_NACE=true. Not included on purpose: theskyvern initwizard, per-prompt-type routing settings, and any benchmark numbers.How Has This Been Tested?
Unit tests in
tests/unit/test_nace_decision_llm_caller.py(mockedhttpx.MockTransport, no network): request shapes (noul/choice only, string instructions, choice criteria as an object of strings, images capped at eight), answer decoding for every supported prompt against the real templates, HTTP error classification, the chat-shapedModelResponseround-tripping throughparse_api_response, factory routing away from litellm, input-token-only cost, and theENABLE_NACEfallback helper.Two manual live calls against
https://console.nace.ai/v1/systemonethroughcall_nace_decision(apage-classifyprompt with a synthetic login-form screenshot, and acheck-user-goalprompt with the same screenshot) returned{"classification": "LOGIN", "confidence": 0.998, ...}and{"user_goal_achieved": true, "confidence_float": 0.984, ...}withusage.input_tokenspopulated, confirming the request shape the docs specify (object criteria of strings, top-levelimagesarray, noboxquestion type).Not run: the full unit suite, the Playwright/integration suites, and
pre-commit run --all-files(which with ruff 0.16 reports pre-existing findings in the large files this PR touches).Artifacts (if appropriate):
Example parsed replies from the live calls above:
{"reasoning": "Drex Max decision: classification LOGIN (p=0.998)", "classification": "LOGIN", "confidence": 0.998} {"thoughts": "Drex Max decision: user goal achieved (p=0.984)", "user_goal_achieved": true, "confidence_float": 0.984}🤖 Generated with Claude Code