[REVIEW ONLY — DO NOT MERGE] fix(providerroutes): degraded stitch must not emit the version segment twice - #9
Merged
damonleelcx merged 1 commit intoAug 15, 2026
Conversation
…t twice A host absent from provider_routes takes the literal-prepend branch, which deliberately does not strip the client's version segment — for an unknown vendor we cannot tell a mount prefix (https://gw.example/proxy serving /proxy/v1/chat/completions) from a complete API root, so we must not decide its shape. That decision stands and is untouched. What was still wrong: when the STORED base_url already ends in the exact segment the client sends, literal-prepend emitted it twice: https://www.cun.ai/v1 + /v1/chat/completions -> https://www.cun.ai/v1/v1/chat/completions (upstream 404) /v1/v1 is wrong under every reading of a vendor's shape, so collapsing it decides nothing about shape. The segment still survives — it is contributed by basePath instead of by reqPath — so the bare-host case the existing note protects is byte-identical. This is not an edge case: the mixed-version manifest carries 19 real vendor endpoints in this shape (vercel ai-gateway, aihubmix, 302.ai, cerebras, fireworks, hunyuan, minimax, mistral, moonshot, sambanova, stepfun, together, z.ai). Those vendors DOCUMENT their base_url with /v1 on it, so the user who pasted the documented URL got a broken route and the one who trimmed it got a working one. Same /v1/v1 poison already fixed on the OAuth path (stitchOAuthRequestURL) and for known rows (2026-08-03); this join was the last. trailingVersionSegment shares trimLeadingVersionSegment's digits-only strictness, so /v1beta and /v1abc are never treated as duplicates of /v1. Fence: stitch_unknown_host_dedup_test.go (11 cases). 4 pin the collapse; 6 pin that every other shape stays byte-identical — those carry the weight, they guard the "do not decide for an unknown vendor" rule. Mutation-verified: removing the guard reproduces www.cun.ai/v1/v1/chat/completions exactly. Does NOT implement D-17's fail-loud half — provider_routes has no registration mechanism, so rejecting unknown hosts would break every third-party relay. Remains 单独立项; reasoning recorded in the bugfix doc. Refs: workflow/CI/bugfix/2026-08-15-degraded-stitch-duplicates-version-segment.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
damonleelcx
marked this pull request as ready for review
August 15, 2026 09:52
damonleelcx
merged commit Aug 15, 2026
1d2cf74
into
review-base-2/providerroutes-stitch-dedup
48 of 56 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
A host absent from
provider_routestakes the literal-prepend branch. When the storedbase_urlalready ends in the exact segment the client sends, it was emitted twice:Not an edge case. The mixed-version manifest carries 19 real vendor endpoints in this shape — vercel ai-gateway, aihubmix, 302.ai, cerebras, fireworks, hunyuan, minimax, mistral, moonshot, sambanova, stepfun, together, z.ai. Those vendors document their base_url with
/v1on it, so the user who pasted the documented URL got a broken route and the one who happened to trim it got a working one.Same
/v1/v1poison already fixed on the OAuth path (stitchOAuthRequestURL) and for known rows (2026-08-03); this join was the last one left.What is deliberately NOT changed
The degraded path stays shape-agnostic. Its existing note is explicit, and it stands:
For an unknown vendor we cannot tell a mount prefix (
https://gw.example/proxyserving/proxy/v1/chat/completions) from a complete API root — so we must not decide. This change decides nothing about shape. It only refuses to emit the same segment twice;/v1/v1is wrong under every reading. The segment still survives — contributed bybasePathinstead ofreqPath— so the bare-host case that note protects is byte-identical.trailingVersionSegmentsharestrimLeadingVersionSegment's digits-only strictness, so/v1betaand/v1abcare never duplicates of/v1.Behaviour matrix
https://www.cun.ai/v1/chat/completions/v1/chat/completionshttps://gw.example/proxy/v1/chat/completions/proxy/v1/chat/completionshttps://relay/api/v3/v1/chat/completions/api/v3/v1/chat/completionshttps://gw.example/v1beta/v1/models/v1beta/v1/modelshttps://www.cun.ai/v1/v1/chat/completions/v1/chat/completionshttps://relay/api/v3/v3/chat/completions/api/v3/chat/completionsFences
stitch_unknown_host_dedup_test.go— 11 cases. 4 pin the collapse; 6 pin that every other shape stays byte-identical (those carry the weight — they guard the "do not decide for an unknown vendor" rule); 1 pins the strictness helper. Mutation-verified: removing the guard reproduceswww.cun.ai/v1/v1/chat/completionsexactly.Not done
D-17's fail-loud half.
provider_routesis an embedded fixed list with no user-extension mechanism, so rejecting unknown hosts would break every third-party relay outright — a product-capability decision needing a relay-registration channel first. Remains 单独立项.Refs:
workflow/CI/bugfix/2026-08-15-degraded-stitch-duplicates-version-segment.md🤖 Generated with Claude Code