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
1ba2cb0b
authored
Sep 28, 2025
by
Calcium-Ion
Committed by
GitHub
Sep 28, 2025
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1897 from QuantumNous/openrouter-enterprise
feat: 添加 openrouter-enterprise 支持
parents
11ce2a6d
6f8edd49
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
76 additions
and
0 deletions
+76
-0
dto/channel_settings.go
+8
-0
relay/channel/api_request.go
+1
-0
relay/channel/openai/relay-openai.go
+18
-0
relay/channel/openrouter/dto.go
+7
-0
web/src/components/table/channels/modals/EditChannelModal.jsx
+42
-0
No files found.
dto/channel_settings.go
View file @
1ba2cb0b
...
...
@@ -19,4 +19,12 @@ const (
type
ChannelOtherSettings
struct
{
AzureResponsesVersion
string
`json:"azure_responses_version,omitempty"`
VertexKeyType
VertexKeyType
`json:"vertex_key_type,omitempty"`
// "json" or "api_key"
OpenRouterEnterprise
*
bool
`json:"openrouter_enterprise,omitempty"`
}
func
(
s
*
ChannelOtherSettings
)
IsOpenRouterEnterprise
()
bool
{
if
s
==
nil
||
s
.
OpenRouterEnterprise
==
nil
{
return
false
}
return
*
s
.
OpenRouterEnterprise
}
relay/channel/api_request.go
View file @
1ba2cb0b
...
...
@@ -265,6 +265,7 @@ func doRequest(c *gin.Context, req *http.Request, info *common.RelayInfo) (*http
resp
,
err
:=
client
.
Do
(
req
)
if
err
!=
nil
{
logger
.
LogError
(
c
,
"do request failed: "
+
err
.
Error
())
return
nil
,
types
.
NewError
(
err
,
types
.
ErrorCodeDoRequestFailed
,
types
.
ErrOptionWithHideErrMsg
(
"upstream error: do request failed"
))
}
if
resp
==
nil
{
...
...
relay/channel/openai/relay-openai.go
View file @
1ba2cb0b
...
...
@@ -12,6 +12,7 @@ import (
"one-api/constant"
"one-api/dto"
"one-api/logger"
"one-api/relay/channel/openrouter"
relaycommon
"one-api/relay/common"
"one-api/relay/helper"
"one-api/service"
...
...
@@ -185,10 +186,27 @@ func OpenaiHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respo
if
common
.
DebugEnabled
{
println
(
"upstream response body:"
,
string
(
responseBody
))
}
// Unmarshal to simpleResponse
if
info
.
ChannelType
==
constant
.
ChannelTypeOpenRouter
&&
info
.
ChannelOtherSettings
.
IsOpenRouterEnterprise
()
{
// 尝试解析为 openrouter enterprise
var
enterpriseResponse
openrouter
.
OpenRouterEnterpriseResponse
err
=
common
.
Unmarshal
(
responseBody
,
&
enterpriseResponse
)
if
err
!=
nil
{
return
nil
,
types
.
NewOpenAIError
(
err
,
types
.
ErrorCodeBadResponseBody
,
http
.
StatusInternalServerError
)
}
if
enterpriseResponse
.
Success
{
responseBody
=
enterpriseResponse
.
Data
}
else
{
logger
.
LogError
(
c
,
fmt
.
Sprintf
(
"openrouter enterprise response success=false, data: %s"
,
enterpriseResponse
.
Data
))
return
nil
,
types
.
NewOpenAIError
(
fmt
.
Errorf
(
"openrouter response success=false"
),
types
.
ErrorCodeBadResponseBody
,
http
.
StatusInternalServerError
)
}
}
err
=
common
.
Unmarshal
(
responseBody
,
&
simpleResponse
)
if
err
!=
nil
{
return
nil
,
types
.
NewOpenAIError
(
err
,
types
.
ErrorCodeBadResponseBody
,
http
.
StatusInternalServerError
)
}
if
oaiError
:=
simpleResponse
.
GetOpenAIError
();
oaiError
!=
nil
&&
oaiError
.
Type
!=
""
{
return
nil
,
types
.
WithOpenAIError
(
*
oaiError
,
resp
.
StatusCode
)
}
...
...
relay/channel/openrouter/dto.go
View file @
1ba2cb0b
package
openrouter
import
"encoding/json"
type
RequestReasoning
struct
{
// One of the following (not both):
Effort
string
`json:"effort,omitempty"`
// Can be "high", "medium", or "low" (OpenAI-style)
...
...
@@ -7,3 +9,8 @@ type RequestReasoning struct {
// Optional: Default is false. All models support this.
Exclude
bool
`json:"exclude,omitempty"`
// Set to true to exclude reasoning tokens from response
}
type
OpenRouterEnterpriseResponse
struct
{
Data
json
.
RawMessage
`json:"data"`
Success
bool
`json:"success"`
}
web/src/components/table/channels/modals/EditChannelModal.jsx
View file @
1ba2cb0b
...
...
@@ -164,6 +164,8 @@ const EditChannelModal = (props) => {
settings
:
''
,
// 仅 Vertex: 密钥格式(存入 settings.vertex_key_type)
vertex_key_type
:
'json'
,
// 企业账户设置
is_enterprise_account
:
false
,
};
const
[
batch
,
setBatch
]
=
useState
(
false
);
const
[
multiToSingle
,
setMultiToSingle
]
=
useState
(
false
);
...
...
@@ -189,6 +191,7 @@ const EditChannelModal = (props) => {
const
[
channelSearchValue
,
setChannelSearchValue
]
=
useState
(
''
);
const
[
useManualInput
,
setUseManualInput
]
=
useState
(
false
);
// 是否使用手动输入模式
const
[
keyMode
,
setKeyMode
]
=
useState
(
'append'
);
// 密钥模式:replace(覆盖)或 append(追加)
const
[
isEnterpriseAccount
,
setIsEnterpriseAccount
]
=
useState
(
false
);
// 是否为企业账户
// 2FA验证查看密钥相关状态
const
[
twoFAState
,
setTwoFAState
]
=
useState
({
...
...
@@ -437,15 +440,19 @@ const EditChannelModal = (props) => {
parsedSettings
.
azure_responses_version
||
''
;
// 读取 Vertex 密钥格式
data
.
vertex_key_type
=
parsedSettings
.
vertex_key_type
||
'json'
;
// 读取企业账户设置
data
.
is_enterprise_account
=
parsedSettings
.
openrouter_enterprise
===
true
;
}
catch
(
error
)
{
console
.
error
(
'解析其他设置失败:'
,
error
);
data
.
azure_responses_version
=
''
;
data
.
region
=
''
;
data
.
vertex_key_type
=
'json'
;
data
.
is_enterprise_account
=
false
;
}
}
else
{
// 兼容历史数据:老渠道没有 settings 时,默认按 json 展示
data
.
vertex_key_type
=
'json'
;
data
.
is_enterprise_account
=
false
;
}
setInputs
(
data
);
...
...
@@ -457,6 +464,8 @@ const EditChannelModal = (props) => {
}
else
{
setAutoBan
(
true
);
}
// 同步企业账户状态
setIsEnterpriseAccount
(
data
.
is_enterprise_account
||
false
);
setBasicModels
(
getChannelModels
(
data
.
type
));
// 同步更新channelSettings状态显示
setChannelSettings
({
...
...
@@ -716,6 +725,8 @@ const EditChannelModal = (props) => {
});
// 重置密钥模式状态
setKeyMode
(
'append'
);
// 重置企业账户状态
setIsEnterpriseAccount
(
false
);
// 清空表单中的key_mode字段
if
(
formApiRef
.
current
)
{
formApiRef
.
current
.
setValue
(
'key_mode'
,
undefined
);
...
...
@@ -879,6 +890,21 @@ const EditChannelModal = (props) => {
};
localInputs
.
setting
=
JSON
.
stringify
(
channelExtraSettings
);
// 处理type === 20的企业账户设置
if
(
localInputs
.
type
===
20
)
{
let
settings
=
{};
if
(
localInputs
.
settings
)
{
try
{
settings
=
JSON
.
parse
(
localInputs
.
settings
);
}
catch
(
error
)
{
console
.
error
(
'解析settings失败:'
,
error
);
}
}
// 设置企业账户标识,无论是true还是false都要传到后端
settings
.
openrouter_enterprise
=
localInputs
.
is_enterprise_account
===
true
;
localInputs
.
settings
=
JSON
.
stringify
(
settings
);
}
// 清理不需要发送到后端的字段
delete
localInputs
.
force_format
;
delete
localInputs
.
thinking_to_content
;
...
...
@@ -886,6 +912,7 @@ const EditChannelModal = (props) => {
delete
localInputs
.
pass_through_body_enabled
;
delete
localInputs
.
system_prompt
;
delete
localInputs
.
system_prompt_override
;
delete
localInputs
.
is_enterprise_account
;
// 顶层的 vertex_key_type 不应发送给后端
delete
localInputs
.
vertex_key_type
;
...
...
@@ -1203,6 +1230,21 @@ const EditChannelModal = (props) => {
onChange=
{
(
value
)
=>
handleInputChange
(
'type'
,
value
)
}
/>
{
inputs
.
type
===
20
&&
(
<
Form
.
Switch
field=
'is_enterprise_account'
label=
{
t
(
'是否为企业账户'
)
}
checkedText=
{
t
(
'是'
)
}
uncheckedText=
{
t
(
'否'
)
}
onChange=
{
(
value
)
=>
{
setIsEnterpriseAccount
(
value
);
handleInputChange
(
'is_enterprise_account'
,
value
);
}
}
extraText=
{
t
(
'企业账户为特殊返回格式,需要特殊处理,如果非企业账户,请勿勾选'
)
}
initValue=
{
inputs
.
is_enterprise_account
}
/>
)
}
<
Form
.
Input
field=
'name'
label=
{
t
(
'名称'
)
}
...
...
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