feat(serve): automatic shared-prefix write at the system/developer frontier - #152
feat(serve): automatic shared-prefix write at the system/developer frontier#152Astrangemaninhere wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 58ded12138
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
0e4cb22 to
937abea
Compare
|
这次的pr修改程度较小,希望能得到采纳;如果不能接受,希望能给出意见。 |
|
Thanks for landing this as a small serve-only patch, and for calling out the 16k sibling probe. We can re-run the same Chat Completions probe we used in #142 (16k shared system, no
Two practical notes, not blockers:
Will post the table on this PR when the run finishes. Chat Completions is the path we can speak to; we did not exercise Responses in the original issue. |
|
16k sibling probe on this patch, RTX 5090 32GB, exclusive Tree: cherry-pick of Compile: as submitted, Serve flags: 1. Default ON (no client
|
| scenario | reuse | cache | prompt | hit% | ttft ms | wall s |
|---|---|---|---|---|---|---|
| chat S1 cold 2k | root |
0 | 2193 | 0.0 | 320 | 0.381 |
| chat S1 warm continue | private_turn_closure |
2188 | 2209 | 99.0 | 86 | 0.138 |
| chat S2a sib A, implicit 16k | root |
0 | 16484 | 0.0 | 2402 | 2.456 |
| chat S2a sib B, implicit 16k | root |
0 | 16485 | 0.0 | 2428 | 2.483 |
| chat S2b sib A, explicit breakpoint | root |
0 | 16484 | 0.0 | 2493 | 2.530 |
| chat S2b sib B, explicit breakpoint | shared_stable_prefix |
16466 | 16485 | 99.9 | 108 | 0.146 |
| chat S4 conc CONC1 (after S2b) | shared_stable_prefix |
16468 | 16484 | 99.9 | 240 | 0.173 |
| chat S4 conc CONC2 (after S2b) | shared_stable_prefix |
16468 | 16484 | 99.9 | 103 | 0.295 |
| responses S2a sib A, implicit 16k | root |
0 | 16484 | 0.0 | 2404 | 2.459 |
| responses S2a sib B, implicit 16k | root |
0 | 16485 | 0.0 | 2495 | 2.550 |
Historical same box on 3d9fda2 without this patch: S2a both root / 2363 ms; S2b sib B shared_stable_prefix / 91 ms / cache=16466. Same-turn was private_turn_closure / 92 ms.
2. --no-auto-system-shared-prefix
| scenario | reuse | cache | prompt | ttft ms |
|---|---|---|---|---|
| opt-out chat S2a sib A/B | root / root |
0 | 16485 / 16486 | 2404 / 2428 |
| opt-out chat S1 warm continue | private_turn_closure |
2189 | 2210 | 110 |
| opt-out responses S2a sib A/B | root / root |
0 | 16485 / 16486 | 2426 / 2425 |
Verdict
- Same-turn is fine.
private_turn_closurestill hits (~86–110 ms). Last-content implicit write is intact. - Explicit breakpoint still works. S2b / S4 match the Agent sibling sessions miss shared prefix without prompt_cache_breakpoint #142 control: sibling B
shared_stable_prefix, cache ≈ system tokens, TTFT in the ~100 ms class vs ~2.4 sroot. - The Agent sibling sessions miss shared prefix without prompt_cache_breakpoint #142 case does not. Chat and Responses 16k siblings with no client marker both stay
root/ ~2.4 s, same as unpatched master. Hermes / Pi / OpenCode would still re-prefill the shared system head.
So the extra write is not becoming a published shared_stable_prefix that a second user can reuse.
Likely reason, from reading current planner + this diff (not from extra instrumentation):
- The new candidate is
CacheBoundary{ .evidence = DefaultAutomatic }onChatTurn::cache_boundary_after(MessageBoundary). - In
resource_manager.h,DefaultAutomaticis only a surplus candidate.pressure_capable/explicit_shared_creditrequireExplicitBoundaryorRequestedAutomatic. The value model then has to beat baseline + split cost before anything is written into the shared catalog. That is already why today's last-content implicit write does not help siblings. - The control that does publish is S2b:
prompt_cache_breakpointon the system part (ExplicitBoundary+LeadingInstructionBoundary).
--no-auto-system-shared-prefix could not be shown to restore a Chat miss, because Chat never hits in the first place, and the Chat parser still does not take options_.auto_system_shared_prefix (as the PR text said). Responses S2a also missed with the flag off.
Happy to re-run the same table if you:
#include "serve/openai_common.h"inopenai_responses.h- Give the leading-instruction candidate
RequestedAutomaticorExplicitBoundaryevidence so the planner treats it as declared - Place it as a leading-instruction part marker (the S2b path), not only a turn
MessageBoundary - Optionally thread the CLI flag into the Chat parser
Raw: serve log reuse= lines from this run, ctx 237568, INT8, C=2.
Closes #142
What
Agent clients (Hermes, OpenCode, Pi, etc.) rarely send
prompt_cache_breakpoint; on master they always miss the shared system/tools head. This adds one automatic write candidate at the leading system/developer frontier so the existing shared-prefix machinery can publish a prefix those clients actually share.--no-auto-system-shared-prefix(extra write on by default) for the Responses path; the Chat path currently uses the default-on policy value (consistent with the issue discussion; wiring the flag into the Chat parse signature was deliberately left out to keep the diff minimal).shared_stable_prefixpath does the work.Per-file rationale (8 files, +65/-7, serve layer only)
src/serve/openai_common.h(+5) -OpenAIPromptCachePolicygainsauto_system_shared_prefix(defaulttrue). The policy is the single switch that reaches both Chat and Responses without threading a new parameter through every parser.src/serve/openai_common.cpp(+31/-1) - the actual feature:DefaultAutomaticcandidate on that last leading turn when unmarked (the existing explicit-breakpoint contract is untouched).frontend.cpprejects >4 markers); the explicit-write selection shrinks by one when the candidate is distinct, so a request with 3 explicit breakpoints + both automatic writes stays at 4.src/serve/serve_options.h(+3) -ServeOptionsgainsauto_system_shared_prefix = true.src/serve/serve_options.cpp(+4/-1) - parses--no-auto-system-shared-prefixand documents it in the usage text.src/serve/openai_responses.h(+8/-1) -OpenAIResponsesPromptRequestcarries the parsedcache_policyso the policy can be applied after prompt resolution (at parse time the leading turns are still ininput_turns/instructions, not ingeneration.messages).src/serve/openai_responses_request.cpp(+11/-3) - stores the policy on the prompt instead of applying it to the empty message list; the parser signature gains the opt-out bool from the HTTP layer.src/serve/openai_responses_state.cpp(+7) -resolve_openai_responses_promptapplies the policy oncegeneration.messagesis assembled (instructions + input turns), which is the earliest point the leading-instruction candidate can be placed correctly. (Fixes the review finding that Responses requests saw no system/developer turns.)src/serve/openai_responses_http.cpp(+3/-1) - passesoptions_.auto_system_shared_prefixinto the parser; without this one line the flag would exist but never reach Responses requests.Verification
da49c0d(WSL2, CUDA 13.3, sm_120a).--no-auto-system-shared-prefixrestores strict OpenAI-implicit behavior on the Responses path.Follow-ups agreed with the issue author (#142 discussion, not blockers)