fix: the provider's handle on its thought reaches the next round #121
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/reasoning-handle-round-trip"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The end of the arc that started with a field report on
google/gemini-3.5-flash. Bumpsexternal/OpenRouter.Netfrom75ec695tofd43f04— mergedorigin/mainof TeamAI/OpenRouter.Net#11 — and adds the one assertion the vendor library cannot make about itself.Why this is not just tidiness
#119 stopped the crash. It did not stop the cause. Measured against the live model (evidence on OpenRouter.Net#10): when the handle Gemini puts on its own thought does not come back, it can no longer reattach that thought — it answers with an empty reasoning segment, stops calling tools, and states a plausible wrong conclusion. Over eight paired five-round conversations differing only in whether the handle was echoed: 0/8 degraded when it was kept, 7/8 when it was stripped.
For a boxing or refinement stage that is worse than the crash was. A failed execution shows up red in the monitor; an agent that quietly decides a region is "background hatching" and stops looking produces a region you have to catch by eye.
The change
fd43f04, where[JsonExtensionData]onReasoningDetailpreserves every unmodelled field on a segment.AgentRunnerTests.The_providers_handle_on_its_thought_goes_back_out_on_the_next_round— a two-round run over the gateway's real HTTP path, answering round 1 with the live wire shape ({"type":"reasoning.encrypted","data":"AY89a18GGWWbsm81","format":"google-gemini-v1","id":"bheu85ws","index":0}), then asserting onhttp.RequestBodies[1]— the request the loop actually sends next.That last point is why the test belongs here rather than in the library: OpenRouter.Net can prove a segment survives its own serializer, but not that the agent loop puts it back on the wire. What matters to a run is the outgoing body, and this is the only place that can see it. The assertion covers the handle and the encrypted payload beside it, so it cannot pass by dropping the segment wholesale.
Verified load-bearing rather than assumed: submodule back to
75ec695(post-#9, pre-#11), rebuild, run that test → FAIL. Restoredfd43f04, rebuild → PASS.Tests
+1 → 1143 total, all green (Domain 132 · UseCases 527 · Integration 228 · BlazorAdapter 256), full
dotnet test Orihon.slnx, 0 warnings / 0 errors. The totals moved withmainsince #119 — #120 and its siblings landed in between.What is now closed, and what is not
Closed: the crash (#9), the transcript that could not explain its own last round (#119), and the cause of both (#11 + this).
Not addressed, and worth knowing before pointing a fan-out at this model: it spent 1.8k output tokens of chain-of-thought on one region's bounding box, $0.023 for that single round. The reasoning-effort control on the Agents tab is the lever, and Gemini's own trail is now readable in Settings → Transcripts if you want to judge whether the thinking is earning its keep.
🤖 Generated with Claude Code
Summary
Summary
Coverage
Orihon.BlazorAdapter - 95.5%
Orihon.Domain - 100%
Orihon.Infrastructure - 96.9%
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlankLines_4
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__BlockBreaks_1
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__SpaceRuns_3
A835C427B12E8B84E2A8A7283193FC51C220B5B4E80CE8D56__Tags_2
Orihon.Kernel - 90.9%
Orihon.Server - 94.4%
Orihon.UseCases - 97.5%
🔮 fufu~ Jibril reviewed your code!
Oh? Oh~ This is the end of an arc, isn't it? A field report, a crash, a transcript that couldn't speak for itself — and now the cause, pinned down with the one assertion only this codebase can make. fufu~ That's a proper investigation. I'm impressed~ ♡
Verdict: ✅ Looks good to me~
I traced every thread and I'm satisfied. Let me show you what I found~
✅ What I liked~
The test lives where it belongs, and the PR body explains why better than I could. The vendor library can prove a segment survives its own serializer, but not that the agent loop puts it back on the wire — and it's the outgoing body (
http.RequestBodies[1]) that determines whether Gemini can reattach its thought. Asserting on that body is the only honest place this test can live. fufu~ you understood the level the failure was felt at, not just the level the fix was made at. That's excellent.The assertion is triple-locked against the easy way out. You assert
bheu85ws(the provider'sidhandle),AY89a18GGWWbsm81(the encrypteddata), andgoogle-gemini-v1(theformat). A test that only checked the modelleddatacould pass by dropping the unmodelledid/format— exactly the regression you're guarding against. Belt and suspenders. The doc comment says it plainly: "so this cannot pass by dropping the segment wholesale." ♪ToolCallResponseWithHandlemirrors its siblings exactly. I compared it againstToolCallResponse,ReasoningToolCallResponse, andFinalResponseWithWithheldReasoning— same shape, same field order, sameJsonSerializer.Serialize(argumentsJson)plumbing. The only addition isreasoning_detailswith the live Gemini wire shape. Consistent with the established pattern. No smell here~The submodule bump is precisely what it claims.
75ec695→fd43f04= the merge commit of OpenRouter.Net#11, whose headf3ed4caI already approved (round 2, comment 5297). The substantive delta is one property:[JsonExtensionData] ProviderFieldsonReasoningDetail. I confirmedf3ed4cais an ancestor offd43f04and that75ec695..fd43f04touches exactlyReasoningDetail.cs+ its tests. Clean lineage, no surprise commits smuggled in.And I verified the load-bearing claim myself — because I always do~ I checked out the submodule at
75ec695(post-#9, pre-#11, noProviderFields), rebuilt, and ran your new test:FAIL. Restored
fd43f04, rebuilt → PASS. Your PR body says "verified load-bearing rather than assumed" and it is honest. ♡ The handle really is stripped without the fix, and the test catches it.Final checks~
Orihon.slnx.ToolCallResponseWithHandlefollows the established Gemini-specific helper family (FinalResponseWithWithheldReasoningwas its sibling from #119).The arc is closed cleanly. Merge it~ ♡
Automated review by Jibril · 2026-07-30
CI/CD: absent for head
c0c7fda(PR just opened, 0 comments) · Local checks: build 0/0, Integration 228/228 pass, load-bearing FAIL/PASS verified at 75ec695/fd43f04