Skip to content

Fix #239 reasoning-echo 400 self-heal, #233 glm tool-image 422, #228 provider toggle - #242

Merged
ltmoerdani merged 5 commits into
mainfrom
fix/issues-228-233-239-batch
Sep 23, 2026
Merged

ltmoerdani merged 5 commits into
mainfrom
fix/issues-228-233-239-batch

Conversation

@ltmoerdani

Copy link
Copy Markdown
Owner

Batch of three verified bug fixes: #239, #233, #228.

Fixes #239
Fixes #233
Fixes #228

1. #239 — DeepSeek 400 "reasoning_content must be passed back" (self-heal)

When Copilot Chat compaction, history trimming, or a pre-thinking-capture turn strips prior-turn reasoning from the replayed history, deepseek-v4.1-flash (thinking on) rejected every follow-up turn with HTTP 400 and retries could never recover.

Fix: a new recoverable-400 pattern in src/retry.ts (executed by the existing 400-patch loop in engine.ts) strips the reasoning_content echo from assistant messages and turns reasoning_effort off — making the request self-contained and stopping the lose-echo→400 cycle. No-ops when there is nothing to strip.

2. #233 — glm-5.3-flash 422 on tool-result images

The Console Go upstream rejects image_url parts inside role: "tool" content (422 Input should be a valid string) while accepting identical images in user messages (verified with a direct gateway repro matrix in the issue). The tool result stays in history, so every follow-up turn re-422s.

Fix: new requiresStringToolContent() mapping in src/models/modelCapabilities.ts:

  • mimo-* → existing drop-with-placeholder behavior (issue fix: add option to turn off reasoning #38, byte-identical)
  • glm-5.3* → tool message flattened to a string, images moved to a follow-up user message via the new pure withDeferredToolImageMessages() (request/shared.ts) — vision preserved
  • everyone else → multimodal tool content forwarded unchanged

3. #228 — Toggle Provider Registration wrong-key write + blind toggle

Two defects: (a) agent-variant definitions wrote <variant>.enabled (e.g. opencodezen-agent.enabled) — a key the provider when clause never reads — leaving the provider permanently removed while settings looked enabled; (b) the command was a blind toggle behind a "Remove/Re-add" title.

Fix: toggleProviderEnabled resolves agent variants to the base vendor before touching configuration, and derives Remove vs Re-add from the current setting via a confirmable quick-pick (Esc cancels). Command titles reworded to "Toggle Provider Registration in Language Models".

Verification

  • npm run lint — full 7-check gate PASS
  • 480/480 unit tests pass (8 new: retry patch + no-op, capability mapping, deferred-message shape)
  • Mock-server retry E2E: 9/9 (npx tsx scripts/test-retry-e2e.ts), incl. the DeepSeek validator scenario (400 → patch → 200)
  • Serialization E2E simulation: 13/13 (tmp/e2e-issues-233-239-serialization.mjs), incl. MiMo/kimi regression guards
  • Real-model manual tests via Copilot Chat: PASS for all three

Docs

Merge method: merge commit (no squash) — preserves the per-fix commit history.

…fixes #239)

DeepSeek V4 thinking mode requires reasoning_content to be passed back
on multi-turn requests. When Copilot Chat compaction, history trimming,
or a pre-thinking-capture turn removes it from the replayed history,
every follow-up turn 400s and retries never recover.

Add a recoverable-400 pattern that strips the reasoning_content echo
from assistant messages AND turns reasoning_effort off, making the
request self-contained. Dropping reasoning_effort stops the cycle:
with thinking still on, the next response emits new reasoning that
compaction strips again, re-400ing the turn after.
…fixes #233)

glm-5.3-flash upstream (Console Go) rejects image_url parts inside
role:"tool" content with 422 "[invalid_request_error] Input should be
a valid string", while the SAME images in a user message return 200
(reproduced directly against zen/go/v1 chat-completions). The tool
result stays in history, so every follow-up turn re-422s.

Introduce requiresStringToolContent() mapping per upstream:
- "drop"  — mimo-*: flatten + placeholder (existing #38 behavior,
  unchanged)
- "defer" — glm-5.3*: flatten the tool message to a string and move
  the images into a follow-up user message, preserving vision
- null    — forward multimodal tool content unchanged (kimi, glm-5.2,
  minimax, qwen)
fixes #228)

Two defects in the Remove/Re-add Provider flow:

1. Wrong-key write on agent variants: toggleProviderEnabled() wrote
   <variant>.enabled (e.g. opencodezen-agent.enabled) for agent-host
   definitions, but the provider when-clause and settings schema only
   read opencodezen.enabled. The provider stayed removed while settings
   looked enabled — the exact workaround in the issue report (manually
   deleting the stale key). Resolve agent variants to their base vendor
   before touching configuration.

2. Blind toggle: the command flipped the setting without regard to the
   desired outcome, so running it twice could never re-add the provider
   without a reload in between. Replace with a state-aware quick-pick
   (Remove when enabled / Re-add when disabled, Esc cancels) and retitle
   the command to 'Toggle Provider Registration in Language Models'.
…chain

- Extract the deferred tool-image emission into the pure
  withDeferredToolImageMessages() helper (request/shared.ts) so the
  glm-5.3* relocation is testable without a VS Code host; convertMessage
  now delegates to it (behavior unchanged).
- Mock-server retry E2E: new deepseek-v4.1-flash validator scenario +
  full-loop and healthy-request cases (9/9).
- Unit tests for the deferred-message shape and the
  requiresStringToolContent mapping (480 total).
@ltmoerdani
ltmoerdani merged commit c601c62 into main Sep 23, 2026
2 checks passed
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.

OpenCode Go API request failed (400) [BUG] Error loading an image with glm-5.3-flash [BUG] opencodezen.toggleProvider disables provider

1 participant