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
6682842c
authored
Sep 28, 2025
by
CaIon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: enhance OpenRouter enterprise support with new settings and response handling
parent
4bbd59c7
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
78 additions
and
3 deletions
+78
-3
dto/channel_settings.go
+8
-0
relay/channel/api_request.go
+1
-0
relay/channel/openai/relay-openai.go
+20
-3
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 @
6682842c
...
...
@@ -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 @
6682842c
...
...
@@ -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 @
6682842c
...
...
@@ -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,9 +186,25 @@ func OpenaiHandler(c *gin.Context, info *relaycommon.RelayInfo, resp *http.Respo
if
common
.
DebugEnabled
{
println
(
"upstream response body:"
,
string
(
responseBody
))
}
err
=
common
.
Unmarshal
(
responseBody
,
&
simpleResponse
)
if
err
!=
nil
{
return
nil
,
types
.
NewOpenAIError
(
err
,
types
.
ErrorCodeBadResponseBody
,
http
.
StatusInternalServerError
)
// 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
)
}
}
else
{
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 @
6682842c
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 @
6682842c
...
...
@@ -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