Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
4d6bce1d
authored
Feb 11, 2025
by
1808837298@qq.com
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: Add support for VolcEngine (Doubao) channel #313 #734
parent
e5147648
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
285 additions
and
297 deletions
+285
-297
common/constants.go
+5
-3
model/cache.go
+0
-100
relay/channel/volcengine/adaptor.go
+76
-0
relay/channel/volcengine/constants.go
+13
-0
relay/constant/api_type.go
+3
-0
relay/relay_adaptor.go
+3
-0
web/src/components/ChannelsTable.js
+2
-2
web/src/constants/channel.constants.js
+21
-32
web/src/i18n/locales/en.json
+1
-2
web/src/pages/Channel/EditChannel.js
+161
-158
No files found.
common/constants.go
View file @
4d6bce1d
...
@@ -231,8 +231,9 @@ const (
...
@@ -231,8 +231,9 @@ const (
ChannelTypeVertexAi
=
41
ChannelTypeVertexAi
=
41
ChannelTypeMistral
=
42
ChannelTypeMistral
=
42
ChannelTypeDeepSeek
=
43
ChannelTypeDeepSeek
=
43
ChannelTypeMokaAI
=
47
ChannelTypeMokaAI
=
47
ChannelTypeDummy
// this one is only for count, do not add any channel after this
ChannelTypeVolcEngine
=
48
ChannelTypeDummy
// this one is only for count, do not add any channel after this
)
)
...
@@ -281,5 +282,6 @@ var ChannelBaseURLs = []string{
...
@@ -281,5 +282,6 @@ var ChannelBaseURLs = []string{
""
,
//41
""
,
//41
"https://api.mistral.ai"
,
//42
"https://api.mistral.ai"
,
//42
"https://api.deepseek.com"
,
//43
"https://api.deepseek.com"
,
//43
"https://api.moka.ai"
,
//43
"https://api.moka.ai"
,
//43
"https://ark.cn-beijing.volces.com"
,
//44
}
}
model/cache.go
View file @
4d6bce1d
...
@@ -11,106 +11,6 @@ import (
...
@@ -11,106 +11,6 @@ import (
"time"
"time"
)
)
//func CacheGetUserGroup(id int) (group string, err error) {
// if !common.RedisEnabled {
// return GetUserGroup(id)
// }
// group, err = common.RedisGet(fmt.Sprintf("user_group:%d", id))
// if err != nil {
// group, err = GetUserGroup(id)
// if err != nil {
// return "", err
// }
// err = common.RedisSet(fmt.Sprintf("user_group:%d", id), group, time.Duration(constant.UserId2GroupCacheSeconds)*time.Second)
// if err != nil {
// common.SysError("Redis set user group error: " + err.Error())
// }
// }
// return group, err
//}
//
//func CacheGetUsername(id int) (username string, err error) {
// if !common.RedisEnabled {
// return GetUsernameById(id)
// }
// username, err = common.RedisGet(fmt.Sprintf("user_name:%d", id))
// if err != nil {
// username, err = GetUsernameById(id)
// if err != nil {
// return "", err
// }
// err = common.RedisSet(fmt.Sprintf("user_name:%d", id), username, time.Duration(constant.UserId2GroupCacheSeconds)*time.Second)
// if err != nil {
// common.SysError("Redis set user group error: " + err.Error())
// }
// }
// return username, err
//}
//
//func CacheGetUserQuota(id int) (quota int, err error) {
// if !common.RedisEnabled {
// return GetUserQuota(id)
// }
// quotaString, err := common.RedisGet(fmt.Sprintf("user_quota:%d", id))
// if err != nil {
// quota, err = GetUserQuota(id)
// if err != nil {
// return 0, err
// }
// return quota, nil
// }
// quota, err = strconv.Atoi(quotaString)
// return quota, nil
//}
//
//func CacheUpdateUserQuota(id int) error {
// if !common.RedisEnabled {
// return nil
// }
// quota, err := GetUserQuota(id)
// if err != nil {
// return err
// }
// return cacheSetUserQuota(id, quota)
//}
//
//func cacheSetUserQuota(id int, quota int) error {
// err := common.RedisSet(fmt.Sprintf("user_quota:%d", id), fmt.Sprintf("%d", quota), time.Duration(constant.UserId2QuotaCacheSeconds)*time.Second)
// return err
//}
//
//func CacheDecreaseUserQuota(id int, quota int) error {
// if !common.RedisEnabled {
// return nil
// }
// err := common.RedisDecrease(fmt.Sprintf("user_quota:%d", id), int64(quota))
// return err
//}
//
//func CacheIsUserEnabled(userId int) (bool, error) {
// if !common.RedisEnabled {
// return IsUserEnabled(userId)
// }
// enabled, err := common.RedisGet(fmt.Sprintf("user_enabled:%d", userId))
// if err == nil {
// return enabled == "1", nil
// }
//
// userEnabled, err := IsUserEnabled(userId)
// if err != nil {
// return false, err
// }
// enabled = "0"
// if userEnabled {
// enabled = "1"
// }
// err = common.RedisSet(fmt.Sprintf("user_enabled:%d", userId), enabled, time.Duration(constant.UserId2StatusCacheSeconds)*time.Second)
// if err != nil {
// common.SysError("Redis set user enabled error: " + err.Error())
// }
// return userEnabled, err
//}
var
group2model2channels
map
[
string
]
map
[
string
][]
*
Channel
var
group2model2channels
map
[
string
]
map
[
string
][]
*
Channel
var
channelsIDM
map
[
int
]
*
Channel
var
channelsIDM
map
[
int
]
*
Channel
var
channelSyncLock
sync
.
RWMutex
var
channelSyncLock
sync
.
RWMutex
...
...
relay/channel/volcengine/adaptor.go
0 → 100644
View file @
4d6bce1d
package
volcengine
import
(
"errors"
"fmt"
"github.com/gin-gonic/gin"
"io"
"net/http"
"one-api/dto"
"one-api/relay/channel"
"one-api/relay/channel/openai"
relaycommon
"one-api/relay/common"
)
type
Adaptor
struct
{
}
func
(
a
*
Adaptor
)
ConvertAudioRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
AudioRequest
)
(
io
.
Reader
,
error
)
{
//TODO implement me
return
nil
,
errors
.
New
(
"not implemented"
)
}
func
(
a
*
Adaptor
)
ConvertImageRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
ImageRequest
)
(
any
,
error
)
{
//TODO implement me
return
nil
,
errors
.
New
(
"not implemented"
)
}
func
(
a
*
Adaptor
)
Init
(
info
*
relaycommon
.
RelayInfo
)
{
}
func
(
a
*
Adaptor
)
GetRequestURL
(
info
*
relaycommon
.
RelayInfo
)
(
string
,
error
)
{
return
fmt
.
Sprintf
(
"%s/chat/completions"
,
info
.
BaseUrl
),
nil
}
func
(
a
*
Adaptor
)
SetupRequestHeader
(
c
*
gin
.
Context
,
req
*
http
.
Header
,
info
*
relaycommon
.
RelayInfo
)
error
{
channel
.
SetupApiRequestHeader
(
info
,
c
,
req
)
req
.
Set
(
"Authorization"
,
"Bearer "
+
info
.
ApiKey
)
return
nil
}
func
(
a
*
Adaptor
)
ConvertRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
*
dto
.
GeneralOpenAIRequest
)
(
any
,
error
)
{
if
request
==
nil
{
return
nil
,
errors
.
New
(
"request is nil"
)
}
return
request
,
nil
}
func
(
a
*
Adaptor
)
ConvertRerankRequest
(
c
*
gin
.
Context
,
relayMode
int
,
request
dto
.
RerankRequest
)
(
any
,
error
)
{
return
nil
,
nil
}
func
(
a
*
Adaptor
)
ConvertEmbeddingRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
request
dto
.
EmbeddingRequest
)
(
any
,
error
)
{
//TODO implement me
return
nil
,
errors
.
New
(
"not implemented"
)
}
func
(
a
*
Adaptor
)
DoRequest
(
c
*
gin
.
Context
,
info
*
relaycommon
.
RelayInfo
,
requestBody
io
.
Reader
)
(
any
,
error
)
{
return
channel
.
DoApiRequest
(
a
,
c
,
info
,
requestBody
)
}
func
(
a
*
Adaptor
)
DoResponse
(
c
*
gin
.
Context
,
resp
*
http
.
Response
,
info
*
relaycommon
.
RelayInfo
)
(
usage
any
,
err
*
dto
.
OpenAIErrorWithStatusCode
)
{
if
info
.
IsStream
{
err
,
usage
=
openai
.
OaiStreamHandler
(
c
,
resp
,
info
)
}
else
{
err
,
usage
=
openai
.
OpenaiHandler
(
c
,
resp
,
info
.
PromptTokens
,
info
.
UpstreamModelName
)
}
return
}
func
(
a
*
Adaptor
)
GetModelList
()
[]
string
{
return
ModelList
}
func
(
a
*
Adaptor
)
GetChannelName
()
string
{
return
ChannelName
}
relay/channel/volcengine/constants.go
0 → 100644
View file @
4d6bce1d
package
volcengine
var
ModelList
=
[]
string
{
"Doubao-pro-128k"
,
"Doubao-pro-32k"
,
"Doubao-pro-4k"
,
"Doubao-lite-128k"
,
"Doubao-lite-32k"
,
"Doubao-lite-4k"
,
"Doubao-embedding"
,
}
var
ChannelName
=
"volcengine"
relay/constant/api_type.go
View file @
4d6bce1d
...
@@ -28,6 +28,7 @@ const (
...
@@ -28,6 +28,7 @@ const (
APITypeMistral
APITypeMistral
APITypeDeepSeek
APITypeDeepSeek
APITypeMokaAI
APITypeMokaAI
APITypeVolcEngine
APITypeDummy
// this one is only for count, do not add any channel after this
APITypeDummy
// this one is only for count, do not add any channel after this
)
)
...
@@ -80,6 +81,8 @@ func ChannelType2APIType(channelType int) (int, bool) {
...
@@ -80,6 +81,8 @@ func ChannelType2APIType(channelType int) (int, bool) {
apiType
=
APITypeDeepSeek
apiType
=
APITypeDeepSeek
case
common
.
ChannelTypeMokaAI
:
case
common
.
ChannelTypeMokaAI
:
apiType
=
APITypeMokaAI
apiType
=
APITypeMokaAI
case
common
.
ChannelTypeVolcEngine
:
apiType
=
APITypeVolcEngine
}
}
if
apiType
==
-
1
{
if
apiType
==
-
1
{
return
APITypeOpenAI
,
false
return
APITypeOpenAI
,
false
...
...
relay/relay_adaptor.go
View file @
4d6bce1d
...
@@ -23,6 +23,7 @@ import (
...
@@ -23,6 +23,7 @@ import (
"one-api/relay/channel/task/suno"
"one-api/relay/channel/task/suno"
"one-api/relay/channel/tencent"
"one-api/relay/channel/tencent"
"one-api/relay/channel/vertex"
"one-api/relay/channel/vertex"
"one-api/relay/channel/volcengine"
"one-api/relay/channel/xunfei"
"one-api/relay/channel/xunfei"
"one-api/relay/channel/zhipu"
"one-api/relay/channel/zhipu"
"one-api/relay/channel/zhipu_4v"
"one-api/relay/channel/zhipu_4v"
...
@@ -77,6 +78,8 @@ func GetAdaptor(apiType int) channel.Adaptor {
...
@@ -77,6 +78,8 @@ func GetAdaptor(apiType int) channel.Adaptor {
return
&
deepseek
.
Adaptor
{}
return
&
deepseek
.
Adaptor
{}
case
constant
.
APITypeMokaAI
:
case
constant
.
APITypeMokaAI
:
return
&
mokaai
.
Adaptor
{}
return
&
mokaai
.
Adaptor
{}
case
constant
.
APITypeVolcEngine
:
return
&
volcengine
.
Adaptor
{}
}
}
return
nil
return
nil
}
}
...
...
web/src/components/ChannelsTable.js
View file @
4d6bce1d
...
@@ -53,11 +53,11 @@ const ChannelsTable = () => {
...
@@ -53,11 +53,11 @@ const ChannelsTable = () => {
for
(
let
i
=
0
;
i
<
CHANNEL_OPTIONS
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
CHANNEL_OPTIONS
.
length
;
i
++
)
{
type2label
[
CHANNEL_OPTIONS
[
i
].
value
]
=
CHANNEL_OPTIONS
[
i
];
type2label
[
CHANNEL_OPTIONS
[
i
].
value
]
=
CHANNEL_OPTIONS
[
i
];
}
}
type2label
[
0
]
=
{
value
:
0
,
text
:
t
(
'未知类型'
),
color
:
'grey'
};
type2label
[
0
]
=
{
value
:
0
,
label
:
t
(
'未知类型'
),
color
:
'grey'
};
}
}
return
(
return
(
<
Tag
size
=
"large"
color
=
{
type2label
[
type
]?.
color
}
>
<
Tag
size
=
"large"
color
=
{
type2label
[
type
]?.
color
}
>
{
type2label
[
type
]?.
text
}
{
type2label
[
type
]?.
label
}
<
/Tag
>
<
/Tag
>
);
);
};
};
...
...
web/src/constants/channel.constants.js
View file @
4d6bce1d
export
const
CHANNEL_OPTIONS
=
[
export
const
CHANNEL_OPTIONS
=
[
{
key
:
1
,
text
:
'OpenAI'
,
value
:
1
,
color
:
'green'
,
label
:
'OpenAI'
},
{
key
:
1
,
value
:
1
,
color
:
'green'
,
label
:
'OpenAI'
},
{
{
key
:
2
,
key
:
2
,
text
:
'Midjourney Proxy'
,
value
:
2
,
value
:
2
,
color
:
'light-blue'
,
color
:
'light-blue'
,
label
:
'Midjourney Proxy'
label
:
'Midjourney Proxy'
},
},
{
{
key
:
5
,
key
:
5
,
text
:
'Midjourney Proxy Plus'
,
value
:
5
,
value
:
5
,
color
:
'blue'
,
color
:
'blue'
,
label
:
'Midjourney Proxy Plus'
label
:
'Midjourney Proxy Plus'
},
},
{
{
key
:
36
,
key
:
36
,
text
:
'Suno API'
,
value
:
36
,
value
:
36
,
color
:
'purple'
,
color
:
'purple'
,
label
:
'Suno API'
label
:
'Suno API'
},
},
{
key
:
4
,
text
:
'Ollama'
,
value
:
4
,
color
:
'grey'
,
label
:
'Ollama'
},
{
key
:
4
,
value
:
4
,
color
:
'grey'
,
label
:
'Ollama'
},
{
{
key
:
14
,
key
:
14
,
text
:
'Anthropic Claude'
,
value
:
14
,
value
:
14
,
color
:
'indigo'
,
color
:
'indigo'
,
label
:
'Anthropic Claude'
label
:
'Anthropic Claude'
},
},
{
{
key
:
33
,
key
:
33
,
text
:
'AWS Claude'
,
value
:
33
,
value
:
33
,
color
:
'indigo'
,
color
:
'indigo'
,
label
:
'AWS Claude'
label
:
'AWS Claude'
},
},
{
key
:
41
,
text
:
'Vertex AI'
,
value
:
41
,
color
:
'blue'
,
label
:
'Vertex AI'
},
{
key
:
41
,
value
:
41
,
color
:
'blue'
,
label
:
'Vertex AI'
},
{
{
key
:
3
,
key
:
3
,
text
:
'Azure OpenAI'
,
value
:
3
,
value
:
3
,
color
:
'teal'
,
color
:
'teal'
,
label
:
'Azure OpenAI'
label
:
'Azure OpenAI'
},
},
{
{
key
:
34
,
key
:
34
,
text
:
'Cohere'
,
value
:
34
,
value
:
34
,
color
:
'purple'
,
color
:
'purple'
,
label
:
'Cohere'
label
:
'Cohere'
},
},
{
key
:
39
,
text
:
'Cloudflare'
,
value
:
39
,
color
:
'grey'
,
label
:
'Cloudflare'
},
{
key
:
39
,
value
:
39
,
color
:
'grey'
,
label
:
'Cloudflare'
},
{
key
:
43
,
text
:
'DeepSeek'
,
value
:
43
,
color
:
'blue'
,
label
:
'DeepSeek'
},
{
key
:
43
,
value
:
43
,
color
:
'blue'
,
label
:
'DeepSeek'
},
{
{
key
:
15
,
key
:
15
,
text
:
'百度文心千帆'
,
value
:
15
,
value
:
15
,
color
:
'blue'
,
color
:
'blue'
,
label
:
'百度文心千帆'
label
:
'百度文心千帆'
},
},
{
{
key
:
17
,
key
:
17
,
text
:
'阿里通义千问'
,
value
:
17
,
value
:
17
,
color
:
'orange'
,
color
:
'orange'
,
label
:
'阿里通义千问'
label
:
'阿里通义千问'
},
},
{
{
key
:
18
,
key
:
18
,
text
:
'讯飞星火认知'
,
value
:
18
,
value
:
18
,
color
:
'blue'
,
color
:
'blue'
,
label
:
'讯飞星火认知'
label
:
'讯飞星火认知'
},
},
{
{
key
:
16
,
key
:
16
,
text
:
'智谱 ChatGLM'
,
value
:
16
,
value
:
16
,
color
:
'violet'
,
color
:
'violet'
,
label
:
'智谱 ChatGLM'
label
:
'智谱 ChatGLM'
},
},
{
{
key
:
26
,
key
:
26
,
text
:
'智谱 GLM-4V'
,
value
:
26
,
value
:
26
,
color
:
'purple'
,
color
:
'purple'
,
label
:
'智谱 GLM-4V'
label
:
'智谱 GLM-4V'
},
},
{
{
key
:
24
,
key
:
24
,
text
:
'Google Gemini'
,
value
:
24
,
value
:
24
,
color
:
'orange'
,
color
:
'orange'
,
label
:
'Google Gemini'
label
:
'Google Gemini'
},
},
{
{
key
:
11
,
key
:
11
,
text
:
'Google PaLM2'
,
value
:
11
,
value
:
11
,
color
:
'orange'
,
color
:
'orange'
,
label
:
'Google PaLM2'
label
:
'Google PaLM2'
},
},
{
key
:
25
,
text
:
'Moonshot'
,
value
:
25
,
color
:
'green'
,
label
:
'Moonshot'
},
{
{
key
:
19
,
text
:
'360 智脑'
,
value
:
19
,
color
:
'blue'
,
label
:
'360 智脑'
},
key
:
48
,
{
key
:
23
,
text
:
'腾讯混元'
,
value
:
23
,
color
:
'teal'
,
label
:
'腾讯混元'
},
value
:
48
,
{
key
:
31
,
text
:
'零一万物'
,
value
:
31
,
color
:
'green'
,
label
:
'零一万物'
},
color
:
'blue'
,
{
key
:
35
,
text
:
'MiniMax'
,
value
:
35
,
color
:
'green'
,
label
:
'MiniMax'
},
label
:
'火山方舟(豆包)'
{
key
:
37
,
text
:
'Dify'
,
value
:
37
,
color
:
'teal'
,
label
:
'Dify'
},
},
{
key
:
38
,
text
:
'Jina'
,
value
:
38
,
color
:
'blue'
,
label
:
'Jina'
},
{
key
:
25
,
value
:
25
,
color
:
'green'
,
label
:
'Moonshot'
},
{
key
:
40
,
text
:
'SiliconCloud'
,
value
:
40
,
color
:
'purple'
,
label
:
'SiliconCloud'
},
{
key
:
19
,
value
:
19
,
color
:
'blue'
,
label
:
'360 智脑'
},
{
key
:
42
,
text
:
'Mistral AI'
,
value
:
42
,
color
:
'blue'
,
label
:
'Mistral AI'
},
{
key
:
23
,
value
:
23
,
color
:
'teal'
,
label
:
'腾讯混元'
},
{
key
:
8
,
text
:
'自定义渠道'
,
value
:
8
,
color
:
'pink'
,
label
:
'自定义渠道'
},
{
key
:
31
,
value
:
31
,
color
:
'green'
,
label
:
'零一万物'
},
{
key
:
35
,
value
:
35
,
color
:
'green'
,
label
:
'MiniMax'
},
{
key
:
37
,
value
:
37
,
color
:
'teal'
,
label
:
'Dify'
},
{
key
:
38
,
value
:
38
,
color
:
'blue'
,
label
:
'Jina'
},
{
key
:
40
,
value
:
40
,
color
:
'purple'
,
label
:
'SiliconCloud'
},
{
key
:
42
,
value
:
42
,
color
:
'blue'
,
label
:
'Mistral AI'
},
{
key
:
8
,
value
:
8
,
color
:
'pink'
,
label
:
'自定义渠道'
},
{
{
key
:
22
,
key
:
22
,
text
:
'知识库:FastGPT'
,
value
:
22
,
value
:
22
,
color
:
'blue'
,
color
:
'blue'
,
label
:
'知识库:FastGPT'
label
:
'知识库:FastGPT'
},
},
{
{
key
:
21
,
key
:
21
,
text
:
'知识库:AI Proxy'
,
value
:
21
,
value
:
21
,
color
:
'purple'
,
color
:
'purple'
,
label
:
'知识库:AI Proxy'
label
:
'知识库:AI Proxy'
},
},
{
{
key
:
47
,
key
:
47
,
text
:
'嵌入模型:MokaAI M3E'
,
value
:
47
,
value
:
47
,
color
:
'purple'
,
color
:
'purple'
,
label
:
'嵌入模型:MokaAI M3E'
label
:
'嵌入模型:MokaAI M3E'
...
...
web/src/i18n/locales/en.json
View file @
4d6bce1d
...
@@ -498,8 +498,7 @@
...
@@ -498,8 +498,7 @@
"请输入用户名"
:
"Please enter username"
,
"请输入用户名"
:
"Please enter username"
,
"请输入显示名称"
:
"Please enter display name"
,
"请输入显示名称"
:
"Please enter display name"
,
"请输入密码"
:
"Please enter password"
,
"请输入密码"
:
"Please enter password"
,
"模型部署名称必须和模型名称保持一致"
:
"The model deployment name must be consistent with the model name"
,
"注意,模型部署名称必须和模型名称保持一致"
:
"Note that the model deployment name must be consistent with the model name"
,
",因为 One API 会把请求体中的 model"
:
", because One API will take the model in the request body"
,
"请输入 AZURE_OPENAI_ENDPOINT"
:
"Please enter AZURE_OPENAI_ENDPOINT"
,
"请输入 AZURE_OPENAI_ENDPOINT"
:
"Please enter AZURE_OPENAI_ENDPOINT"
,
"请输入自定义渠道的 Base URL"
:
"Please enter the Base URL of the custom channel"
,
"请输入自定义渠道的 Base URL"
:
"Please enter the Base URL of the custom channel"
,
"Homepage URL 填"
:
"Fill in the Homepage URL"
,
"Homepage URL 填"
:
"Fill in the Homepage URL"
,
...
...
web/src/pages/Channel/EditChannel.js
View file @
4d6bce1d
...
@@ -438,13 +438,16 @@ const EditChannel = (props) => {
...
@@ -438,13 +438,16 @@ const EditChannel = (props) => {
value
=
{
inputs
.
type
}
value
=
{
inputs
.
type
}
onChange
=
{(
value
)
=>
handleInputChange
(
'type'
,
value
)}
onChange
=
{(
value
)
=>
handleInputChange
(
'type'
,
value
)}
style
=
{{
width
:
'50%'
}}
style
=
{{
width
:
'50%'
}}
filter
searchPosition
=
'dropdown'
placeholder
=
{
t
(
'请选择渠道类型'
)}
/
>
/
>
{
inputs
.
type
===
3
&&
(
{
inputs
.
type
===
3
&&
(
<>
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Banner
<
Banner
type
=
{
'warning'
}
type
=
{
'warning'
}
description
=
{
t
(
'注意,模型部署名称必须和模型名称保持一致
,因为 One API 会把请求体中的 model 参数替换为你的部署名称(模型名称中的点会被剔除)
'
)}
description
=
{
t
(
'注意,模型部署名称必须和模型名称保持一致'
)}
><
/Banner
>
><
/Banner
>
<
/div
>
<
/div
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
...
@@ -501,6 +504,19 @@ const EditChannel = (props) => {
...
@@ -501,6 +504,19 @@ const EditChannel = (props) => {
/>
/>
<
/
>
<
/
>
)}
)}
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'名称'
)}
:
<
/Typography.Text
>
<
/div
>
<
Input
required
name
=
"name"
placeholder
=
{
t
(
'请为渠道命名'
)}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'name'
,
value
);
}}
value
=
{
inputs
.
name
}
autoComplete
=
"new-password"
/>
{
inputs
.
type
!==
3
&&
inputs
.
type
!==
8
&&
inputs
.
type
!==
22
&&
inputs
.
type
!==
36
&&
(
{
inputs
.
type
!==
3
&&
inputs
.
type
!==
8
&&
inputs
.
type
!==
22
&&
inputs
.
type
!==
36
&&
(
<>
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
...
@@ -518,6 +534,77 @@ const EditChannel = (props) => {
...
@@ -518,6 +534,77 @@ const EditChannel = (props) => {
/>
/>
<
/
>
<
/
>
)}
)}
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'密钥'
)}
:
<
/Typography.Text
>
<
/div
>
{
batch
?
(
<
TextArea
label
=
{
t
(
'密钥'
)}
name
=
"key"
required
placeholder
=
{
t
(
'请输入密钥,一行一个'
)}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'key'
,
value
);
}}
value
=
{
inputs
.
key
}
style
=
{{
minHeight
:
150
,
fontFamily
:
'JetBrains Mono, Consolas'
}}
autoComplete
=
"new-password"
/>
)
:
(
<>
{
inputs
.
type
===
41
?
(
<
TextArea
label
=
{
t
(
'鉴权json'
)}
name
=
"key"
required
placeholder
=
{
'{\n'
+
' "type": "service_account",\n'
+
' "project_id": "abc-bcd-123-456",\n'
+
' "private_key_id": "123xxxxx456",\n'
+
' "private_key": "-----BEGIN PRIVATE KEY-----xxxx\n'
+
' "client_email": "xxx@developer.gserviceaccount.com",\n'
+
' "client_id": "111222333",\n'
+
' "auth_uri": "https://accounts.google.com/o/oauth2/auth",\n'
+
' "token_uri": "https://oauth2.googleapis.com/token",\n'
+
' "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",\n'
+
' "client_x509_cert_url": "https://xxxxx.gserviceaccount.com",\n'
+
' "universe_domain": "googleapis.com"\n'
+
'}'
}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'key'
,
value
);
}}
autosize
=
{{
minRows
:
10
}}
value
=
{
inputs
.
key
}
autoComplete
=
"new-password"
/>
)
:
(
<
Input
label
=
{
t
(
'密钥'
)}
name
=
"key"
required
placeholder
=
{
t
(
type2secretPrompt
(
inputs
.
type
))}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'key'
,
value
);
}}
value
=
{
inputs
.
key
}
autoComplete
=
"new-password"
/>
)}
<
/
>
)}
{
!
isEdit
&&
(
<
div
style
=
{{
marginTop
:
10
,
display
:
'flex'
}}
>
<
Space
>
<
Checkbox
checked
=
{
batch
}
label
=
{
t
(
'批量创建'
)}
name
=
"batch"
onChange
=
{()
=>
setBatch
(
!
batch
)}
/
>
<
Typography
.
Text
strong
>
{
t
(
'批量创建'
)}
<
/Typography.Text
>
<
/Space
>
<
/div
>
)}
{
inputs
.
type
===
22
&&
(
{
inputs
.
type
===
22
&&
(
<>
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
...
@@ -553,19 +640,6 @@ const EditChannel = (props) => {
...
@@ -553,19 +640,6 @@ const EditChannel = (props) => {
<
/
>
<
/
>
)}
)}
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'名称'
)}
:
<
/Typography.Text
>
<
/div
>
<
Input
required
name
=
"name"
placeholder
=
{
t
(
'请为渠道命名'
)}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'name'
,
value
);
}}
value
=
{
inputs
.
name
}
autoComplete
=
"new-password"
/>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'分组'
)}
:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
{
t
(
'分组'
)}
:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Select
<
Select
...
@@ -640,7 +714,7 @@ const EditChannel = (props) => {
...
@@ -640,7 +714,7 @@ const EditChannel = (props) => {
{
inputs
.
type
===
21
&&
(
{
inputs
.
type
===
21
&&
(
<>
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
��
识库
ID
:
<
/Typography.Text
>
<
Typography
.
Text
strong
>
知
识库
ID
:
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Input
<
Input
label
=
"知识库 ID"
label
=
"知识库 ID"
...
@@ -769,149 +843,6 @@ const EditChannel = (props) => {
...
@@ -769,149 +843,6 @@ const EditChannel = (props) => {
{
t
(
'填入模板'
)}
{
t
(
'填入模板'
)}
<
/Typography.Text
>
<
/Typography.Text
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'密钥'
)}
:
<
/Typography.Text
>
<
/div
>
{
batch
?
(
<
TextArea
label
=
{
t
(
'密钥'
)}
name
=
"key"
required
placeholder
=
{
t
(
'请输入密钥,一行一个'
)}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'key'
,
value
);
}}
value
=
{
inputs
.
key
}
style
=
{{
minHeight
:
150
,
fontFamily
:
'JetBrains Mono, Consolas'
}}
autoComplete
=
"new-password"
/>
)
:
(
<>
{
inputs
.
type
===
41
?
(
<
TextArea
label
=
{
t
(
'鉴权json'
)}
name
=
"key"
required
placeholder
=
{
'{\n'
+
' "type": "service_account",\n'
+
' "project_id": "abc-bcd-123-456",\n'
+
' "private_key_id": "123xxxxx456",\n'
+
' "private_key": "-----BEGIN PRIVATE KEY-----xxxx\n'
+
' "client_email": "xxx@developer.gserviceaccount.com",\n'
+
' "client_id": "111222333",\n'
+
' "auth_uri": "https://accounts.google.com/o/oauth2/auth",\n'
+
' "token_uri": "https://oauth2.googleapis.com/token",\n'
+
' "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",\n'
+
' "client_x509_cert_url": "https://xxxxx.gserviceaccount.com",\n'
+
' "universe_domain": "googleapis.com"\n'
+
'}'
}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'key'
,
value
);
}}
autosize
=
{{
minRows
:
10
}}
value
=
{
inputs
.
key
}
autoComplete
=
"new-password"
/>
)
:
(
<
Input
label
=
{
t
(
'密钥'
)}
name
=
"key"
required
placeholder
=
{
t
(
type2secretPrompt
(
inputs
.
type
))}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'key'
,
value
);
}}
value
=
{
inputs
.
key
}
autoComplete
=
"new-password"
/>
)}
<
/
>
)}
{
!
isEdit
&&
(
<
div
style
=
{{
marginTop
:
10
,
display
:
'flex'
}}
>
<
Space
>
<
Checkbox
checked
=
{
batch
}
label
=
{
t
(
'批量创建'
)}
name
=
"batch"
onChange
=
{()
=>
setBatch
(
!
batch
)}
/
>
<
Typography
.
Text
strong
>
{
t
(
'批量创建'
)}
<
/Typography.Text
>
<
/Space
>
<
/div
>
)}
{
inputs
.
type
===
1
&&
(
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'组织'
)}
:
<
/Typography.Text
>
<
/div
>
<
Input
label
=
{
t
(
'组织,可选,不填则为默认组织'
)}
name
=
"openai_organization"
placeholder
=
{
t
(
'请输入组织org-xxx'
)}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'openai_organization'
,
value
);
}}
value
=
{
inputs
.
openai_organization
}
/
>
<
/
>
)}
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'默认测试模型'
)}
:
<
/Typography.Text
>
<
/div
>
<
Input
name
=
"test_model"
placeholder
=
{
t
(
'不填则为模型列表第一个'
)}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'test_model'
,
value
);
}}
value
=
{
inputs
.
test_model
}
/
>
<
div
style
=
{{
marginTop
:
10
,
display
:
'flex'
}}
>
<
Space
>
<
Checkbox
name
=
"auto_ban"
checked
=
{
autoBan
}
onChange
=
{()
=>
{
setAutoBan
(
!
autoBan
);
}}
/
>
<
Typography
.
Text
strong
>
{
t
(
'是否自动禁用(仅当自动禁用开启时有效),关闭后不会自动禁用该渠道:'
)}
<
/Typography.Text
>
<
/Space
>
<
/div
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'状态码复写(仅影响本地判断,不修改返回到上游的状态码)'
)}
:
<
/Typography.Text
>
<
/div
>
<
TextArea
placeholder
=
{
t
(
'此项可选,用于复写返回的状态码,比如将claude渠道的400错误复写为500(用于重试),请勿滥用该功能,例如:'
)
+
'\n'
+
JSON
.
stringify
(
STATUS_CODE_MAPPING_EXAMPLE
,
null
,
2
)}
name
=
"status_code_mapping"
onChange
=
{(
value
)
=>
{
handleInputChange
(
'status_code_mapping'
,
value
);
}}
autosize
value
=
{
inputs
.
status_code_mapping
}
autoComplete
=
"new-password"
/>
<
Typography
.
Text
style
=
{{
color
:
'rgba(var(--semi-blue-5), 1)'
,
userSelect
:
'none'
,
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
handleInputChange
(
'status_code_mapping'
,
JSON
.
stringify
(
STATUS_CODE_MAPPING_EXAMPLE
,
null
,
2
)
);
}}
>
{
t
(
'填入模板'
)}
<
/Typography.Text
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
<
Typography
.
Text
strong
>
{
t
(
'渠道标签'
)}
{
t
(
'渠道标签'
)}
<
/Typography.Text
>
<
/Typography.Text
>
...
@@ -1014,6 +945,78 @@ const EditChannel = (props) => {
...
@@ -1014,6 +945,78 @@ const EditChannel = (props) => {
<
/Typography.Text
>
<
/Typography.Text
>
<
/Space
>
<
/Space
>
<
/
>
<
/
>
{
inputs
.
type
===
1
&&
(
<>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'组织'
)}
:
<
/Typography.Text
>
<
/div
>
<
Input
label
=
{
t
(
'组织,可选,不填则为默认组织'
)}
name
=
"openai_organization"
placeholder
=
{
t
(
'请输入组织org-xxx'
)}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'openai_organization'
,
value
);
}}
value
=
{
inputs
.
openai_organization
}
/
>
<
/
>
)}
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'默认测试模型'
)}
:
<
/Typography.Text
>
<
/div
>
<
Input
name
=
"test_model"
placeholder
=
{
t
(
'不填则为模型列表第一个'
)}
onChange
=
{(
value
)
=>
{
handleInputChange
(
'test_model'
,
value
);
}}
value
=
{
inputs
.
test_model
}
/
>
<
div
style
=
{{
marginTop
:
10
,
display
:
'flex'
}}
>
<
Space
>
<
Checkbox
name
=
"auto_ban"
checked
=
{
autoBan
}
onChange
=
{()
=>
{
setAutoBan
(
!
autoBan
);
}}
/
>
<
Typography
.
Text
strong
>
{
t
(
'是否自动禁用(仅当自动禁用开启时有效),关闭后不会自动禁用该渠道:'
)}
<
/Typography.Text
>
<
/Space
>
<
/div
>
<
div
style
=
{{
marginTop
:
10
}}
>
<
Typography
.
Text
strong
>
{
t
(
'状态码复写(仅影响本地判断,不修改返回到上游的状态码)'
)}
:
<
/Typography.Text
>
<
/div
>
<
TextArea
placeholder
=
{
t
(
'此项可选,用于复写返回的状态码,比如将claude渠道的400错误复写为500(用于重试),请勿滥用该功能,例如:'
)
+
'\n'
+
JSON
.
stringify
(
STATUS_CODE_MAPPING_EXAMPLE
,
null
,
2
)}
name
=
"status_code_mapping"
onChange
=
{(
value
)
=>
{
handleInputChange
(
'status_code_mapping'
,
value
);
}}
autosize
value
=
{
inputs
.
status_code_mapping
}
autoComplete
=
"new-password"
/>
<
Typography
.
Text
style
=
{{
color
:
'rgba(var(--semi-blue-5), 1)'
,
userSelect
:
'none'
,
cursor
:
'pointer'
}}
onClick
=
{()
=>
{
handleInputChange
(
'status_code_mapping'
,
JSON
.
stringify
(
STATUS_CODE_MAPPING_EXAMPLE
,
null
,
2
)
);
}}
>
{
t
(
'填入模板'
)}
<
/Typography.Text
>
<
/Spin
>
<
/Spin
>
<
/SideSheet
>
<
/SideSheet
>
<
/
>
<
/
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment