feat: support v2/v3/non-v1 API version prefix in upstream channel baseURL - #5348
feat: support v2/v3/non-v1 API version prefix in upstream channel baseURL#5348waxiangzi wants to merge 2 commits into
Conversation
- Add BaseUrlHasVersionPrefix/StripVersionPrefix helpers in relay/common - Auto-strip /v1 from requestURL when baseURL already contains version prefix - Normalize version prefix in path-to-relay-mode matching - Update channel billing, models fetch, upstream sync, video proxy to use GetFullRequestURL - Update deepseek, moonshot, openai, perplexity, siliconflow adaptors
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (5)
WalkthroughThis PR centralizes request URL construction through version-aware helpers, updates relay-mode path normalization, and migrates controller and relay adaptor endpoints from direct string concatenation to shared URL generation. ChangesURL Building Centralization
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@controller/video_proxy.go`:
- Around line 110-113: The follow-up fetch for OpenAI/Sora videos uses the live
channel.Key which can break if keys rotate; update the logic in the case
handling (where relaycommon.GetFullRequestURL and task.GetUpstreamTaskID are
used) to prefer the submission-captured key stored on the task
(task.PrivateData.Key) falling back to channel.Key, and apply any stored
resolved header overrides from task.PrivateData (e.g., Authorization or other
headers) to req.Header before sending the request so the request reuses the
exact auth context captured at submission time.
In `@relay/common/relay_utils.go`:
- Around line 43-57: The function StripVersionPrefix is dropping the leading
slash when removing the version (it currently returns path[secondSlash+1:]), so
update the return in StripVersionPrefix to return path[secondSlash:] instead
(preserving the leading '/'); locate the return inside the version-detection
branch in StripVersionPrefix and replace the slice expression accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9f06659d-2fee-46b2-b5b2-f50532a2ba4f
📒 Files selected for processing (11)
controller/channel-billing.gocontroller/channel.gocontroller/channel_upstream_update.gocontroller/video_proxy.gorelay/channel/deepseek/adaptor.gorelay/channel/moonshot/adaptor.gorelay/channel/openai/adaptor.gorelay/channel/perplexity/adaptor.gorelay/channel/siliconflow/adaptor.gorelay/common/relay_utils.gorelay/constant/relay_mode.go
| case constant.ChannelTypeOpenAI, constant.ChannelTypeSora: | ||
| videoURL = fmt.Sprintf("%s/v1/videos/%s/content", baseURL, task.GetUpstreamTaskID()) | ||
| videoURL = relaycommon.GetFullRequestURL(baseURL, fmt.Sprintf("/v1/videos/%s/content", task.GetUpstreamTaskID()), channel.Type) | ||
| req.Header.Set("Authorization", "Bearer "+channel.Key) | ||
| default: |
There was a problem hiding this comment.
Reuse task-captured auth for OpenAI/Sora content fetches
Line 112 still uses channel.Key for the follow-up request. If channel keys rotate (or multi-key selection changes) after submission, completed tasks can fail to download with upstream auth errors. Prefer task.PrivateData.Key first (fallback to channel.Key), and apply stored resolved header overrides when available.
💡 Suggested fix (key selection)
case constant.ChannelTypeOpenAI, constant.ChannelTypeSora:
videoURL = relaycommon.GetFullRequestURL(baseURL, fmt.Sprintf("/v1/videos/%s/content", task.GetUpstreamTaskID()), channel.Type)
- req.Header.Set("Authorization", "Bearer "+channel.Key)
+ apiKey := strings.TrimSpace(task.PrivateData.Key)
+ if apiKey == "" {
+ apiKey = strings.TrimSpace(channel.Key)
+ }
+ req.Header.Set("Authorization", "Bearer "+apiKey)Based on learnings, async video follow-up requests should reuse authentication context captured at submission time (including task-stored key and resolved header overrides).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| case constant.ChannelTypeOpenAI, constant.ChannelTypeSora: | |
| videoURL = fmt.Sprintf("%s/v1/videos/%s/content", baseURL, task.GetUpstreamTaskID()) | |
| videoURL = relaycommon.GetFullRequestURL(baseURL, fmt.Sprintf("/v1/videos/%s/content", task.GetUpstreamTaskID()), channel.Type) | |
| req.Header.Set("Authorization", "Bearer "+channel.Key) | |
| default: | |
| case constant.ChannelTypeOpenAI, constant.ChannelTypeSora: | |
| videoURL = relaycommon.GetFullRequestURL(baseURL, fmt.Sprintf("/v1/videos/%s/content", task.GetUpstreamTaskID()), channel.Type) | |
| apiKey := strings.TrimSpace(task.PrivateData.Key) | |
| if apiKey == "" { | |
| apiKey = strings.TrimSpace(channel.Key) | |
| } | |
| req.Header.Set("Authorization", "Bearer "+apiKey) | |
| default: |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@controller/video_proxy.go` around lines 110 - 113, The follow-up fetch for
OpenAI/Sora videos uses the live channel.Key which can break if keys rotate;
update the logic in the case handling (where relaycommon.GetFullRequestURL and
task.GetUpstreamTaskID are used) to prefer the submission-captured key stored on
the task (task.PrivateData.Key) falling back to channel.Key, and apply any
stored resolved header overrides from task.PrivateData (e.g., Authorization or
other headers) to req.Header before sending the request so the request reuses
the exact auth context captured at submission time.
Source: Learnings
51fdfc5 to
2b6f1df
Compare
Problem
When Base URL contains a version prefix or custom path (e.g.
https://example.com/v2,https://open.bigmodel.cn/api/coding/paas/v4,https://api.lkeap.cloud.tencent.com/plan/v3), the system hardcodes/v1into the upstream URL, causing duplication and 404 errors.For example:
https://example.com/v2https://example.com/v2/v1/chat/completions❌Solution
1. Normalize version prefix at the router level (
relay/constant/relay_mode.go)Add
normalizeVersionPrefix(): any incoming path like/v2/chat/completionsor/v1beta/models/...is normalized to/v1/...for internal routing, so the rest of the relay pipeline works unchanged.2. Auto-detect version prefix in
GetFullRequestURL(relay/common/relay_utils.go)BaseUrlHasVersionPrefix(): parses baseURL path segments to detectvN...prefixes.StripVersionPrefix(): strips the/v1prefix from the client request path before appending to baseURL, preventing duplication when baseURL already contains a version.3. Remove hardcoded
/v1/constructions in Controller layercontroller/channel.go: model list fetchingcontroller/channel_upstream_update.go: upstream sync (volcengine/moonshot/default)controller/channel-billing.go: billing endpointscontroller/video_proxy.go: video proxyAll replaced with
GetFullRequestURL(...).4. Remove hardcoded
/v1/in OpenAI-compat Adaptor layerrelay/channel/openai/adaptor.go: Claude/Gemini conversion branch now extracts version prefix from baseURL automaticallyrelay/channel/moonshot/adaptor.gorelay/channel/perplexity/adaptor.gorelay/channel/siliconflow/adaptor.gorelay/channel/deepseek/adaptor.goUsage Example
Configure Base URL with the full upstream API prefix:
https://example.com/v2https://example.com/api/v3https://api.openai.com/v1Clients continue sending standard
/v1/chat/completionsto new-api; the gateway automatically adjusts the upstream URL based on the channel's Base URL.Summary by CodeRabbit