Skip to content

fix: pre-release review fixes for pings, DeepSeek Harness cleaning and GLM quota - #208

Merged
fylorn merged 4 commits into
mainfrom
claude/core-prerelease-review-ei3lsb
Sep 25, 2026
Merged

fylorn merged 4 commits into
mainfrom
claude/core-prerelease-review-ei3lsb

Conversation

@fylorn

@fylorn fylorn commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

Requested by F · project thread

What this changes

Fixes six bugs found while reviewing #203, #204, #205 and #206 before v0.50.0. There is one commit per area.

  1. Anthropic pings now follow what the client received (feat(gateway): meet what Claude Desktop's third-party mode asks of a gateway #203). The ping timer restarted on every upstream chunk, SSE comments included. When a stream is converted, those comments are dropped: DeepSeek's : keep-alive while a request is queued, OpenRouter's processing notes. The upstream therefore never looked silent, the client got nothing, and it hit its five-minute idle limit anyway. The timer now runs from the last bytes the client actually received.
  2. Pings stop when the upstream is dead (feat(gateway): meet what Claude Desktop's third-party mode asks of a gateway #203). Pings used to continue for as long as the upstream stayed silent. A half-open upstream connection never yields another byte, and the gateway has no read timeout, so the client waited forever. After 10 minutes without a single upstream byte (PING_FOR), the gateway stops pinging and the client's own timer ends the request.
  3. DeepSeek Harness requests that do not generate are cleaned too (feat(gateway): clean DeepSeek Harness requests for non-DeepSeek upstreams #205). Previously only generating requests were cleaned. A count_tokens call from dsh reached a non-DeepSeek upstream with the x-deepseek-harness-* headers, and with dsh_* fields such as the session log if the body had them.
  4. anthropic-beta sent on several header lines survives (feat(gateway): clean DeepSeek Harness requests for non-DeepSeek upstreams #205). All lines were dropped, and only the first line was filtered and forwarded, so betas on the other lines were lost.
  5. Disabled GLM upstreams are no longer asked for quota (feat(quota): read the GLM Coding Plan quota for Z.ai and BigModel upstreams #204). Opening the quota view still sent a disabled upstream's key to the quota endpoint, and still showed a quota card for it.
  6. One transient business-500 no longer hides a GLM quota for an hour (feat(quota): read the GLM Coding Plan quota for Z.ai and BigModel upstreams #204). "No plan" is identified by business code 500, and a transient server error returns the same code. When the previous answer for the same key was a quota, the first "no plan" now counts as a failure and is retried after the usual backoff. A second "no plan" in a row is believed.

Why

Pre-release review of the changes going into v0.50.0.

How it was verified

  • Each fix has a new test that fails on main and passes here: upstream_comments_that_never_reach_the_client_do_not_hold_pings_back, pings_stop_once_the_upstream_has_been_silent_too_long, counting_tokens_elsewhere_is_cleaned_too, betas_sent_on_separate_lines_all_survive_the_cleaning, a_key_that_had_a_plan_must_say_no_plan_twice, and a disabled case in only_a_glm_upstream_with_a_key_is_asked.
  • cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, cargo test --workspace (1691 passed) and scripts/smoke.sh (60 passed, 0 failed) all pass.
  • Credentials: the only related change is that the GLM quota request now skips disabled upstreams, so fewer keys are sent. The harness change removes more headers and fields going to non-DeepSeek upstreams, never fewer.

Notes for review

  • PING_FOR is 10 minutes. An upstream that is healthy but completely silent for longer than that (no comments at all) would lose its pings. Anthropic upstreams send their own pings, and DeepSeek sends keep-alive comments while a request is queued.
  • The locally newer clippy flags a nonminimal_bool in tw-dialect/src/convert.rs:276. That code predates this PR and is left untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_014yLMu6J4y8DLa8LiWygfzA


Generated by Claude Code

…or a dead upstream

Two gaps in the keep-alive pings added for Claude Desktop:

- The timer restarted on every upstream chunk, including SSE comments
  (DeepSeek's `: keep-alive` while queued, OpenRouter's processing
  notes) that conversion drops. The upstream never looked silent while
  the client received nothing, so a long queue still hit the client's
  five-minute idle limit. The timer now runs from the last bytes the
  client actually got.
- Pings never stopped. A half-open upstream connection yields no more
  bytes and the gateway has no read timeout, so pinging forever kept the
  client waiting forever. After ten minutes without a single upstream
  byte the gateway stops pinging and lets the client's own timer end it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014yLMu6J4y8DLa8LiWygfzA
…nd keep every beta line

- Only generating requests were cleaned, so a count_tokens call from
  DeepSeek Harness still reached a non-DeepSeek upstream with its user
  and session id headers, and with the session log if the body carried
  one. Cleaning now applies to every request Harness sends elsewhere.
- anthropic-beta sent as several header lines: all lines were dropped
  and only the first was filtered and forwarded, so betas on the other
  lines were lost. All lines are now joined before filtering.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014yLMu6J4y8DLa8LiWygfzA
… from a key that had a plan

- The quota view asked every configured GLM upstream, disabled ones
  included, so a key the user had switched off was still sent to the
  quota endpoint and still showed a quota card.
- A no-plan answer is business code 500, which a transient server error
  also returns. Taken at face value it cleared the key's quota from the
  UI and stopped asking for an hour. When the previous answer for the
  same key was a quota, the first no-plan now counts as a failure and is
  retried after the usual backoff; a second one in a row is believed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014yLMu6J4y8DLa8LiWygfzA
@fylorn fylorn self-assigned this Sep 25, 2026
@fylorn
fylorn merged commit 6ad637d into main Sep 25, 2026
4 checks passed
@fylorn
fylorn deleted the claude/core-prerelease-review-ei3lsb branch September 25, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants