Skip to content

slot79 (MODE_CATEGORY) is ignored upstream: gemini-3.1-pro returns "3.7 Flash" with no thinking #82

Description

@qcsunny

Summary

Selecting gemini-3.1-pro always comes back labelled 3.7 Flash upstream, with zero thinking
markers
in the response. slot79 (MODE_CATEGORY) in the request payload is no longer honoured by
the backend, so this project's model selection is effectively inert — every request gets the account's
default model regardless of which model the caller asked for.

The account is a paid Google AI Pro subscription, the cookie is valid, and the same conversations show
up in the Gemini web UI sidebar.

Environment

Item Value
Base commit 2bb988b feat(models): 新增 gemini-3.7-flash 并调整 3.6 版本描述 (#77)
Python 3.11.2
gemini_bl boq_assistant-bard-web-server_20260816.12_p0, then refreshed to 20260817.02_p0 (both tested)
auth_user 0
Endpoint /u/0/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate

All logs below are redacted. Cookies, SAPISID, XSRF tokens, conversation/response IDs and account
geo context are replaced by placeholders or reduced to a byte length. gemini_bl is kept as-is
because it is a public build ID, not a credential.

Reproduction

Send a genuinely hard reasoning problem (not something answerable in one token) to gemini-3.1-pro,
then read slot42 of the final upstream frame — that field is the model name the web UI itself displays.

[05:39:33] fetching account page WIZ tokens (not a generation request)
[05:39:35] page tokens keys=['at', 'f_sid', 'pctx', 'push_id'] at=yes f_sid=yes image_model=None
[05:39:35] resolve_model -> name=gemini-3.1-pro mode(slot79)=3 think(slot17)=0 extra=None err=None
[05:39:35] gemini_bl=boq_assistant-bard-web-server_20260816.12_p0  auth_user=0  xsrf_token=present
[05:39:35] [t1_stream_pro] POST .../StreamGenerate  slot79=3 slot17=0
[05:39:37] [t1_stream_pro] HTTP 200
[05:39:37] [t1_stream_pro] TTFB (first byte)      = 2.01s
[05:39:51] [t1_stream_pro] TTFT (first text frame) = 16.20s
[05:39:56] [t1_stream_pro] total=21.18s  raw=296210B

### t1_stream_pro model-label scan (raw 296210B)
  version+tier strings: [('3.7 Flash', 36)]
  tier keyword hits:    [('Flash', 36)]

[05:39:56] === sleeping 200s to stay under rate limiting ===
[05:43:16] [t2_plain_pro] non-streaming _generate_raw  slot79=3 slot17=0
[05:43:34] [t2_plain_pro] total=17.49s  raw=179856B

### t2_plain_pro model-label scan (raw 179856B)
  version+tier strings: [('3.7 Flash', 33)]
  tier keyword hits:    [('Flash', 33)]

Controlled experiment: 4 real upstream generations

A different question each time, ≥200s apart to avoid rate limiting.

# Mode slot79 gemini_bl upstream slot42 thinking markers
1 streaming 3 (PRO) 20260816.12 (stale) 3.7 Flash 0
2 non-streaming 3 (PRO) 20260816.12 (stale) 3.7 Flash 0
3 non-streaming 3 (PRO) 20260817.02 (refreshed) 3.7 Flash 0
4 non-streaming 6 (FLASH_LITE) 20260817.02 3.7 Flash 0

Run 4 is the decisive one. Switching slot79 from 3 (PRO) to 6 (FLASH_LITE) — two entirely
different tiers — produced the identical label. slot79 is not participating in routing at all; the
backend just serves the account default.

Ruled out

  • Stale BL — run 3 used that day's newest 20260817.02_p0. slot42 did not change.
  • Invalid / ineffective cookie — the response frames carry account geo context and real
    c_ / r_ conversation IDs, and the conversation is visible in the web UI. The session is authenticated.
  • "TTFT < 5s therefore no thinking" is a flawed premise — measured TTFB (first byte) is 2.01s, which
    is just HTTP headers plus padding; TTFT (first real text frame) is 16.20s. The latency is fine; what
    is genuinely missing is thinking — 0 markers in the response, matching what the web UI shows.

Final upstream frame (redacted)

=== non-empty slots of the final frame (redacted) ===
  slot[ 1] = <redacted: conversation / response IDs>
  slot[ 4] = <redacted: answer body container>
  slot[ 5] = <redacted: account geo / personalization context>
  slot[14] = true
  slot[19] = "zh"
  slot[23] = true
  slot[26] = [[[[null, [null, 0, "结论:存在这样的填法。", …     <- text delta, truncated
  slot[42] = "3.7 Flash"                                     <- authoritative model label
  slot[43] = true
  slot[44] = [[[[1004, "Longer", 5, …                         <- length-option chips
  slot[46] = 2
  slot[58] = 1
  slot[59] = 1

Root cause

Model selection appears to have moved from a payload slot to a request header:
x-goog-ext-525001261-jspb. It is JSPB-encoded (a protobuf message serialized as a positional JSON
array), and index 4 holds a 16-hex internal model ID:

[1,null,null,null,"<16-hex model id>",null,null,0,[4,5,6,8],null,null,<capacity>,null,null,<category>]

This repository already builds that header_build_model_headers() / _image_model_headers() in
gemini.py — but only the image path sends it. The text path's _build_headers() sends no model
header at all and relies solely on slot79, which the backend now ignores. That also explains why image
generation does reach a non-default model while text is always pinned to the account default.

Two more observations:

  • The account page enumerates the currently available model IDs as feature-flag lists
    (e.g. sylssb / flag 45749278), so the valid IDs are discoverable at runtime rather than hardcoded.
  • The MODE_CATEGORY enum quoted in models.py comes from an old JS bundle (028-6eb337387583.js).
    That enum is most likely no longer the routing key.

Proposed fix (not yet verified)

Send x-goog-ext-525001261-jspb on the text path too, selecting the tier by real model ID instead of
slot79.

I have not confirmed this experimentally yet. I am currently sweeping the candidate model IDs one at
a time — a different question per request, >3 minutes apart to stay clear of rate limiting — to see
whether slot42 follows the ID in the header. I will follow up in this thread with the result.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions