Repository navigation
feat: runtime-info protocol — plugins report real model config to the proxy (#955) - #956
Conversation
… proxy (#955) - New per-request headers x-bili-plugin-max-output / x-bili-plugin-model (gated on x-bili-plugin, like the existing window header) - New POST /__bili/plugin/runtime-info endpoint: bootstrap + model-switch report of {agent, model, contextWindow, maxOutput, baseURL, source}, per-agent LRU table, consulted in the native-window chain directly under the per-request header report (agent+model must match) - Reported maxOutput stands in for output-headroom reservation when the request body carries no budget of its own (#924 fallback order) - dsh-native: reads ctx.llm.resolveModelInfo + ctx.agentDefaultModel (dynamic ctx.inject) and stamps model/window/max-output headers + report - opencode v1 (extractV1Outputs), opencode v2 (catalog limit.output), pi: add model/max-output stamps + change-detected runtime report - /__bili/plugin/status now exposes model, windowSource, runtimeInfo - Tests: header parsing gates, endpoint validation/LRU/model guard, change-detection with failure rollback, dsh stamp, 3 e2e chain cases - README (en/zh): runtime-info protocol table for future client ports
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)npm install -g billion-context@pr-956Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pr956.tgz
npm install -g packageInstall instructions are refreshed on each push. |
[bot] 🏷 Review started on PR #956 (branch Initial state:
I'm now walking the diff file by file against the protocol table in the PR description. Will report findings (or a mergeable verdict once CI is green) shortly. |
[bot] 🏷 PR #956 review — complete ✅ Verification (ran locally against branch head
Code review
One minor finding (non-blocking): Verdict: code matches the description, all claims independently verified, CI fully green. Per §7.4 this is a 中文摘要:审查了 runtime-info 协议(插件上报真实模型配置、窗口解析链新增 runtime-info 档、maxOutput 输出预留兜底)——本地独立复验 typecheck / 1893 项测试 / 构建 / 真 codex E2E 全部通过,CI 8 项检查全绿,diff 干净且未动版本号;仅发现一个 dsh-native 异步解析的瞬时竞态(非阻塞,可选后续加固),可以合并。 |
Acceptance criterion #1 ("dsh session: /acp before first request shows model + window from client config") was not met: the runtime-info table was only consulted once a session existed, so the latest-session probe a client without a stable id sends (conversationId=<agent>&fallback=latest) 404'd until the first model request landed. - handlePluginStatus now answers that probe from the agent-keyed runtime table (phase: "pre-first-request") when no session resolves; original 404 messages preserved otherwise - dsh statusOutcome renders the reported model/window/maxOutput/source instead of the generic "no model request yet" notice - tests: e2e pre-first-request status (report->200, empty->404, real session takes over after traffic), dsh /acp render of the probe response; harness now isolates XDG_STATE_HOME + resets the in-memory session map so "no session yet" preconditions are deterministic - README en/zh: one line documenting the pre-first-request behavior
…eck on plugin headers Review findings on PR #956: - dsh-native refreshModelInfo: an in-flight resolve whose model selection changed mid-flight must not overwrite the cache or report the OLD model (one-turn transient mis-sizing); commit + report are now gated on the live selection still matching what was resolved. - server: per-request x-bili-plugin-context-window / -max-output headers are now ignored when x-bili-plugin-model differs from the request body's model (exact or last path segment — openai bodies use provider/model composites). A missing model header keeps the pre-#956 trust.
|
Pushed
New tests: cross-check semantics (4 cases) + existing suites re-run. Local: typecheck clean, 1896 pass / 0 fail, build ok. |
… review) The selectionStillCurrent gate in refreshModelInfo had no dedicated test: drive apply() with a gated async resolve of model A, switch the live selection to B mid-resolve, assert A's numbers never land in the cache or headers, then assert the next refresh self-heals by re-resolving B.
[bot] 🏷 Round-2 review complete ✅ — reviewed both new commits ( Both fixes verified correct
One gap I fixed directly (per repo review rules): the race guard had no regression test. Pushed Verification on final head
Verdict: findings addressed completely, no new issues found, green across the board. Per §7.4 this remains a 中文摘要:二轮审查确认两处加固(dsh 中途切模型的竞态守卫、plugin 头与请求体 model 的交叉校验)实现正确且覆盖全部路径,另审了新增的 /acp 首请求前应答逻辑无问题;竞态守卫原本缺回归测试,我已直接在 PR 分支补上(3f3e5e9,纯测试提交),本地 typecheck/1899 项测试/构建/真 codex E2E 全部通过、CI 8 项全绿,可以合并。 |
…r strips x-bili-plugin-context-window for the process lifetime Root cause of the ranxianglei#1812 mis-sizing handshake gap: dsh's catalog always knows a window (user-set models[].contextWindow or the built-in default), and headersFor stamps x-bili-plugin-context-window from the ModelInfoCache — but refreshModelInfo cached {provider, model} (no window) whenever resolveModelInfo failed once, and the early-return only compared provider+model, so a single boot-time race (catalog still loading) latched the process headerless. Every request of the session then sized against bili's unconfigured 200k fallback. Observed live on the same dsh install against the same model: two proxy runs sent x-bili-plugin-model without any context-window header (proxy warned ranxianglei#1569 and only survived via a lucky registry-peek match), a third run sent the header on every request — intermittency = the boot race. Fix: a matching cache entry is final only when it carries a window; a failure-shaped entry retries after a 30s cooldown (test hook: BILI_MODEL_INFO_RETRY_MS). Successful resolves with a window clear the retry state; selection-change semantics from ranxianglei#956 are untouched. Test: new dsh-native unit test — resolve fails twice, model id still stamped, window absent; after the cooldown the next stamp carries x-bili-plugin-context-window: 262144 (attempts >= 3). Part of ranxianglei#1812 (handshake root cause; the error-text extraction rework stays in ranxianglei#1818).
…ranxianglei#3261) The hermes native plugin stamped model/max-output but never the context window, so custom-alias models (absent from every registry) sized sessions at the 200K default even when the host knew the real window. Now the plugin reads HERMES_HOME/context_length_cache.yaml (the host's own per-model window cache, keyed <model>@<base_url>) best-effort and reports it on both ranxianglei#955/ranxianglei#956 channels: x-bili-plugin-context-window header in the llm_request middleware and contextWindow in the runtime-info body (dedup key extended so a changed window re-reports). Base URL resolution: hook/request-supplied fields first, then the provider entry in config.yaml; matching is exact-URL then origin-granularity (cache keeps /v1, configs often omit it); conflicting per-upstream windows are never guessed (fail-open to the proxy's normal chain, like every other miss). Adjacent fix in the same call path: on_pre_api_request read 'table' unconditionally while binding it only inside the max_tokens-present branch — a hook fire without max_tokens raised NameError into the swallowing except and silently skipped the whole runtime-info report. Hoisted the binding.
Fixes #955
What
Native plugins live INSIDE the client process, so they can read the client's real model configuration. This PR defines and implements the runtime-info protocol so the proxy stops guessing (models.dev snapshot / built-in table / first-request learning) and gets the truth pushed upstream:
x-bili-plugin):x-bili-plugin-context-window(existed) + newx-bili-plugin-max-output,x-bili-plugin-modelPOST /__bili/plugin/runtime-info(loopback):{agent, model, contextWindow, maxOutput, baseURL, source}— sent at plugin bootstrap and on model switch, per-agent LRU tableanthropic-beta> header report > runtime-info table (agent+model must match — a stale post-switch entry can never size a different model) > launcher env > route config > registry > tablePlugin implementations
ctx.agentDefaultModel.currentSelection()+ctx.llm.resolveModelInfo(provider, model)via dynamicctx.inject(missing services degrade gracefully), caches, stamps all three headers, reports at bootstrap/switchextractV1Outputs(limit.output), stamps model + max-output alongside the existing windowlimit.outputmap, same stampsctx.model.id+ optionalmaxTokens, same stamps + report withbaseURLObservability
/__bili/plugin/statusnow returnsmodel,windowSource,runtimeInfo; the[window]log line has aruntime-infosource.Docs
README (en/zh) gained a protocol table — this is the contract other client integrations (see sibling issues) build against.
Pre-flight
npm run typecheck✅npm test1893 pass / 0 fail ✅npm run build✅