feat(channels): 添加对 Gemini 渠道的支持和相关配置

This commit is contained in:
Jeff Gu committed 2026-04-10 18:00:21 +08:00
1 parent 88ae321d4f
commit 7707de2b3e
10 files changed
+108 -18

No files matched your search

+2 -1
View File
@@ -90,11 +90,12 @@ const buildModelsFetchRequest = (
headers.set("anthropic-version", DEFAULT_CLAUDE_API_VERSION);
break;
case "openai":
case "gemini":
case "openai-audio":
case "openai-responses":
case "claude-to-openai":
default:
targetUrl = buildPrefixedTargetUrl(config.endpoint, "/v1/models");
targetUrl = buildPrefixedTargetUrl(config.endpoint, "/v1/models", "/v1", config.type);
headers.set("Authorization", `Bearer ${apiKey}`);
break;
}
+1 -1
View File
@@ -8,7 +8,7 @@ const buildProxyRequest = (
config: ChannelConfig
): Request => {
const url = new URL(request.url)
const targetUrl = buildPrefixedTargetUrl(config.endpoint, url.pathname)
const targetUrl = buildPrefixedTargetUrl(config.endpoint, url.pathname, "/v1", config.type)
const apiKey = config.api_key || ""
const targetHeaders = new Headers(request.headers)
+26 -1
View File
@@ -10,10 +10,27 @@ const joinPath = (...segments: string[]): string => {
return `/${cleanedSegments.join("/")}`;
};
const normalizeGeminiBasePath = (basePath: string): string => {
if (basePath.endsWith("v1beta/openai")) {
return basePath;
}
if (basePath.endsWith("v1beta")) {
return joinPath(basePath, "openai");
}
if (basePath.endsWith("openai")) {
return basePath;
}
return joinPath(basePath, "v1beta/openai");
};
export const buildPrefixedTargetUrl = (
endpoint: string,
requestPath: string,
prefixToStrip = "/v1"
prefixToStrip = "/v1",
providerType?: string | null,
): URL => {
const targetUrl = new URL(endpoint);
@@ -22,6 +39,14 @@ export const buildPrefixedTargetUrl = (
}
const currentBasePath = trimSlashes(targetUrl.pathname);
if (providerType === "gemini") {
const geminiBasePath = normalizeGeminiBasePath(currentBasePath);
const normalizedRequestPath = requestPath.replace(/^\/v1(?=\/|$)/, "");
targetUrl.pathname = joinPath(geminiBasePath, normalizedRequestPath);
return targetUrl;
}
const normalizedPrefix = trimSlashes(prefixToStrip);
const baseAlreadyContainsPrefix = normalizedPrefix.length > 0
&& currentBasePath.endsWith(normalizedPrefix);
@@ -17,6 +17,7 @@ export type ProviderFetch = (
const providerMap: Record<string, ProviderFetch> = {
"azure-openai": azureOpenaiProxy.fetch,
"openai": openaiProxy.fetch,
"gemini": openaiProxy.fetch,
"azure-openai-audio": azureOpenaiProxy.fetch,
"openai-audio": openaiProxy.fetch,
"claude": claudeProxy.fetch,
+7 -3
View File
@@ -4,16 +4,20 @@ type RoutePolicy = {
allowedTypes: ChannelType[] | null
}
const TEXT_CHANNEL_TYPES: ChannelType[] = [
const CHAT_COMPLETIONS_CHANNEL_TYPES: ChannelType[] = [
"openai",
"azure-openai",
"gemini",
]
const MESSAGES_CHANNEL_TYPES: ChannelType[] = [
"claude",
"claude-to-openai",
]
const ROUTE_POLICIES: Record<RouteId, RoutePolicy> = {
"chat-completions": { allowedTypes: TEXT_CHANNEL_TYPES },
"messages": { allowedTypes: TEXT_CHANNEL_TYPES },
"chat-completions": { allowedTypes: CHAT_COMPLETIONS_CHANNEL_TYPES },
"messages": { allowedTypes: MESSAGES_CHANNEL_TYPES },
"responses": { allowedTypes: ["openai-responses", "azure-openai-responses"] },
"audio-speech": { allowedTypes: ["openai-audio", "azure-openai-audio"] },
}