feat: support Tencent TokenHub API key via OpenAI-compatible protocol (#6232)

* feat: support tencent tokenhub api key via openai-compatible protocol

* fix: use tokenhub base url for tencent api key channels

* test: cover tencent key-format dispatch and add TokenHub key prompt locales
This commit is contained in:
feitianbubu
2026-07-25 22:05:09 +08:00
committed by GitHub
parent 8b41defbe0
commit 08f88d25e5
12 changed files with 119 additions and 9 deletions
+37
View File
@@ -0,0 +1,37 @@
package tencent
import (
"strings"
"github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/relay/channel"
"github.com/QuantumNous/new-api/relay/channel/openai"
relaycommon "github.com/QuantumNous/new-api/relay/common"
)
const tokenHubBaseURL = "https://tokenhub.tencentmaas.com"
// DispatchAdaptor 按密钥格式分流:三段式 ak/sk 走原生 TC3,单段 TokenHub key 走 OpenAI 兼容。
type DispatchAdaptor struct {
channel.Adaptor
}
func (a *DispatchAdaptor) Init(info *relaycommon.RelayInfo) {
if strings.Contains(info.ApiKey, "|") {
a.Adaptor = &Adaptor{}
} else {
a.Adaptor = &openai.Adaptor{}
if info.ChannelBaseUrl == "" || info.ChannelBaseUrl == constant.ChannelBaseURLs[constant.ChannelTypeTencent] {
info.ChannelBaseUrl = tokenHubBaseURL
}
}
a.Adaptor.Init(info)
}
func (a *DispatchAdaptor) GetModelList() []string {
return ModelList
}
func (a *DispatchAdaptor) GetChannelName() string {
return ChannelName
}
+72
View File
@@ -0,0 +1,72 @@
package tencent
import (
"testing"
"github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/relay/channel/openai"
relaycommon "github.com/QuantumNous/new-api/relay/common"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDispatchAdaptorInit(t *testing.T) {
tests := []struct {
name string
apiKey string
baseURL string
wantTC3 bool
wantBaseURL string
}{
{
name: "legacy three-segment key selects TC3 adaptor and keeps base url",
apiKey: "1300000000|AKIDxxxxxxxx|secretxxxxxxxx",
baseURL: constant.ChannelBaseURLs[constant.ChannelTypeTencent],
wantTC3: true,
wantBaseURL: constant.ChannelBaseURLs[constant.ChannelTypeTencent],
},
{
name: "tokenhub key with default base url rewrites to tokenhub",
apiKey: "sk-xxxxxxxxxxxxxxxx",
baseURL: constant.ChannelBaseURLs[constant.ChannelTypeTencent],
wantTC3: false,
wantBaseURL: tokenHubBaseURL,
},
{
name: "tokenhub key with empty base url rewrites to tokenhub",
apiKey: "sk-xxxxxxxxxxxxxxxx",
baseURL: "",
wantTC3: false,
wantBaseURL: tokenHubBaseURL,
},
{
name: "tokenhub key with custom base url is preserved",
apiKey: "sk-xxxxxxxxxxxxxxxx",
baseURL: "https://proxy.example.com",
wantTC3: false,
wantBaseURL: "https://proxy.example.com",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
info := &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{
ChannelType: constant.ChannelTypeTencent,
ApiKey: tt.apiKey,
ChannelBaseUrl: tt.baseURL,
}}
dispatch := &DispatchAdaptor{}
dispatch.Init(info)
require.NotNil(t, dispatch.Adaptor)
if tt.wantTC3 {
assert.IsType(t, &Adaptor{}, dispatch.Adaptor)
} else {
assert.IsType(t, &openai.Adaptor{}, dispatch.Adaptor)
}
assert.Equal(t, tt.wantBaseURL, info.ChannelBaseUrl)
})
}
}
+1
View File
@@ -342,6 +342,7 @@ var streamSupportedChannels = map[int]bool{
constant.ChannelTypeMiniMax: true, constant.ChannelTypeMiniMax: true,
constant.ChannelTypeSiliconFlow: true, constant.ChannelTypeSiliconFlow: true,
constant.ChannelTypeAdvancedCustom: true, constant.ChannelTypeAdvancedCustom: true,
constant.ChannelTypeTencent: true,
} }
func GenRelayInfoWs(c *gin.Context, ws *websocket.Conn) *RelayInfo { func GenRelayInfoWs(c *gin.Context, ws *websocket.Conn) *RelayInfo {
+1 -1
View File
@@ -66,7 +66,7 @@ func GetAdaptor(apiType int) channel.Adaptor {
case constant.APITypePaLM: case constant.APITypePaLM:
return &palm.Adaptor{} return &palm.Adaptor{}
case constant.APITypeTencent: case constant.APITypeTencent:
return &tencent.Adaptor{} return &tencent.DispatchAdaptor{}
case constant.APITypeXunfei: case constant.APITypeXunfei:
return &xunfei.Adaptor{} return &xunfei.Adaptor{}
case constant.APITypeZhipu: case constant.APITypeZhipu:
+1 -1
View File
@@ -386,7 +386,7 @@ export const TYPE_TO_KEY_PROMPT: Record<number, string> = {
15: 'Format: APIKey|SecretKey', 15: 'Format: APIKey|SecretKey',
18: 'Format: APPID|APISecret|APIKey', 18: 'Format: APPID|APISecret|APIKey',
22: 'Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041', 22: 'Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041',
23: 'Format: AppId|SecretId|SecretKey', 23: 'Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey',
33: 'Format: Ak|Sk|Region', 33: 'Format: Ak|Sk|Region',
50: 'Format: AccessKey|SecretKey (or just ApiKey if upstream is New API)', 50: 'Format: AccessKey|SecretKey (or just ApiKey if upstream is New API)',
51: 'Format: Access Key ID|Secret Access Key', 51: 'Format: Access Key ID|Secret Access Key',
+1 -1
View File
@@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041", "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "Format: APIKey|SecretKey", "Format: APIKey|SecretKey": "Format: APIKey|SecretKey",
"Format: APPID|APISecret|APIKey": "Format: APPID|APISecret|APIKey", "Format: APPID|APISecret|APIKey": "Format: APPID|APISecret|APIKey",
"Format: AppId|SecretId|SecretKey": "Format: AppId|SecretId|SecretKey", "Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey": "Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey",
"Forward requests directly to upstream providers without any post-processing.": "Forward requests directly to upstream providers without any post-processing.", "Forward requests directly to upstream providers without any post-processing.": "Forward requests directly to upstream providers without any post-processing.",
"Frames per second": "Frames per second", "Frames per second": "Frames per second",
"Free": "Free", "Free": "Free",
+1 -1
View File
@@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Format : APIKey-AppId, par ex. fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041", "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Format : APIKey-AppId, par ex. fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "Format : APIKey|SecretKey", "Format: APIKey|SecretKey": "Format : APIKey|SecretKey",
"Format: APPID|APISecret|APIKey": "Format : APPID|APISecret|APIKey", "Format: APPID|APISecret|APIKey": "Format : APPID|APISecret|APIKey",
"Format: AppId|SecretId|SecretKey": "Format : AppId|SecretId|SecretKey", "Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey": "Format : TokenHub API Key, ou l'ancien format AppId|SecretId|SecretKey",
"Forward requests directly to upstream providers without any post-processing.": "Transférer les requêtes directement aux fournisseurs amont sans aucun post-traitement.", "Forward requests directly to upstream providers without any post-processing.": "Transférer les requêtes directement aux fournisseurs amont sans aucun post-traitement.",
"Frames per second": "Images par seconde", "Frames per second": "Images par seconde",
"Free": "Libre", "Free": "Libre",
+1 -1
View File
@@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "形式: APIKey-AppId、例: fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041", "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "形式: APIKey-AppId、例: fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "形式: APIKey|SecretKey", "Format: APIKey|SecretKey": "形式: APIKey|SecretKey",
"Format: APPID|APISecret|APIKey": "形式: APPID|APISecret|APIKey", "Format: APPID|APISecret|APIKey": "形式: APPID|APISecret|APIKey",
"Format: AppId|SecretId|SecretKey": "形式: AppId|SecretId|SecretKey", "Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey": "形式: TokenHub API Key、または旧形式の AppId|SecretId|SecretKey",
"Forward requests directly to upstream providers without any post-processing.": "ポストプロセスなしで、リクエストをアップストリームプロバイダーに直接転送します。", "Forward requests directly to upstream providers without any post-processing.": "ポストプロセスなしで、リクエストをアップストリームプロバイダーに直接転送します。",
"Frames per second": "フレームレート", "Frames per second": "フレームレート",
"Free": "空き", "Free": "空き",
+1 -1
View File
@@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Формат: APIKey-AppId, напр., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041", "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Формат: APIKey-AppId, напр., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "Формат: APIKey|SecretKey", "Format: APIKey|SecretKey": "Формат: APIKey|SecretKey",
"Format: APPID|APISecret|APIKey": "Формат: APPID|APISecret|APIKey", "Format: APPID|APISecret|APIKey": "Формат: APPID|APISecret|APIKey",
"Format: AppId|SecretId|SecretKey": "Формат: AppId|SecretId|SecretKey", "Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey": "Формат: TokenHub API Key или устаревший AppId|SecretId|SecretKey",
"Forward requests directly to upstream providers without any post-processing.": "Перенаправлять запросы напрямую upstream-провайдерам без какой-либо постобработки.", "Forward requests directly to upstream providers without any post-processing.": "Перенаправлять запросы напрямую upstream-провайдерам без какой-либо постобработки.",
"Frames per second": "Кадров в секунду", "Frames per second": "Кадров в секунду",
"Free": "Свободно", "Free": "Свободно",
+1 -1
View File
@@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Định dạng: APIKey-AppId, ví dụ: fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041", "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Định dạng: APIKey-AppId, ví dụ: fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "Định dạng: APIKey|SecretKey", "Format: APIKey|SecretKey": "Định dạng: APIKey|SecretKey",
"Format: APPID|APISecret|APIKey": "Định dạng: APPID|APISecret|APIKey", "Format: APPID|APISecret|APIKey": "Định dạng: APPID|APISecret|APIKey",
"Format: AppId|SecretId|SecretKey": "Định dạng: AppId|SecretId|SecretKey", "Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey": "Định dạng: TokenHub API Key, hoặc định dạng cũ AppId|SecretId|SecretKey",
"Forward requests directly to upstream providers without any post-processing.": "Chuyển tiếp các yêu cầu trực tiếp đến các nhà cung cấp ngược dòng mà không cần xử lý hậu kỳ nào.", "Forward requests directly to upstream providers without any post-processing.": "Chuyển tiếp các yêu cầu trực tiếp đến các nhà cung cấp ngược dòng mà không cần xử lý hậu kỳ nào.",
"Frames per second": "Khung hình / giây", "Frames per second": "Khung hình / giây",
"Free": "Trống", "Free": "Trống",
+1 -1
View File
@@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "格式:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041", "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "格式:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "格式:APIKey|SecretKey", "Format: APIKey|SecretKey": "格式:APIKey|SecretKey",
"Format: APPID|APISecret|APIKey": "格式:APPID|APISecret|APIKey", "Format: APPID|APISecret|APIKey": "格式:APPID|APISecret|APIKey",
"Format: AppId|SecretId|SecretKey": "格式:AppId|SecretId|SecretKey", "Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey": "格式:TokenHub API Key,或舊版 AppId|SecretId|SecretKey",
"Forward requests directly to upstream providers without any post-processing.": "將請求直接轉發給上游供應商,不進行任何後處理。", "Forward requests directly to upstream providers without any post-processing.": "將請求直接轉發給上游供應商,不進行任何後處理。",
"Frames per second": "幀率", "Frames per second": "幀率",
"Free": "可用", "Free": "可用",
+1 -1
View File
@@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "格式:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041", "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "格式:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "格式:APIKey|SecretKey", "Format: APIKey|SecretKey": "格式:APIKey|SecretKey",
"Format: APPID|APISecret|APIKey": "格式:APPID|APISecret|APIKey", "Format: APPID|APISecret|APIKey": "格式:APPID|APISecret|APIKey",
"Format: AppId|SecretId|SecretKey": "格式:AppId|SecretId|SecretKey", "Format: TokenHub API Key, or legacy AppId|SecretId|SecretKey": "格式:TokenHub API Key,或旧版 AppId|SecretId|SecretKey",
"Forward requests directly to upstream providers without any post-processing.": "将请求直接转发给上游提供商,不进行任何后处理。", "Forward requests directly to upstream providers without any post-processing.": "将请求直接转发给上游提供商,不进行任何后处理。",
"Frames per second": "帧率", "Frames per second": "帧率",
"Free": "可用", "Free": "可用",