feat: custom channel - #2675
Conversation
Gemini Image系列支持图像编辑
feat: 视频下载和界面预览统一使用OAI标准接口
fix(aws): extract HTTP status code from AWS SDK errors
fix: nano-banana not compatible imageSize
…und-debugging feat(playground): enhance SSE debugging and add image paste support with i18n
fix: nano banana pro 4k(StreamScannerMaxBufferMB env)
feat: glm coding plan && kimi coding plan
fix: claude request missing field
fix(i18n): fill missing translations in i18n.
…mageConfig Revert "fix: gemini image correct generationConfig"
…1-i2v Revert "Gemini Veo3.1[AI Studio]增加图生视频支持"
…-edit Revert "Gemini Image系列支持图像编辑"
…ana-err Revert "fix: nano-banana not compatible imageSize"
…-pro-image-preview-oai Revert "OAI生图接口支持gemini 3 pro image preview"
…dels (Midjourney, Rerank, Suno). Add OpenAPI specifications for backend management and relay interfaces.
Use the native Gemini Models API (/v1beta/models) instead of the OpenAI-compatible path when listing models for Gemini channels, improving compatibility with third-party Gemini-format providers that don't implement OpenAI routes. - Add paginated model listing with timeout and optional proxy support - Select an enabled key for multi-key Gemini channels
fix: remove Minimax from FETCHABLE channels
fix(gemini): fetch model list via native v1beta/models endpoint
* feat: /v1/chat/completion -> /v1/response
feat: status code auto-disable configuration
* fix: setting ui * fix: rm global.chat_completions_to_responses_policy * fix: rm global.chat_completions_to_responses_policy
fix: clean propertyNames for gemini function
…rride feat: channel testing supports parameter overriding
* feat: codex channel * feat: codex channel * feat: codex oauth flow * feat: codex refresh cred * feat: codex usage * fix: codex err message detail * fix: codex setting ui * feat: codex refresh cred task * fix: import err * fix: codex store must be false * fix: chat -> responses tool call * fix: chat -> responses tool call
# Conflicts: # common/api_type.go # constant/api_type.go # constant/channel.go # relay/relay_adaptor.go # web/src/components/table/channels/modals/EditChannelModal.jsx
…sing Claude's rendering logs, the two approaches handle input rendering differently.
WalkthroughIntroduces a new multi-endpoint channel type (ChannelTypeMultiEndpoint) that enables routing requests to different upstream endpoints based on configured mappings and request paths. Includes backend URL resolution, relay adaptor implementation, validation logic, and frontend configuration UI. Changes
Sequence DiagramsequenceDiagram
participant UI as Frontend<br/>(EditChannelModal)
participant Ctrl as Controller
participant Resolver as URL Resolver<br/>(multi_endpoint_request_url)
participant RelayAdaptor as Relay Adaptor<br/>(multiendpoint)
participant SubAdaptor as Sub-Adaptor<br/>(OpenAI/Claude/Gemini)
UI->>Ctrl: POST /channel validate config
Ctrl->>Resolver: ResolveMultiEndpointRequestURL(baseURL, path)
Resolver->>Resolver: Parse JSON or plain URL
Resolver->>Resolver: Apply template {model}, {path}, {query}
Resolver->>Resolver: Validate scheme & required endpoints
Resolver-->>Ctrl: Resolved URL or error
Ctrl-->>UI: Validation result
UI->>Ctrl: Route request via multi-endpoint channel
Ctrl->>RelayAdaptor: Init & GetRequestURL
RelayAdaptor->>Resolver: ResolveMultiEndpointRequestURL(config, requestPath)
Resolver-->>RelayAdaptor: Resolved URL
RelayAdaptor->>RelayAdaptor: Select sub-adaptor by relay format/mode
RelayAdaptor->>SubAdaptor: ConvertRequest & DoRequest
SubAdaptor-->>RelayAdaptor: Response
RelayAdaptor->>RelayAdaptor: DoResponse
RelayAdaptor-->>Ctrl: Final response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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
🤖 Fix all issues with AI agents
In `@constant/channel.go`:
- Line 122: Update the incorrect inline index comment for the
ChannelTypeMultiEndpoint entry: change the trailing comment from "//57" to
"//58" near the ChannelTypeMultiEndpoint constant definition (the previous line
already uses index 57 for ChannelTypeCodex), so the comment matches the actual
index without changing any code logic.
In `@relay/channel/multiendpoint/adaptor.go`:
- Around line 118-120: GetModelList on Adaptor currently forwards only to
a.openai.GetModelList(), which omits Claude/Gemini models; update
Adaptor.GetModelList to aggregate and return the union of all sub-adaptors'
model lists (e.g., call a.openai.GetModelList(), a.claude.GetModelList(),
a.gemini.GetModelList()), deduplicate entries, and return the combined slice, or
if model lists are externally configured, document/rename the method to avoid
implying multi-endpoint coverage. Ensure you reference Adaptor.GetModelList and
the sub-adaptor fields a.openai, a.claude, a.gemini when making the change.
🧹 Nitpick comments (7)
common/multi_endpoint_request_url.go (4)
36-36: Incomplete docstring for Realtime behavior.The function comment starts "Realtime behavior:" but has no content. Either complete the documentation or remove the placeholder.
84-98: Silent skipping of invalid config entries may hide misconfiguration.Invalid keys (unrecognized after canonicalization) and non-string values are silently ignored. This could make debugging configuration issues difficult for users. Consider logging a warning or returning validation errors for unrecognized keys.
114-115: Consider breaking long case line for readability.Line 114 contains many aliases making it difficult to read. Consider splitting into multiple lines or grouping related aliases.
♻️ Suggested formatting
- case "image", "images", "openai_image", "openai_image_generation", "openai_image_edit", "image_generation", "imagegeneration", "image_generations", "imagegenerations", "image_edit", "imageedit", "image_edits", "imageedits": + case "image", "images", "openai_image", + "openai_image_generation", "openai_image_edit", + "image_generation", "imagegeneration", + "image_generations", "imagegenerations", + "image_edit", "imageedit", + "image_edits", "imageedits": return MultiEndpointKeyOpenAIImage
175-177: Brace detection may produce false positives for legitimate URLs.URLs can legitimately contain
{or}characters (especially in query parameters). This check could reject valid URLs. Consider checking only for specific unsupported placeholder patterns like{word}instead.♻️ Suggested pattern-based check
- if strings.Contains(out, "{") || strings.Contains(out, "}") { - return "", fmt.Errorf("multi-endpoint URL template contains unsupported variables") + // Check for unsupported placeholders like {variable} + if matched, _ := regexp.MatchString(`\{[a-zA-Z_]+\}`, out); matched { + return "", fmt.Errorf("multi-endpoint URL template contains unsupported variables") }common/multi_endpoint_request_url_test.go (1)
1-139: Comprehensive test coverage for multi-endpoint URL resolution.The tests cover key scenarios including:
- Plain URL and JSON config parsing
- Default/fallback resolution
- Path-based endpoint selection
- Key canonicalization (aliases like
openai-response→openai_responses)- Template variable substitution (
{model},{path},{query})- WebSocket scheme support
- Error handling for invalid JSON
Consider adding edge case tests for completeness:
💡 Optional: Additional test cases
func TestResolveMultiEndpointRequestURL_EmptyConfig(t *testing.T) { got, err := ResolveMultiEndpointRequestURL("", "/v1/chat/completions", "gpt-4o-mini") if err != nil { t.Fatalf("unexpected error: %v", err) } if got != "" { t.Fatalf("expected empty string, got: %q", got) } } func TestResolveMultiEndpointRequestURL_WhitespaceOnlyConfig(t *testing.T) { got, err := ResolveMultiEndpointRequestURL(" \n\t ", "/v1/chat/completions", "gpt-4o-mini") if err != nil { t.Fatalf("unexpected error: %v", err) } if got != "" { t.Fatalf("expected empty string, got: %q", got) } }web/src/components/table/channels/modals/MultiEndpointBaseUrlEditor.jsx (1)
237-241: Consider usingvaluedirectly in the useEffect dependency array.The current dependencies
[parsed.mapping, parsed.error]might cause the effect to miss updates if the raw value changes but parses to the same mapping. Sinceparsedis memoized onvalue, usingvaluedirectly would be clearer:💡 Optional: Simplify dependency array
useEffect(() => { setLocal(parsed.mapping); setParseError(parsed.error); setRawText(typeof value === 'string' ? value : ''); - }, [parsed.mapping, parsed.error]); + }, [value, parsed.mapping, parsed.error]);relay/channel/multiendpoint/adaptor.go (1)
34-49: Method has side effects that mutate input parameters.
GetRequestURLmutatesinfo.ChannelBaseUrlandinfo.ChannelMeta.ChannelBaseUrl(lines 43-46), which is unexpected for a method named with a "Get" prefix. This side effect pattern can lead to subtle bugs if callers don't expect mutation.Consider either:
- Renaming to
ResolveRequestURLto signal mutation, or- Adding a comment documenting this intentional side effect.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
common/api_type.gocommon/multi_endpoint_request_url.gocommon/multi_endpoint_request_url_test.goconstant/api_type.goconstant/channel.gocontroller/channel.gorelay/channel/multiendpoint/adaptor.gorelay/relay_adaptor.goweb/src/components/table/channels/modals/EditChannelModal.jsxweb/src/components/table/channels/modals/MultiEndpointBaseUrlEditor.jsxweb/src/constants/channel.constants.js
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-27T02:15:25.448Z
Learnt from: AAEE86
Repo: QuantumNous/new-api PR: 1658
File: web/src/components/table/channels/modals/EditChannelModal.jsx:555-569
Timestamp: 2025-08-27T02:15:25.448Z
Learning: In EditChannelModal.jsx, the applyModelMapping function transforms the models list by replacing original model names (mapping values) with display names (mapping keys). The database stores this transformed list containing mapped keys. On channel load, data.models contains these mapped display names, making the initialization filter if (data.models.includes(key)) correct.
Applied to files:
web/src/components/table/channels/modals/EditChannelModal.jsx
📚 Learning: 2025-08-27T02:15:25.448Z
Learnt from: AAEE86
Repo: QuantumNous/new-api PR: 1658
File: web/src/components/table/channels/modals/EditChannelModal.jsx:555-569
Timestamp: 2025-08-27T02:15:25.448Z
Learning: In EditChannelModal.jsx, the database stores mapped keys (display names) in the models field after applying model mapping transformations. When loading a channel, data.models contains the mapped keys, not the original model names. The filtering logic if (data.models.includes(key)) in the initialization is correct.
Applied to files:
web/src/components/table/channels/modals/EditChannelModal.jsx
🧬 Code graph analysis (6)
common/api_type.go (2)
constant/channel.go (1)
ChannelTypeMultiEndpoint(58-58)constant/api_type.go (1)
APITypeMultiEndpoint(39-39)
web/src/components/table/channels/modals/MultiEndpointBaseUrlEditor.jsx (1)
web/src/helpers/utils.jsx (1)
copy(72-95)
controller/channel.go (3)
constant/channel.go (1)
ChannelTypeMultiEndpoint(58-58)common/str.go (1)
StrToMap(45-52)common/multi_endpoint_request_url.go (1)
ResolveMultiEndpointRequestURL(37-76)
common/multi_endpoint_request_url_test.go (1)
common/multi_endpoint_request_url.go (1)
ResolveMultiEndpointRequestURL(37-76)
relay/relay_adaptor.go (2)
constant/api_type.go (1)
APITypeMultiEndpoint(39-39)relay/channel/multiendpoint/adaptor.go (1)
Adaptor(22-26)
common/multi_endpoint_request_url.go (2)
common/json.go (1)
Unmarshal(9-11)relay/constant/relay_mode.go (12)
Path2RelayMode(55-91)RelayModeResponses(48-48)RelayModeEmbeddings(12-12)RelayModeImagesGenerations(14-14)RelayModeImagesEdits(15-15)RelayModeEdits(16-16)RelayModeAudioSpeech(35-35)RelayModeAudioTranscription(36-36)RelayModeAudioTranslation(37-37)RelayModeRealtime(50-50)RelayModeRerank(46-46)RelayModeGemini(52-52)
🔇 Additional comments (24)
common/multi_endpoint_request_url.go (3)
1-23: LGTM!The package structure, imports, and constants are well-defined. The endpoint key constants provide clear, consistent naming for the supported endpoint types.
133-155: LGTM!The path-to-endpoint-key mapping correctly handles the Claude special case and delegates to
Path2RelayModefor standard OpenAI-compatible paths. The default fallback toMultiEndpointKeyOpenAIis appropriate.
181-216: LGTM!The URL validation is comprehensive:
- Validates URL syntax and required components (scheme, host).
- Enforces correct scheme for realtime (ws/wss) vs standard (http/https) endpoints.
- Requires explicit path and query handling to prevent silent data loss.
This explicit configuration requirement aligns well with the design note at line 218-219.
relay/relay_adaptor.go (1)
124-125: LGTM!The new case for
APITypeMultiEndpointfollows the established pattern and correctly instantiates the multiendpoint adaptor.web/src/constants/channel.constants.js (1)
122-122: LGTM!The new channel option correctly maps to the backend's
ChannelTypeMultiEndpoint = 58constant and follows the existing pattern for channel definitions.constant/api_type.go (1)
39-40: LGTM!The new
APITypeMultiEndpointconstant is correctly placed before theAPITypeDummysentinel, maintaining the iota sequence without affecting existing constant values.common/api_type.go (1)
78-79: LGTM!The new mapping from
ChannelTypeMultiEndpointtoAPITypeMultiEndpointcorrectly follows the established pattern and completes the integration between channel types and API types.controller/channel.go (2)
593-630: Multi-endpoint channel validation logic looks solid.The validation correctly:
- Ensures
base_urlis non-empty- Validates URL schemes (http/https/ws/wss) for both plain URLs and JSON object values
- Uses
ResolveMultiEndpointRequestURLto verify at least adefaultoropenaiendpoint is configuredOne minor observation: the error messages are in Chinese which is consistent with the rest of the codebase.
1167-1173: Appropriate guard for multi-endpoint channels in FetchModels.Multi-endpoint channels route requests to different backends based on the request path, so there's no single upstream to query for a model list. Returning an informative error message is the right approach.
constant/channel.go (1)
58-58: New channel type constant and name mapping are properly defined.The constant
ChannelTypeMultiEndpoint = 58and its corresponding name "MultiEndpoint" inChannelTypeNamesare correctly added and follow the existing patterns.Also applies to: 180-180
web/src/components/table/channels/modals/EditChannelModal.jsx (4)
189-192: State management for multi-endpoint editor is properly initialized.The two new state variables handle the editor's visibility and draft content appropriately, following the component's existing patterns.
502-518: Codex channel (type 57) template initialization looks appropriate.The template is only set when
base_urlis empty, providing a helpful default without overwriting existing configurations.
2578-2610: Multi-endpoint channel UI (type 58) is well-designed.The UI provides clear visual feedback with a status Tag showing whether endpoints are configured, and a dedicated button to open the endpoint mapping editor. The
isIonetLockedcheck appropriately prevents editing for IO.NET-managed channels.
3403-3433: Modal for endpoint mapping editor is correctly integrated.The modal properly:
- Uses draft state to isolate changes until explicitly saved
- Respects
isIonetLockedto prevent unauthorized modifications- Applies changes through
handleInputChangewhich triggers proper form state updatesweb/src/components/table/channels/modals/MultiEndpointBaseUrlEditor.jsx (5)
44-126: Well-structured endpoint configuration metadata.The
ENDPOINTSarray provides clear metadata for each supported endpoint including:
- Canonical keys for backend matching
- Descriptions for user guidance
- Path patterns for auto-fill functionality
fillableflag to control quick-fill behaviorThe separation of concerns here is clean and maintainable.
130-176: Key canonicalization handles aliases comprehensively.The function normalizes various key formats (underscores, hyphens, singular/plural) to canonical keys, ensuring flexibility in user input while maintaining consistency. The empty string return for unrecognized keys is safely handled by the caller.
183-222: Config parsing and serialization are well-implemented.Notable design choices:
parseConfiggracefully handles both plain URLs and JSON configsstableStringifymaintains consistent key ordering for predictable output- Single-endpoint optimization (line 220) returns plain URL when only
openaiis configured, reducing complexity for simple cases
420-536: Visual editing mode provides excellent UX.The implementation includes:
- Clear navigation with status indicators (configured/required/unconfigured)
- Inline help text explaining the expected input format
- Quick-fill feature to auto-complete endpoints from a base URL
- Proper i18n support with
t()for all user-facing strings
408-418: Raw editing mode with real-time validation is useful for advanced users.The textarea provides direct JSON editing with immediate feedback via the error banner when parsing fails.
relay/channel/multiendpoint/adaptor.go (5)
51-66: LGTM!Header setup correctly delegates to the appropriate sub-adaptor based on relay format, with proper OpenAI-Organization header handling for the default case.
76-81: Consider adding Claude format handling for consistency.
ConvertEmbeddingRequesthandles Gemini format explicitly but falls back to OpenAI for Claude format. If Claude supports embeddings with a different format, this could produce incorrect requests. If Claude doesn't support embeddings or uses OpenAI-compatible format, consider adding a comment clarifying this intentional design.
95-105: LGTM!Request handling correctly routes to the appropriate method (form, WebSocket, or API) based on relay mode.
107-116: LGTM!Response handling correctly delegates to the appropriate sub-adaptor based on relay format.
126-132: LGTM!Claude and Gemini request conversion methods correctly delegate to their respective sub-adaptors.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| "https://api.openai.com", //55 | ||
| "https://api.replicate.com", //56 | ||
| "https://chatgpt.com", //57 | ||
| "", //57 |
There was a problem hiding this comment.
Incorrect comment: should be //58 not //57.
The comment indicates index 57, but this entry is at index 58 for ChannelTypeMultiEndpoint. The previous line (121) already covers index 57 for ChannelTypeCodex.
🔧 Suggested fix
- "", //57
+ "", //58📝 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.
| "", //57 | |
| "", //58 |
🤖 Prompt for AI Agents
In `@constant/channel.go` at line 122, Update the incorrect inline index comment
for the ChannelTypeMultiEndpoint entry: change the trailing comment from "//57"
to "//58" near the ChannelTypeMultiEndpoint constant definition (the previous
line already uses index 57 for ChannelTypeCodex), so the comment matches the
actual index without changing any code logic.
| func (a *Adaptor) GetModelList() []string { | ||
| return a.openai.GetModelList() | ||
| } |
There was a problem hiding this comment.
Model list only includes OpenAI models.
GetModelList returns only the OpenAI adaptor's model list, but this multi-endpoint adaptor also supports Claude and Gemini formats. If this method is used for model validation or discovery, it may incorrectly reject valid Claude/Gemini models.
Consider aggregating models from all three sub-adaptors or clarifying if model lists are configured externally.
🤖 Prompt for AI Agents
In `@relay/channel/multiendpoint/adaptor.go` around lines 118 - 120, GetModelList
on Adaptor currently forwards only to a.openai.GetModelList(), which omits
Claude/Gemini models; update Adaptor.GetModelList to aggregate and return the
union of all sub-adaptors' model lists (e.g., call a.openai.GetModelList(),
a.claude.GetModelList(), a.gemini.GetModelList()), deduplicate entries, and
return the combined slice, or if model lists are externally configured,
document/rename the method to avoid implying multi-endpoint coverage. Ensure you
reference Adaptor.GetModelList and the sub-adaptor fields a.openai, a.claude,
a.gemini when making the change.
Summary by CodeRabbit
Release Notes
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.