Repository navigation
Conversation
Move PrefixPlan and PrefixMode from the Decider worker into the shared crate, behind a small PrefixRow trait (a row's tokens and whether two rows belong to the same group), so other Qwen3.5 workers can plan forward_shared the same way. Decider re-exports them and implements PrefixRow for its rows; its planning, switches and counters are unchanged. One test now names the row type for an empty slice.
OPEN_JEV_PREFIX=1 plans the candidates' shared token prefixes with the shared Qwen planner and runs each prefix once through forward_shared. OPEN_JEV_PREFIX=auto shares only when Decider's auto rule holds, and OPEN_JEV_FIXED=1 runs every candidate as its own fixed-algorithm pass. The switches default off and are refused with CUA_S1_GRAPH=1. With a switch set, the response's prefix_cache metadata reports how the request ran.
|
I replayed recorded System1-Agents decisions against this branch, on the same RTX 6000 Ada: 28 Google Flights episodes (98 decisions) and 36 runs of its local form fixtures (108). They were recorded with Open-Jev-9B deciding and a local chat model planning. Each request is resent as recorded, one agent at a time, with its think time.
|
|
Thanks for this. Just two small non-blocking follow-ups:
|
Shared prefixes reproduce OPEN_JEV_FIXED=1 only on GPUs where the fixed GEMM algorithms keep each row independent of the row count. Check that for the loaded model's projection shapes when OPEN_JEV_PREFIX is set and refuse to start otherwise, instead of relying on the documented manual kernel test alone.
|
Thanks, both done.
One note on the replay numbers: those answer changes come from the fixed algorithms rounding differently from the default ones, which happens on any GPU. The startup check covers the other risk, shared results not matching fixed ones. |
Required human contributor answers
What outcome are you trying to achieve?
Run faster on the native Open-Jev worker for requests that ask several questions about the same thing.
What is the highest-leverage variable for that outcome?
How many prefix tokens each request prefills.
How will this work move that variable rather than create busywork?
Make Open-Jev use
forward_shared: when many questions or candidate answers are asked about the same page, the whole shared context is shared.Who is the customer or user, and what do they actually need?
agent using Open-Jev to ask several questions about one same thing such as a web page, and they need faster answers.
After answering the four questions: is a PR needed at all to achieve this outcome?
Yes, to answer #85.
For a bug fix: link actual observed failure evidence.
N/A, not a bug fix.
Purpose
For #85, as said on #150: Open-Jev moves onto
forward_shared(#99), behind opt-in switches like Decider's (#113).Open-Jev scores each candidate as its own prompt: chat prefix, state, question, then the candidate. So every candidate of a request repeats the state, and a question's candidates also repeat the question. Open-Jev-9B runs each candidate as its own pass; Open-Jev-27B-v1.1 packs them, but every token still runs.
PrefixPlanmoves into the shared Qwen crate (src/models/qwen3_5/native/src/prefix.rs) behind a smallPrefixRowtrait: a row's tokens, and whether two rows are in the same group. Decider implements it for its rows and re-exports the planner; its planning, switches and health counters are unchanged. Open-Jev's rows are its candidates, grouped by question.DECIDER_PREFIXandDECIDER_FIXED, both off by default:OPEN_JEV_PREFIX=1: each shared prefix runs once throughforward_shared, and each candidate continues from the state after it. A request with less than one reusable 64-token chunk runs its candidates one at a time withforward_fixed.OPEN_JEV_PREFIX=auto: shares only when the plan saves at least 4,096 tokens and at least a third of the request's tokens, Decider's rule; other requests take today's path (packing on 27B, one pass per candidate on 9B).OPEN_JEV_FIXED=1: every candidate withforward_fixed, the control for shared results.CUA_S1_GRAPH=1, since these paths run eagerly.OPEN_JEV_PREFIX=1orauto, the worker first checks that the fixed GEMMs give each row the same result whatever the row count, for the model's own projection shapes (subsets of 4,096 random rows run alone against the full call, byte for byte), and refuses to start if not. Shared results equal fixed ones only where that holds.metadata.prefix_cacheis{"enabled": <ran on shared prefixes>, "mode": "fixed" | "shared" | "auto", "saved_tokens": <prefix tokens not recomputed>}. With both off it stays{"enabled": false, "mode": "independent_candidates"}. Usage still counts every candidate's tokens.recipe/open_jev/native.md, the measurements inrecipe/open_jev/validation-9b.md, and a sentence each indocs/architecture.mdand both workers' READMEs.Not here: running a prefix's branches in one batched pass (#150's next stage), and any run on 27B.
Test Plan
System1-Omni Version / Commit:
e02eca4onmaina41e379;2a19ed2adds the startup check, ande02eca4only records the tests below in the validation notes. The five-way measurement ran on an earlier state of this branch onmain7b4c194, before theCUA_S1_GRAPHcheck, the self-review fixes to tests and docs, and the rebases. With the same code onmain51f80ad(#134 in), the GPU tests pass and the no-switch, shared andautoresponses equal that run's; #165, the only later commit onmain, adds a Python worker and docs. The interleaved timing check and the memory check ran with the same worker code onmain27176cb(#129). The CPU checks pass one02eca4.CPU: the CI steps (format, Clippy, frontend tests, workspace tests, release build, Python checks) and
mkdocs build --strict. New tests cover the switches and the graph refusal, candidates grouped by question with the planner rebuilding every prompt in order, andprefix_cachein each mode. The planner's own tests stay intests/decider/prefix.rs.GPU, one RTX 6000 Ada (sm_89), Open-Jev-9B merged export, eager:
tests/open_jev/prefix.rs(new): on a two-question request whose Choice candidates share a question prefix past the next 64-token boundary, shared scores equal fixed scores bit for bit, twice, and save more than the request prefix alone would; underauto, a small request isn't shared and equals the default path. Also perf(open-jev): pack candidate projections within requests #102'sprefill_batchtest, onmainand here.mainand through this worker with no switch, fixed, shared and auto. Responses compared with each other, and JevBench answers with the reference from open_jev: support the Open-Jev-9B checkpoint #92's validation and with the labels.mainwith requests interleaved A B B A, so slow clock and temperature drift hits both.OPEN_JEV_PREFIX=1.Test Result
CPU. All pass on macOS.
GPU tests.
prefixpasses;prefill_batchpasses onmainand here. WithOPEN_JEV_PREFIX=autoandCUA_S1_GRAPH=1the worker refuses to start. Both tests andfixed_gemm_rows_do_not_depend_on_mpass again at01c84c6and at2a19ed2; on the 9B export the startup check takes 0.37 s.Responses.
main's apart from timing.OPEN_JEV_PREFIX=1: the 95 requests with a reusable chunk ran on shared prefixes, and all 253 responses equalOPEN_JEV_FIXED=1's apart from timing andprefix_cache.auto: the 46 requests it shared equal the fixed responses; the other 207 equal the no-switch ones.Fixed against default GEMMs. Shared results equal fixed ones, and fixed ones differ from today's by rounding, about as far as today's path is from the reference:
noul(74)The changed decision is a near tie that #92's validation already lists: 0.52/0.48 with today's GEMMs, 0.46/0.54 fixed, 0.44/0.56 in the reference. Against JevBench's labels: reference 165 of 231, today's path 163, fixed and shared 164,
auto163.Time, sums of per-request client times (the faster of two sends), in seconds:
mainautonoul(74)support_28: 1.06 s instead of 1.88 s.OPEN_JEV_PREFIX=1, requests with nothing to share pay that (thenoultasks: 6.16 s instead of 5.74 s);autokeeps them on today's path.auto's threshold is Decider's and leaves gains: M2's requests of 2,600 to 5,500 tokens run 2 to 3 times faster shared but save fewer than 4,096 tokens. I kept Decider's rule rather than tune it on these requests.main: sent alternately to both (A B B A), the 253 requests took 393.5 s against 392.8 s, median per-request ratio 1.001 (0.99 to 1.02 from the 5th to the 95th percentile), responses identical. The 1% gap in the table above, up to 11% on the first requests with many short candidates, is drift between the two consecutive runs.Memory. After warmup, and after the 22 M1 to M3 requests and the five longest JevBench tasks: 15,874 and 16,674 MiB with no switch, 15,906 and 16,884 MiB with
OPEN_JEV_PREFIX=1. The difference is the fixed-algorithm GEMM workspace (32 MiB, allocated at warmup) and the prefix states.Demo / evidence
No demo. Raw responses, manifests and scripts are kept locally; JevBench's license allows publishing aggregate results only.
Self-review