Skip to content

fix(proxy): normalize leaked GLM/qwen tool-call arg dialect from responses (#85) - #86

Merged
cboettig merged 1 commit into
mainfrom
fix/glm-arg-dialect-85
Jul 14, 2026
Merged

fix(proxy): normalize leaked GLM/qwen tool-call arg dialect from responses (#85)#86
cboettig merged 1 commit into
mainfrom
fix/glm-arg-dialect-85

Conversation

@cboettig

Copy link
Copy Markdown
Member

Closes #85.

Problem

z-ai/glm-5.2 (and, per the same leak class, the qwen family) intermittently fail to decode their own tool-call argument encoding, leaving raw markup inside the structured arguments that a well-formed native tool_calls entry hands back. The verbatim symptom from the issue:

"value_stats": "<arg_key>value_stats</arg_key> <arg_value>{\"by_res\": {\"2\": {\"max\": 9.45…}}}"

The intended payload ({"by_res": {…}}) is intact inside the wrapper — a serialization/parse gap, not lost data. The dialect reached the client (breaking tool execution) and the logs.

Fix

A proxy-side normalization pass (_normalize_response_tool_calls) applied to every successful response before it is returned or logged, so no downstream consumer — client or log — ever sees the markup. This is the durable server-side fix for the leak class that geo-agent#276 was defending against client-side.

It handles two shapes:

  • Value-level leak (the z-ai/glm-5.2 leaks <arg_key>/<arg_value> tool-call arg dialect into argument values #85 symptom): the outer arguments is valid JSON but one value is wrapped in the dialect. The wrapper is stripped and the inner payload is re-parsed so structured values come back structured.
  • Whole-string leak: the entire arguments string is raw dialect; rebuilt into a JSON object by pairing each key tag with the value tag following it.

Both the GLM (<arg_key>/<arg_value>) and qwen/hermes (<parameter=NAME>…</parameter>) forms are covered, including unterminated (truncated) tags.

Fully defensive: any parse failure leaves the value untouched — normalization must never corrupt a response or break serving.

Observability

The number of repaired arguments is recorded as tool_call_dialect_repaired on the response log entry (and a 🧹 stdout breadcrumb), so the leak rate stays measurable even though the markup itself no longer reaches the logs.

Tests

8 new tests in test_logging.py cover value-level leak, unterminated tag, whole-string dialect, the qwen <parameter=> form, clean-passthrough (no wasted re-serialize), the in-place response pass + repair count, defensive handling of malformed shapes, and an end-to-end proxy_chat run asserting the client receives structured data and the log records the count. Full suite: 31 passed.

…onses (#85)

Some open-weight backends (z-ai/glm-5.2, qwen family) intermittently fail
to decode their own tool-call argument encoding, leaving raw markup inside
the structured `arguments` a well-formed native tool_calls entry returns:
  GLM:  <arg_key>NAME</arg_key> <arg_value>VALUE</arg_value>
  qwen: <parameter=NAME>VALUE</parameter>
The intended payload is intact inside the wrapper, so it's a parse gap not
lost data. Normalize both the value-level leak (dialect inside one value of
an otherwise-valid JSON object, the #85 symptom) and the whole-string leak,
applied to each successful response before it is returned or logged, so no
downstream consumer (client or log) sees the markup. Fully defensive: any
parse failure leaves the value untouched. Repair count recorded as
tool_call_dialect_repaired on the response log so the leak rate stays
measurable. Durable server-side fix for the class geo-agent#276 defends
against client-side.
@cboettig
cboettig merged commit a8009bc into main Jul 14, 2026
1 check passed
@cboettig
cboettig deleted the fix/glm-arg-dialect-85 branch July 14, 2026 16:56
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.

z-ai/glm-5.2 leaks <arg_key>/<arg_value> tool-call arg dialect into argument values

1 participant