Repository navigation
feat(standard): let a benchmark keep its tasks gated, pin its own providers, set its own limits and prove a merged null control - #133
Draft
sirotaoleg wants to merge 26 commits into
Conversation
On OpenRouter one model is served by several providers, and a run could
neither say which may answer nor see which one did. `--served-by
[MODEL=]SLUG[,SLUG...]`, repeatable, names them for a model (the run's
when MODEL= is left out). Every relay of the run, a container's sidecar
included, puts `provider: {order: [...], allow_fallbacks: false}` on each
request it sends as that model, on any route and in the three API shapes,
in place of the caller's own routing field. A pinned Chat Completions
request asks for OpenRouter's routing facts, where its reply names the
provider that served it. Without the option a request goes on byte for
byte as before.
The option is refused before any unit when the run's provider routes no
model to several (only openrouter does), when it names a model the run
does not call, or when a slug is malformed or given twice.
Each call's record keeps the routing sent (and the caller's own when it
was replaced), the serving provider the reply names, how the reply ended
and an error the reply holds under a 200, which now makes the call a
failed one. The job's config records the pin by model; each trial and
the job say the answered calls by serving provider and, for a pinned
model, whether every one was served by a pinned provider. merge refuses
shards whose pins differ, unless --force.
The run page gains the option and its rules: the slugs, what the relay sends, the refusals, the record and how a pin reads as held. The standard page names the record's new fields and the pin among merge's checks, and the providers page says that a run may choose OpenRouter's serving providers.
…ge no outcome without a pin A review found the pin's record could copy credentials, miss calls and change what a run without the option records and decides. Now: - The facts of a reply (who served it, how it ended, an error under a 200) are read by reply_facts, apart from read_reply, which is as before: the text, tool calls and usage a record held are read as they were, and no fact can raise into the delivery of a reply. Every choice is read for its end and its error, by index; a top-level error is read whatever else the reply holds; a completed Responses event's routing facts name who served it; a fact of the wrong type is left out. - An error under a 200 is recorded beside the reply and nothing else follows from it: the call keeps its status and failure reads it as before. - The recorded routing objects and facts are cleaned: a value under a credential's name, the relay's own key and key-shaped strings are removed. What is sent is unchanged. - The routing-facts header goes to OpenRouter alone, as enabled whatever the caller sent. - One check of a pin, shared by the run, its command line and the relay's own command; a control is refused a pin on a model it never calls. - Who served a unit counts its judge's and simulated user's routes, and the job the benchmark's own relay's calls; the tokens and the cost stay the agent's. A trial whose answered calls name no provider keeps its count. A name reads as a slug by its case and spaces alone. - merge compares pins as mappings, and a forced merge of different pins reads held unknown for such a model.
…held cannot tell The pages now say that an error under a 200 is recorded and changes nothing else, that the record's copies are cleaned of credentials, that who served a run counts every route, how a name reads as a slug, and that held names a provider, never its endpoint variant.
… it must refuse a pin
A run started from the builder could not pin its serving providers. On OpenRouter, the Model step now shows Served by under each model field (the model, the user simulator's and the judge's): the providers that may answer that model, tried in their order and no other, typed as slugs or added from the model's providers. Empty, the default, pins nothing. The recipe holds them as served_by, by field. The server checks them as --served-by is checked, refuses them for another provider or for a model no job calls, and compiles them into --served-by for each job that calls that model, by the name the run sends it as. Review shows them in its Model row. A saved recipe keeps them, and choosing another provider drops them. A recipe with no pin compiles to the command it did. The model's providers come from a new route, POST /new-run/endpoints, which asks OpenRouter's endpoint list as /new-run/models asks the model list: local and form-token guarded, the key kept on this machine and sent when set, the answer cached by the page for five minutes, and a fixed message when the list cannot be had, the field then taking a typed slug.
… model's providers
…del it was set for A review of the pin found records and summaries that could read held where another provider answered, credentials that could reach a record, and pins that the builder dropped or moved to another model. Now: - The provider recorded is the one of the reply's end: a stream's latest event that names one, and the last attempt of OpenRouter's routing facts; an earlier, failed attempt is never taken, and a last attempt that names none gives none. - An error keeps its code, type and message alone, of those kinds; the rest of the object is not kept. A recorded routing object's keys are cleaned as its values are. - A merged job's summary is made of each shard's whole one, its benchmark relay's calls and an unknown included. - A recipe's pins are resolved by model, then given to every job that calls that model, a control's judge job included. The compile route returns each field's model as the run sends it, and Served by lists that model's providers. - A control is refused a pin on its simulated user's model, which it never calls. relay serve refuses a pin unless its base is OpenRouter's (or --routing is given) and the model is one it sends; a container's relay is given the pins of the models it sends. - The builder clears a field's pin when its model changes, a preset or a benchmark included; keeps a saved pin as written, for the server to refuse what is wrong in sight; keeps an empty part of a typed list; puts its help in a tooltip; and keeps the keyboard on the model field once the form is drawn again.
…d where held reads unknown A variant may be a quantisation, a speed or a region. Of several choices' errors the record keeps the top-level one, else the first choice's. A provider whose name is not its slug once lowercased reads unknown though it served the call; the example of a name that holds is DeepInfra.
…facts mark as selected OpenRouter's routing facts list the endpoints available for a request and mark the one that served it as selected; they hold no list of attempts. The relay now reads that entry, at the top of a reply and in a completed Responses event's response, and asks for the facts on a pinned Responses request too: a Responses reply names its provider there alone, so a pin on a model an agent calls through Responses could never read as held.
… pin no provider serves gets 404, and why held may read unknown
…ake no name a chunk gave before
… asks for the routing facts and where held reads unknown
main gained the OSWorld1 benchmark and the run option --units since this branch started. Seven files met: the command line, the job's record, merge, the run and three pages. Both sides are kept whole, main's first: a run takes --units and --served-by together, config.json records standard.units_file and standard.served_by, and merge compares both (different unit lists are always refused, different pins unless forced).
…s record A run given --units and --served-by from the command line records both in config.json, with and without --from-run.
…n the field guide The field guide names both config fields, the routing fields of a call, the serving counters and held, and says that merge refuses different unit lists always and different pins unless forced.
main gained the settings that say what Nullius may use on each machine, with the run option --constraint, and a fix of the settings page. Seven files met: the command line, the job's record, the recipe, the run, a test module and two pages. Both sides are kept whole, main's first: a run takes --constraint and --served-by together, config.json records standard.constraints and standard.served_by, and a recipe compiles each target's constraints and the pins of the models its jobs call.
Seven cases where the two meet: both records from the command line, with and without --from-run; the order of the refusals; a scope's enforcement with a valid pin; a container relay's caps beside its pins; each target's compiled command; the builder's checks; and shards whose machine limits differ.
…ving pin in the field guide The field guide says that standard.constraints, standard.units_file and standard.served_by may stand together in config.json, and which of them a merged job keeps from its first shard.
…on top of --served-by: gated tasks, a definition's pins as its serving pins, a pinned route's refusal as the unit's, limits of their own, and prove of a merged control A definition's pin (setups.original.pin, score.judge.pin) joins the run's --served-by pins and wins over --served-by for its model; ApiProviderRouteError reads the pin and reply.served_by. A failed Responses object reads reply.end and reply.error.
…eep the score of a unit a guard stopped despite a last 429 limits.run_again false: a unit that ended rate-limited is not run once more; its outcome stands. A unit whose driver scored it after a guard's stop keeps that score, marked stopped, though its last own call got a 429.
… own last 429, not a side call's or a context window's stop The rule reads the agent's own last call, a guard's line in the stop file and a score stopped by other than the context window; a cap's refusal is still the cap's. limits.run_again takes null as unset. The rate limits row names the opt-out.
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 #124: its commits come first, and this pull request's own are the last three. Until #124 is merged, the diff shows its files too, the builder's among them. It can be merged after #124.
What
A benchmark of the standard can now say more about how its units run and end, which a computer-use benchmark like OSWorld2 (#134) needs. The pins,
gated, the new limits andprove's new options are opt-in. The relay's rules and the rules of how a unit ends change for every benchmark of the standard; Risk lists them.Gated tasks (
gated: true): a unit's failure that the relay's records explain, a model endpoint's (the agent's, the judge's or the simulated user's), says in itsresult.jsonthe status and class alone, never the provider's words, which can quote the request. The trial's call records keep the provider's words, and an error a driver raises itself is kept as the driver words it.A definition's providers (
setups.original.pinfor the Original's models,score.judge.pinfor the official judge; OpenRouter only) join the run's--served-bypins (feat(standard): choose the serving providers that may answer a model, in the builder and on the command line #124) and win over--served-byfor the same model: a run of the Original and its official judge always go to the providers the paper used. The pin follows the model sent, so a model the agent shares with the judge goes to the judge's providers. A refused route fails the unit withApiProviderRouteError, an API error outside the agent's rate, not a miss:reply.served_byshares nothing with the pin's slugs. A name that is not its slug (Googleforgoogle-vertex) fails nothing on a guess.The agent's own last call's error comes first, and a setup error stays a setup error. Three units in a row whose pinned route has no endpoint stop the run, as a refused key does. A user's
--served-byon any other model behaves as in feat(standard): choose the serving providers that may answer a model, in the builder and on the command line #124.Limits of a benchmark's own:
limits.score_seconds: how longscoremay take;limits.call_seconds: how long the relay waits for one model call, alsorelay serve --timeout;limits.run_again: false: a unit that ended rate-limited is not run once more, and its outcome stands;limits.turn_seconds, read before, is now documented.How a unit ends:
EnvironmentSetupError:,VerifierError:,CancelledError:orCauseUnknownError:keeps that kind;ContextWindowExceededError, the agent's;scorecan say the stop's reason (verifier_result.stopped);OutputLengthExceededErroronly when its last reply has neither text nor a tool call. When the answerer fails and its last reply was a Responses object answered 200 andfailed, that is an API error.The relay:
answered: 200). feat(standard): choose the serving providers that may answer a model, in the builder and on the command line #124 leaves counting an error inside a 200 to a pull request of its own; this one takes only a 200 with no reply in it;stopfile beside the relay's records: the relay notes in it an agent's call refused for the model's context window, and that note refuses nothing. Once a benchmark's guard writes a line of its own there, the agent's later calls are refused unsent, while side models and the judge's and simulated user's routes go on;A job's name:
.work/relay,result.json,verifier/,agent/) aside to.work/earlier.<ns>.prove and merge:
nullius standard prove ... --bench-option NAME=VALUEsets the benchmark's own options for the units check and the controls;--job nop=MERGED(ororacle=) holds a merged launch of a control to its bound instead of running it, checking every trial's own record;mergeaccepts shards that differ in benchmark options and keeps each shard's inmerged_from;mergerefuses, unless--force, a trial whose record names another job, benchmark, version, control or agent, and a job itself merged despite differences. It lists the shards absent from a merge only when the launch has at most 1,000.Smaller:
agent/trace.error.txt;thinking_tokenscount as reasoning;comparablecan list the models whose runs compare with the paper, and the builder's message for another model says so;Read first:
failure,route_failureandroutes_failure, then_hollowand the stop inRelay._forward(app/nullius/standard/relay.py). Next,pins_of,route_pinsand the outcome rules inRun(app/nullius/standard/run.py), then the--jobcheck inprove.pyand the trial check inmerge.py. Thenapp/tests/test_standard_relay_pin.py(classesPinnedandWired),test_standard_gated.py,test_standard_context_window.pyandtest_standard_controls.py. The rest is the definitions' checks, the CLI options, the sidecar's argument and the pages.Touches
the standard (
app/nullius/standard/:relay,run,prove,definitions,merge,job,cli,container,recipe,fidelity) · docs and site (docs/standard.md,docs/cli/run.md,docs/cli/read-jobs.md)Verified
369054a8, on Python 3.12: backend suite (python3 app/tests/parallel.py), 2114 tests in 121 modules, OK; route suite with Node on PATH, 433 tests in 41 modules, OK.468406d8): 10 fail, 2 pass, and 1 does not end.test_standard_relay_pin: all 15;test_standard_context_window: 19 of 20;test_standard_controls: 17 of 43;test_standard_relay: 8 of 57;test_standard_gated: 4 of 6.test_standard_run: 2 of 50;test_standard_trace: 2 of 20;test_standard_parts,test_standard_served_byandtest_standard_units: 1 each.test_standard_sharddoes not end within 4 minutes: one of its merges names 2^64 shards, and feat(standard): choose the serving providers that may answer a model, in the builder and on the command line #124'smergelists every absent one.test_standard_versionsandtest_dependency_surfacepass on both.WiredorPinnedfail:--served-bypin;setups.original.pinis checked and reaches the run, and thatlimits.call_secondsreaches the relay and the sidecar's--timeout. OSWorld2 is the first entry to set them (test_osworld2_muse.py, and the tests it adds to the classDefinitionoftest_standard_relay_pin.py).test_standard_served_bypasses as feat(standard): choose the serving providers that may answer a model, in the builder and on the command line #124 wrote it.PYTHONPATH=app python3 -m nullius standard check: ok.python3 app/tests/catalogue_docs.py --write, then the check: up to date.python3 app/tests/field_contract.py: ok.app/site/readme_media.py --check: current. The docs check reports the same two findings inAGENTS.mdas on feat(standard): choose the serving providers that may answer a model, in the builder and on the command line #124's branch.c3637ffdis this code with main's two later commits merged in and OSWorld2 on top, a commit of the port's working branch:c3637ffd, the null control on one task with an LLM grader: the grader's call went out withrequest.routing{"order": ["anthropic"], "allow_fallbacks": false}from the definition's pin,reply.served_bynamed Anthropic, andstandard.servedreadsheld: true;c3637ffdplus a test commit that adds one more model to OSWorld2's Original with its pin to MiniMax, an agent episode: all 68 calls named their provider,held: true, no error.--timeout; the docker tier; the site build; a wheel's check.Risk
ContextWindowExceededError), where it was an API error outside its rate;rate_limitedand runs it once more, where its score stood;failedResponses object is an API error, where it was the agent's;proveruns no control when the units check failed, and fails a control job with a unit that has neither a score nor an error;mergerefuses a trial whose record names another job unless--force.468406d8), with no conflict. Once feat(standard): choose the serving providers that may answer a model, in the builder and on the command line #124 is merged, this branch is rebased on main.