Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions relay/channel/tencent/dispatch.go
Original file line number Diff line number Diff line change
@@ -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 changes: 72 additions & 0 deletions relay/channel/tencent/dispatch_test.go
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions relay/common/relay_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ var streamSupportedChannels = map[int]bool{
constant.ChannelTypeMiniMax: true,
constant.ChannelTypeSiliconFlow: true,
constant.ChannelTypeAdvancedCustom: true,
constant.ChannelTypeTencent: true,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -t f -e go . relay/channel/tencent relay/common |
  xargs rg -n -C 3 'StreamOptions|Convert(OpenAI)?Request|DispatchAdaptor|SupportStreamOptions'

Repository: QuantumNous/new-api

Length of output: 4897


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relay/common/relay_info.go relevant sections =="
sed -n '200,250p' relay/common/relay_info.go
sed -n '320,360p' relay/common/relay_info.go

echo
echo "== relay/channel/tencent/dispatch.go =="
cat -n relay/channel/tencent/dispatch.go

echo
echo "== relay/channel/tencent/adaptor.go outline/contents =="
wc -l relay/channel/tencent/adaptor.go
sed -n '1,140p' relay/channel/tencent/adaptor.go

echo
echo "== relay/channel/tencent/dispatch_test.go =="
cat -n relay/channel/tencent/dispatch_test.go

echo
echo "== StreamOptions usages in relay/common =="
rg -n -C 4 'StreamOptions|stream_options|SupportStreamOptions' relay/common relay/channel/tencent

Repository: QuantumNous/new-api

Length of output: 17937


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find stream_options helpers and TC3 conversion =="
rg -n -C 5 'stream_options|StreamOptions|ToTencent|requestOpenAI2Tencent|Tencent.*Request|OpenAI2Tencent|ConvertOpenAIRequest' relay common types dto constant relay/channel/tencent relay/channel/openai

echo
echo "== dto stream option definitions/usages =="
rg -n -C 3 'StreamOptions|stream_options' dto relay relay/common relay/channel || true

Repository: QuantumNous/new-api

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relay/channel/openai/adaptor.go ConvertOpenAIRequest =="
sed -n '220,260p' relay/channel/openai/adaptor.go

echo
echo "== relay/channel/tencent/relay-tencent.go requestOpenAI2Tencent =="
sed -n '28,80p' relay/channel/tencent/relay-tencent.go

echo
echo "== relay/channel/tencent/dto.go =="
cat -n relay/channel/tencent/dto.go

echo
echo "== relay/compatible_handler.go stream option gating =="
sed -n '40,65p' relay/compatible_handler.go

Repository: QuantumNous/new-api

Length of output: 7465


Gate stream options to TokenHub for Tencent.

SupportStreamOptions is assigned before DispatchAdaptor.Init; enabling it for the whole ChannelTypeTencent lets legacy | TC3 keys pass stream_options into requestOpenAI2Tencent, and the TC3 request model has no such field. Remove ChannelTypeTencent from the global stream-supported set, add a per-key/format capability, or strip TC3 requests like the OpenAI adaptor does.

🤖 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 `@relay/common/relay_info.go` at line 345, Restrict SupportStreamOptions for
Tencent so legacy TC3 requests do not pass stream_options into
requestOpenAI2Tencent. Update the capability configuration around the Tencent
entry in SupportStreamOptions to apply only to TokenHub or the appropriate
supported key/format, while preserving stream options for TokenHub requests.

Source: Coding guidelines

}

func GenRelayInfoWs(c *gin.Context, ws *websocket.Conn) *RelayInfo {
Expand Down
2 changes: 1 addition & 1 deletion relay/relay_adaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func GetAdaptor(apiType int) channel.Adaptor {
case constant.APITypePaLM:
return &palm.Adaptor{}
case constant.APITypeTencent:
return &tencent.Adaptor{}
return &tencent.DispatchAdaptor{}
case constant.APITypeXunfei:
return &xunfei.Adaptor{}
case constant.APITypeZhipu:
Expand Down
2 changes: 1 addition & 1 deletion web/src/features/channels/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ export const TYPE_TO_KEY_PROMPT: Record<number, string> = {
15: 'Format: APIKey|SecretKey',
18: 'Format: APPID|APISecret|APIKey',
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',
50: 'Format: AccessKey|SecretKey (or just ApiKey if upstream is New API)',
51: 'Format: Access Key ID|Secret Access Key',
Expand Down
2 changes: 1 addition & 1 deletion web/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "Format: APIKey|SecretKey",
"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.",
"Frames per second": "Frames per second",
"Free": "Free",
Expand Down
2 changes: 1 addition & 1 deletion web/src/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Format : APIKey-AppId, par ex. fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "Format : APIKey|SecretKey",
"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.",
"Frames per second": "Images par seconde",
"Free": "Libre",
Expand Down
2 changes: 1 addition & 1 deletion web/src/i18n/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "形式: APIKey-AppId、例: fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "形式: APIKey|SecretKey",
"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.": "ポストプロセスなしで、リクエストをアップストリームプロバイダーに直接転送します。",
"Frames per second": "フレームレート",
"Free": "空き",
Expand Down
2 changes: 1 addition & 1 deletion web/src/i18n/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "Формат: APIKey-AppId, напр., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "Формат: APIKey|SecretKey",
"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-провайдерам без какой-либо постобработки.",
"Frames per second": "Кадров в секунду",
"Free": "Свободно",
Expand Down
2 changes: 1 addition & 1 deletion web/src/i18n/locales/vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,7 @@
"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: 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.",
"Frames per second": "Khung hình / giây",
"Free": "Trống",
Expand Down
2 changes: 1 addition & 1 deletion web/src/i18n/locales/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "格式:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "格式:APIKey|SecretKey",
"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.": "將請求直接轉發給上游供應商,不進行任何後處理。",
"Frames per second": "幀率",
"Free": "可用",
Expand Down
2 changes: 1 addition & 1 deletion web/src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,7 @@
"Format: APIKey-AppId, e.g., fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041": "格式:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
"Format: APIKey|SecretKey": "格式:APIKey|SecretKey",
"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.": "将请求直接转发给上游提供商,不进行任何后处理。",
"Frames per second": "帧率",
"Free": "可用",
Expand Down