grok-build CLI recap-style side-calls (turn-summary / title-refresh) send
a fresh per-call X-Grok-Conv-Id label but set body prompt_cache_key to
the parent session id, so a direct xAI connection shares the main turn's
server-side prompt cache prefix.
sub2api's explicitGrokCacheSeed preferred X-Grok-Conv-Id over the body
key, which fragmented the cache identity on every side-call and forced a
full-price replay of the whole conversation (~300K+ tokens each, ~2.3%
cache hit rate observed over 44 side-calls / 4.9M input tokens in 7
days — about a third of all grok-4.6 input).
Priority is now: CC session > generic session headers > body
prompt_cache_key > X-Grok-Conv-Id > explicitKey > previous_response_id.
IDE header-stability tests updated; new regression test locks in the
side-call/parent key-sharing contract.
Team GLM Coding Plan accounts fail the quota probe with
"当前用户不存在coding plan" because the personal-plan query path
does not carry team context.
- quota probe: when credentials contain zhipu_organization, switch the
Zhipu quota endpoint to the team variant (?type=2 + bigmodel-organization
/ bigmodel-project headers); personal plans keep the existing path
- account create/edit modals: optional organization/project ID inputs for
zhipu coding-plan accounts, persisted in credentials; edit modal
backfills them and clears back to personal plan when emptied
- add a click-to-open tooltip tutorial (zh/en) explaining how to grab the
IDs from the bigmodel.cn web console via the /api/biz/v1/organization
network request, with a sample URL mapping org-/proj_ segments to fields
The passthrough streaming loop buffers leading events in `pendingLines` until
the first visible output, so a client receives **zero bytes** — not even the
HTTP response headers, since gin's ResponseWriter only commits them on the
first write — for as long as the upstream takes to produce its first visible
event.
The Forward path already recognises this exact hazard and solves it with a
downstream keepalive:
// Track downstream writes separately from upstream reads: pre-output
// failover can buffer response.created / response.in_progress, so
// keepalive must be based on downstream idle time.
The passthrough path has the same buffering but never starts a keepalive.
Reasoning models routinely think for several minutes before emitting a visible
event, so intermediate proxies close the connection on idle timeout.
Production evidence from one deployment (12h window, gpt-5.6 family):
* 44 `/v1/responses` requests produced their first visible output only after
600–900 s, each carrying 30k–50k output tokens — the upstream had finished
the work.
* Every one of them was killed by an intermediate nginx `proxy_read_timeout`
(600 s) and returned 504. The user received nothing.
* Cutting early does not even save resources: the code deliberately keeps
draining the upstream for usage after the client disconnects.
Fix: start the existing keepalive helper for the passthrough stream as well.
* `StartOpenAICompactSSEKeepalive` keeps its compact-marker gate; the gate-free
body is extracted as `startOpenAISSEKeepalive` for callers that already know
they are in an SSE context.
* The passthrough loop starts it when `gateway.stream_keepalive_interval > 0`
and stops it immediately before its first real write, so the ResponseWriter
is never shared.
Pre-output failover is unaffected: keepalive bytes are already excluded by
`OpenAICompactKeepaliveAdjustedWrittenSize`, which `openAIStreamClientOutputStarted`
on this very path already consults (#3887). No pendingLines and no
account-specific headers are ever emitted by a keepalive.
Behaviour is unchanged when `gateway.stream_keepalive_interval` is 0.