Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a significant feature allowing users to configure custom API endpoints and path overrides for channels. The implementation spans the entire stack, including database schema updates, GraphQL API enhancements, a new frontend dialog for endpoint management, and logic in the backend orchestrator to select appropriate transformers based on request types. Additionally, the linting configuration was updated to support multi-module checks. Feedback focuses on a critical security risk where several gosec analyzers were disabled globally without sufficient justification, and a logic bug in the channel mutation input handling where the wrong field was being referenced during endpoint appending.
| - linters: [gosec] | ||
| text: "(G704: SSRF via taint analysis|G703: Path traversal via taint analysis|G702: Command injection via taint analysis|G115: integer overflow conversion uintptr|G705: XSS via taint analysis)" |
There was a problem hiding this comment.
Disabling these gosec taint analysis rules (G702, G703, G704, G705) and the integer overflow check (G115) across the entire project is highly risky. These rules protect against critical vulnerabilities like Command Injection, Path Traversal, SSRF, and XSS. The comment "those reports are not relevant" is not a sufficient justification for disabling such important security checks globally. If there are specific false positives, they should be excluded on a case-by-case basis using //nolint:gosec comments with a clear explanation for each case. Please remove this global exclusion.
| if i.AppendEndpoints != nil { | ||
| m.AppendEndpoints(i.Endpoints) | ||
| } |
There was a problem hiding this comment.
There is a bug in the UpdateChannelInput.Mutate method. When AppendEndpoints is used in a mutation, the code incorrectly appends i.Endpoints instead of i.AppendEndpoints. This will lead to incorrect data being persisted.
| if i.AppendEndpoints != nil { | |
| m.AppendEndpoints(i.Endpoints) | |
| } | |
| if i.AppendEndpoints != nil { | |
| m.AppendEndpoints(i.AppendEndpoints) | |
| } |
| var SupportedAPIFormats = map[string]struct{}{ | ||
| "openai/chat_completions": {}, | ||
| "openai/responses": {}, | ||
| "openai/responses_compact": {}, | ||
| "openai/embeddings": {}, | ||
| "openai/image_generation": {}, | ||
| "openai/image_edit": {}, | ||
| "openai/image_variation": {}, | ||
| "openai/video": {}, | ||
| "anthropic/messages": {}, | ||
| "gemini/contents": {}, | ||
| "gemini/embeddings": {}, | ||
| "jina/rerank": {}, | ||
| "jina/embeddings": {}, | ||
| "ollama/chat": {}, | ||
| } |
There was a problem hiding this comment.
🔴 SupportedAPIFormats allows formats that buildNonDefaultEndpointOutbound cannot handle, silently disabling channels
ValidateEndpoints (channel_override.go:218-233) uses SupportedAPIFormats which includes openai/responses_compact, openai/video, and gemini/embeddings. However, buildNonDefaultEndpointOutbound (channel_llm.go:228-289) has no case for these formats and returns an error via the default branch. This means: (1) a user can successfully save an endpoint with e.g. openai/responses_compact because validation passes, (2) on the next enabled-channels cache reload, buildChannelWithOutbounds propagates the error, and (3) reloadEnabledChannels (channel.go:227-228) logs a warning and skips the channel entirely via continue. The channel silently disappears from load balancing with no user-visible error at save time.
Prompt for agents
The SupportedAPIFormats map in channel_override.go (used by ValidateEndpoints) accepts formats that buildNonDefaultEndpointOutbound in channel_llm.go cannot build outbound transformers for. The formats openai/responses_compact, openai/video, and gemini/embeddings pass validation but cause buildChannelWithOutbounds to fail, which silently excludes the channel from the enabled channels list.
Two possible fixes:
1. Remove the unsupported formats (openai/responses_compact, openai/video, gemini/embeddings) from SupportedAPIFormats so they are rejected at validation time.
2. Add handling for these formats in buildNonDefaultEndpointOutbound so they actually work.
Option 1 is safer if these formats are not yet implemented. The key files are internal/server/biz/channel_override.go (SupportedAPIFormats map) and internal/server/biz/channel_llm.go (buildNonDefaultEndpointOutbound switch).
Was this helpful? React with 👍 or 👎 to provide feedback.
| var defaultEndpointsForChannelType = map[channel.Type][]objects.ChannelEndpoint{ | ||
| channel.TypeOpenai: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeOpenaiResponses: {{APIFormat: "openai/responses"}}, | ||
| channel.TypeCodex: {{APIFormat: "openai/responses"}}, | ||
| channel.TypeVercel: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeAnthropic: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeAnthropicAWS: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeAnthropicGcp: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeGeminiOpenai: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeGemini: {{APIFormat: "gemini/contents"}}, | ||
| channel.TypeGeminiVertex: {{APIFormat: "gemini/contents"}}, | ||
| channel.TypeDeepseek: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeDeepseekAnthropic: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeDeepinfra: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeFireworks: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeDoubao: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeDoubaoAnthropic: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeMoonshot: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeMoonshotAnthropic: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeZhipu: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeZai: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeZhipuAnthropic: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeZaiAnthropic: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeAnthropicFake: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeOpenaiFake: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeOpenrouter: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeXiaomi: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeXai: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypePpio: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeSiliconflow: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeVolcengine: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeLongcat: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeLongcatAnthropic: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeMinimax: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeMinimaxAnthropic: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeAihubmix: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeBurncloud: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeModelscope: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeBailian: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeBailianAnthropic: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeMoonshotCoding: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeJina: { | ||
| {APIFormat: "jina/rerank"}, | ||
| {APIFormat: "jina/embeddings"}, | ||
| }, | ||
| channel.TypeGithub: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeGithubCopilot: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeClaudecode: {{APIFormat: "anthropic/messages"}}, | ||
| channel.TypeCerebras: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeAntigravity: {{APIFormat: "gemini/contents"}}, | ||
| channel.TypeNanogpt: {{APIFormat: "openai/chat_completions"}}, | ||
| channel.TypeNanogptResponses: {{APIFormat: "openai/responses"}}, | ||
| channel.TypeOllama: {{APIFormat: "ollama/chat"}}, | ||
| } |
There was a problem hiding this comment.
🟡 Three channel types missing from defaultEndpointsForChannelType map
The defaultEndpointsForChannelType map in channel_llm.go:1085-1138 is missing entries for TypeXiaomiAnthropic, TypeVolcengineAnthropic, and TypeAihubmixAnthropic. These types are valid channel types (defined in internal/ent/channel/channel.go) and use Anthropic transformers. Without entries, ResolveEndpoints() returns nil for these types, so buildChannelWithOutbounds returns early with a nil Outbounds map and the multi-endpoint feature is inoperative. The same three types are also missing from the frontend's CHANNEL_TYPE_TO_DEFAULT_ENDPOINTS in frontend/src/features/channels/data/config_channels.ts:708-761, causing the endpoints dialog to show empty defaults for these channel types.
Prompt for agents
Three channel types are missing from the defaultEndpointsForChannelType map in internal/server/biz/channel_llm.go: TypeXiaomiAnthropic, TypeVolcengineAnthropic, and TypeAihubmixAnthropic. All three should map to []objects.ChannelEndpoint{{APIFormat: "anthropic/messages"}} since they are Anthropic-protocol variants of their respective providers.
The same three types (xiaomi_anthropic, volcengine_anthropic, aihubmix_anthropic) are also missing from CHANNEL_TYPE_TO_DEFAULT_ENDPOINTS in frontend/src/features/channels/data/config_channels.ts. They should each map to [{ apiFormat: ANTHROPIC_MESSAGES }].
Both maps need to be updated together to keep frontend and backend consistent.
Was this helpful? React with 👍 or 👎 to provide feedback.
Greptile SummaryThis PR adds per-channel customizable endpoint configuration, allowing administrators to assign one or more API-format/path pairs to a channel, overriding the default inferred from the channel type. The change spans the full stack: a new Ent
Confidence Score: 4/5Safe to merge after fixing the aisdk/text and aisdk/datastream schema mismatch that makes those dropdown options always error on save One P1 bug: two API format values in the frontend dropdown (aisdk/text, aisdk/datastream) are not in the backend SupportedAPIFormats, so selecting either and saving will always fail with a validation error. The rest of the implementation is solid — validation, fallback logic, outbound builder dispatch, and cache invalidation all look correct. frontend/src/features/channels/data/schema.ts (remove unsupported aisdk formats) and internal/server/biz/channel_override.go (consider syncing with frontend list) Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Incoming Request] --> B[CandidateSelector]
B --> C{Model Association or Legacy Channel?}
C -->|Association| D[resolveAssociations]
C -->|Legacy| E[selectChannelCandidates]
D --> F[filterResolvedCandidatesForRequest]
F --> G[aggregateChannelModelCandidates]
G --> H[populateAPIFormat]
E --> H
H --> I[Channel.ResolveEndpoints]
I --> J{Custom Endpoints Configured?}
J -->|Yes| K[Use ch.Endpoints]
J -->|No| L[DefaultEndpointsForChannelType]
K --> M[SelectAPIFormatForRequestType]
L --> M
M --> N{Request Type Match?}
N -->|Match Found| O[Use Matched APIFormat]
N -->|No Match| P[Fallback: endpoints 0 .APIFormat]
O --> Q[ChannelModelsCandidate with APIFormat]
P --> Q
Q --> R[buildChannelWithOutbounds]
R --> S{Endpoint = default with no path?}
S -->|Yes| T[Reuse existing ch.Outbound]
S -->|No| U[buildNonDefaultEndpointOutbound]
U --> V[New Outbound Transformer per api_format]
T --> W[ch.Outbounds map]
V --> W
|
| var SupportedAPIFormats = map[string]struct{}{ | ||
| "openai/chat_completions": {}, | ||
| "openai/responses": {}, | ||
| "openai/responses_compact": {}, | ||
| "openai/embeddings": {}, | ||
| "openai/image_generation": {}, | ||
| "openai/image_edit": {}, | ||
| "openai/image_variation": {}, | ||
| "openai/video": {}, | ||
| "anthropic/messages": {}, | ||
| "gemini/contents": {}, | ||
| "gemini/embeddings": {}, | ||
| "jina/rerank": {}, | ||
| "jina/embeddings": {}, | ||
| "ollama/chat": {}, | ||
| } |
There was a problem hiding this comment.
SupportedAPIFormats and frontend apiFormatSchema are out of sync
SupportedAPIFormats contains gemini/embeddings, openai/responses_compact, and openai/video — none of which appear in the frontend's apiFormatSchema enum. Conversely, the frontend enum includes aisdk/text and aisdk/datastream which are absent from SupportedAPIFormats (see related comment on schema.ts). Consider either adding a shared source-of-truth (e.g. GraphQL enum) that both sides derive from, or at least a test that cross-checks them. Without this, divergence will silently re-occur whenever formats are added on one side.
Uh oh!
There was an error while loading. Please reload this page.