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
0b245ff4
authored
Jun 20, 2025
by
Calcium-Ion
Committed by
GitHub
Jun 20, 2025
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1268 from QuantumNous/alpha
fix: gemini relay empty response
parents
05aaf633
2b2e0a47
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
264 additions
and
119 deletions
+264
-119
controller/channel.go
+19
-9
model/main.go
+9
-0
model/utils.go
+19
-2
relay/channel/gemini/relay-gemini-native.go
+0
-13
relay/relay-gemini.go
+15
-0
web/src/pages/TopUp/index.js
+202
-95
No files found.
controller/channel.go
View file @
0b245ff4
...
@@ -102,14 +102,14 @@ func GetAllChannels(c *gin.Context) {
...
@@ -102,14 +102,14 @@ func GetAllChannels(c *gin.Context) {
typeCounts
,
_
:=
model
.
CountChannelsGroupByType
()
typeCounts
,
_
:=
model
.
CountChannelsGroupByType
()
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"success"
:
true
,
"message"
:
""
,
"message"
:
""
,
"data"
:
gin
.
H
{
"data"
:
gin
.
H
{
"items"
:
channelData
,
"items"
:
channelData
,
"total"
:
total
,
"total"
:
total
,
"page"
:
p
,
"page"
:
p
,
"page_size"
:
pageSize
,
"page_size"
:
pageSize
,
"type_counts"
:
typeCounts
,
"type_counts"
:
typeCounts
,
},
},
})
})
return
return
...
@@ -237,10 +237,20 @@ func SearchChannels(c *gin.Context) {
...
@@ -237,10 +237,20 @@ func SearchChannels(c *gin.Context) {
}
}
channelData
=
channels
channelData
=
channels
}
}
// calculate type counts for search results
typeCounts
:=
make
(
map
[
int64
]
int64
)
for
_
,
channel
:=
range
channelData
{
typeCounts
[
int64
(
channel
.
Type
)]
++
}
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"success"
:
true
,
"message"
:
""
,
"message"
:
""
,
"data"
:
channelData
,
"data"
:
gin
.
H
{
"items"
:
channelData
,
"type_counts"
:
typeCounts
,
},
})
})
return
return
}
}
...
...
model/main.go
View file @
0b245ff4
...
@@ -46,6 +46,15 @@ func initCol() {
...
@@ -46,6 +46,15 @@ func initCol() {
logGroupCol
=
commonGroupCol
logGroupCol
=
commonGroupCol
logKeyCol
=
commonKeyCol
logKeyCol
=
commonKeyCol
}
}
}
else
{
// LOG_SQL_DSN 为空时,日志数据库与主数据库相同
if
common
.
UsingPostgreSQL
{
logGroupCol
=
`"group"`
logKeyCol
=
`"key"`
}
else
{
logGroupCol
=
commonGroupCol
logKeyCol
=
commonKeyCol
}
}
}
// log sql type and database type
// log sql type and database type
common
.
SysLog
(
"Using Log SQL Type: "
+
common
.
LogSqlType
)
common
.
SysLog
(
"Using Log SQL Type: "
+
common
.
LogSqlType
)
...
...
model/utils.go
View file @
0b245ff4
...
@@ -2,11 +2,12 @@ package model
...
@@ -2,11 +2,12 @@ package model
import
(
import
(
"errors"
"errors"
"github.com/bytedance/gopkg/util/gopool"
"gorm.io/gorm"
"one-api/common"
"one-api/common"
"sync"
"sync"
"time"
"time"
"github.com/bytedance/gopkg/util/gopool"
"gorm.io/gorm"
)
)
const
(
const
(
...
@@ -48,6 +49,22 @@ func addNewRecord(type_ int, id int, value int) {
...
@@ -48,6 +49,22 @@ func addNewRecord(type_ int, id int, value int) {
}
}
func
batchUpdate
()
{
func
batchUpdate
()
{
// check if there's any data to update
hasData
:=
false
for
i
:=
0
;
i
<
BatchUpdateTypeCount
;
i
++
{
batchUpdateLocks
[
i
]
.
Lock
()
if
len
(
batchUpdateStores
[
i
])
>
0
{
hasData
=
true
batchUpdateLocks
[
i
]
.
Unlock
()
break
}
batchUpdateLocks
[
i
]
.
Unlock
()
}
if
!
hasData
{
return
}
common
.
SysLog
(
"batch update started"
)
common
.
SysLog
(
"batch update started"
)
for
i
:=
0
;
i
<
BatchUpdateTypeCount
;
i
++
{
for
i
:=
0
;
i
<
BatchUpdateTypeCount
;
i
++
{
batchUpdateLocks
[
i
]
.
Lock
()
batchUpdateLocks
[
i
]
.
Lock
()
...
...
relay/channel/gemini/relay-gemini-native.go
View file @
0b245ff4
...
@@ -35,19 +35,6 @@ func GeminiTextGenerationHandler(c *gin.Context, resp *http.Response, info *rela
...
@@ -35,19 +35,6 @@ func GeminiTextGenerationHandler(c *gin.Context, resp *http.Response, info *rela
return
nil
,
service
.
OpenAIErrorWrapper
(
err
,
"unmarshal_response_body_failed"
,
http
.
StatusInternalServerError
)
return
nil
,
service
.
OpenAIErrorWrapper
(
err
,
"unmarshal_response_body_failed"
,
http
.
StatusInternalServerError
)
}
}
// 检查是否有候选响应
if
len
(
geminiResponse
.
Candidates
)
==
0
{
return
nil
,
&
dto
.
OpenAIErrorWithStatusCode
{
Error
:
dto
.
OpenAIError
{
Message
:
"No candidates returned"
,
Type
:
"server_error"
,
Param
:
""
,
Code
:
500
,
},
StatusCode
:
resp
.
StatusCode
,
}
}
// 计算使用量(基于 UsageMetadata)
// 计算使用量(基于 UsageMetadata)
usage
:=
dto
.
Usage
{
usage
:=
dto
.
Usage
{
PromptTokens
:
geminiResponse
.
UsageMetadata
.
PromptTokenCount
,
PromptTokens
:
geminiResponse
.
UsageMetadata
.
PromptTokenCount
,
...
...
relay/relay-gemini.go
View file @
0b245ff4
...
@@ -165,8 +165,23 @@ func GeminiHelper(c *gin.Context) (openaiErr *dto.OpenAIErrorWithStatusCode) {
...
@@ -165,8 +165,23 @@ func GeminiHelper(c *gin.Context) (openaiErr *dto.OpenAIErrorWithStatusCode) {
return
service
.
OpenAIErrorWrapperLocal
(
err
,
"do_request_failed"
,
http
.
StatusInternalServerError
)
return
service
.
OpenAIErrorWrapperLocal
(
err
,
"do_request_failed"
,
http
.
StatusInternalServerError
)
}
}
statusCodeMappingStr
:=
c
.
GetString
(
"status_code_mapping"
)
var
httpResp
*
http
.
Response
if
resp
!=
nil
{
httpResp
=
resp
.
(
*
http
.
Response
)
relayInfo
.
IsStream
=
relayInfo
.
IsStream
||
strings
.
HasPrefix
(
httpResp
.
Header
.
Get
(
"Content-Type"
),
"text/event-stream"
)
if
httpResp
.
StatusCode
!=
http
.
StatusOK
{
openaiErr
=
service
.
RelayErrorHandler
(
httpResp
,
false
)
// reset status code 重置状态码
service
.
ResetStatusCode
(
openaiErr
,
statusCodeMappingStr
)
return
openaiErr
}
}
usage
,
openaiErr
:=
adaptor
.
DoResponse
(
c
,
resp
.
(
*
http
.
Response
),
relayInfo
)
usage
,
openaiErr
:=
adaptor
.
DoResponse
(
c
,
resp
.
(
*
http
.
Response
),
relayInfo
)
if
openaiErr
!=
nil
{
if
openaiErr
!=
nil
{
service
.
ResetStatusCode
(
openaiErr
,
statusCodeMappingStr
)
return
openaiErr
return
openaiErr
}
}
...
...
web/src/pages/TopUp/index.js
View file @
0b245ff4
...
@@ -588,11 +588,10 @@ const TopUp = () => {
...
@@ -588,11 +588,10 @@ const TopUp = () => {
<
Card
<
Card
key
=
{
index
}
key
=
{
index
}
onClick
=
{()
=>
selectPresetAmount
(
preset
)}
onClick
=
{()
=>
selectPresetAmount
(
preset
)}
className
=
{
`cursor-pointer !rounded-2xl transition-all hover:shadow-md
${
className
=
{
`cursor-pointer !rounded-2xl transition-all hover:shadow-md
${
selectedPreset
===
preset
.
value
selectedPreset
===
preset
.
value
?
'border-blue-500'
?
'border-blue-500'
:
'border-gray-200 hover:border-gray-300'
:
'border-gray-200 hover:border-gray-300'
}
`
}
}
`
}
bodyStyle
=
{{
textAlign
:
'center'
}}
bodyStyle
=
{{
textAlign
:
'center'
}}
>
>
<
div
className
=
'font-medium text-lg flex items-center justify-center mb-1'
>
<
div
className
=
'font-medium text-lg flex items-center justify-center mb-1'
>
...
@@ -661,54 +660,139 @@ const TopUp = () => {
...
@@ -661,54 +660,139 @@ const TopUp = () => {
/
>
/
>
<
/div
>
<
/div
>
<
div
className
=
'grid grid-cols-1 sm:grid-cols-2 gap-4'
>
<
div
>
{
/* <Button
<
Text
strong
className
=
'block mb-3'
>
type='primary'
{
t
(
'选择支付方式'
)}
onClick={() => preTopUp('zfb')}
<
/Text
>
size='large'
{
payMethods
.
length
===
2
?
(
disabled={!enableOnlineTopUp}
<
div
className
=
'grid grid-cols-1 sm:grid-cols-2 gap-3'
>
loading={paymentLoading && payWay === 'zfb'}
{
payMethods
.
map
((
payMethod
)
=>
(
icon={<SiAlipay size={18} />}
<
Button
style={{ height: '44px' }}
key
=
{
payMethod
.
type
}
>
type
=
'primary'
<span className='ml-2'>{t('支付宝')}</span>
onClick
=
{()
=>
preTopUp
(
payMethod
.
type
)}
</Button>
size
=
'large'
<Button
disabled
=
{
!
enableOnlineTopUp
}
type='primary'
loading
=
{
paymentLoading
&&
payWay
===
payMethod
.
type
}
onClick={() => preTopUp('wx')}
icon
=
{
size='large'
payMethod
.
type
===
'zfb'
?
(
disabled={!enableOnlineTopUp}
<
SiAlipay
size
=
{
16
}
/
>
loading={paymentLoading && payWay === 'wx'}
)
:
payMethod
.
type
===
'wx'
?
(
icon={<SiWechat size={18} />}
<
SiWechat
size
=
{
16
}
/
>
style={{ height: '44px' }}
)
:
(
>
<
CreditCard
size
=
{
16
}
/
>
<span className='ml-2'>{t('微信')}</span>
)
</Button> */
}
}
{
payMethods
.
map
((
payMethod
)
=>
(
style
=
{{
<
Button
height
:
'40px'
,
key
=
{
payMethod
.
type
}
color
:
payMethod
.
color
,
type
=
'primary'
}}
onClick
=
{()
=>
preTopUp
(
payMethod
.
type
)}
className
=
'transition-all hover:shadow-md w-full'
size
=
'large'
>
disabled
=
{
!
enableOnlineTopUp
}
<
span
className
=
'ml-1'
>
{
payMethod
.
name
}
<
/span
>
loading
=
{
paymentLoading
&&
payWay
===
payMethod
.
type
}
<
/Button
>
icon
=
{
))}
payMethod
.
type
===
'zfb'
?
(
<
/div
>
<
SiAlipay
size
=
{
18
}
/
>
)
:
payMethods
.
length
===
3
?
(
)
:
payMethod
.
type
===
'wx'
?
(
<
div
className
=
'grid grid-cols-1 sm:grid-cols-3 gap-3'
>
<
SiWechat
size
=
{
18
}
/
>
{
payMethods
.
map
((
payMethod
)
=>
(
)
:
(
<
Button
<
CreditCard
size
=
{
18
}
/
>
key
=
{
payMethod
.
type
}
)
type
=
'primary'
}
onClick
=
{()
=>
preTopUp
(
payMethod
.
type
)}
style
=
{{
size
=
'large'
height
:
'44px'
,
disabled
=
{
!
enableOnlineTopUp
}
color
:
payMethod
.
color
,
loading
=
{
paymentLoading
&&
payWay
===
payMethod
.
type
}
}}
icon
=
{
>
payMethod
.
type
===
'zfb'
?
(
<
span
className
=
'ml-2'
>
{
payMethod
.
name
}
<
/span
>
<
SiAlipay
size
=
{
16
}
/
>
<
/Button
>
)
:
payMethod
.
type
===
'wx'
?
(
))}
<
SiWechat
size
=
{
16
}
/
>
)
:
(
<
CreditCard
size
=
{
16
}
/
>
)
}
style
=
{{
height
:
'40px'
,
color
:
payMethod
.
color
,
}}
className
=
'transition-all hover:shadow-md w-full'
>
<
span
className
=
'ml-1'
>
{
payMethod
.
name
}
<
/span
>
<
/Button
>
))}
<
/div
>
)
:
payMethods
.
length
>
3
?
(
<
div
className
=
'grid grid-cols-2 sm:grid-cols-4 gap-3'
>
{
payMethods
.
map
((
payMethod
)
=>
(
<
Card
key
=
{
payMethod
.
type
}
onClick
=
{()
=>
preTopUp
(
payMethod
.
type
)}
disabled
=
{
!
enableOnlineTopUp
}
className
=
{
`cursor-pointer !rounded-xl p-0 transition-all hover:shadow-md
${
paymentLoading
&&
payWay
===
payMethod
.
type
?
'border-blue-400'
:
'border-gray-200 hover:border-gray-300'
}
`
}
bodyStyle
=
{{
padding
:
'10px'
,
textAlign
:
'center'
,
opacity
:
!
enableOnlineTopUp
?
0.5
:
1
}}
>
{
paymentLoading
&&
payWay
===
payMethod
.
type
?
(
<
div
className
=
'flex flex-col items-center justify-center h-full'
>
<
div
className
=
'mb-1'
>
<
div
className
=
'animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500'
><
/div
>
<
/div
>
<
div
className
=
'text-xs text-gray-500'
>
{
t
(
'处理中'
)}
<
/div
>
<
/div
>
)
:
(
<>
<
div
className
=
'flex items-center justify-center mb-1'
>
{
payMethod
.
type
===
'zfb'
?
(
<
SiAlipay
size
=
{
20
}
color
=
{
payMethod
.
color
}
/
>
)
:
payMethod
.
type
===
'wx'
?
(
<
SiWechat
size
=
{
20
}
color
=
{
payMethod
.
color
}
/
>
)
:
(
<
CreditCard
size
=
{
20
}
color
=
{
payMethod
.
color
}
/
>
)}
<
/div
>
<
div
className
=
'text-sm font-medium'
>
{
payMethod
.
name
}
<
/div
>
<
/
>
)}
<
/Card
>
))}
<
/div
>
)
:
(
<
div
className
=
'grid grid-cols-1 gap-3'
>
{
payMethods
.
map
((
payMethod
)
=>
(
<
Button
key
=
{
payMethod
.
type
}
type
=
'primary'
onClick
=
{()
=>
preTopUp
(
payMethod
.
type
)}
size
=
'large'
disabled
=
{
!
enableOnlineTopUp
}
loading
=
{
paymentLoading
&&
payWay
===
payMethod
.
type
}
icon
=
{
payMethod
.
type
===
'zfb'
?
(
<
SiAlipay
size
=
{
16
}
/
>
)
:
payMethod
.
type
===
'wx'
?
(
<
SiWechat
size
=
{
16
}
/
>
)
:
(
<
CreditCard
size
=
{
16
}
/
>
)
}
style
=
{{
height
:
'40px'
,
color
:
payMethod
.
color
,
}}
className
=
'transition-all hover:shadow-md w-full'
>
<
span
className
=
'ml-1'
>
{
payMethod
.
name
}
<
/span
>
<
/Button
>
))}
<
/div
>
)}
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/
>
<
/
>
...
@@ -941,48 +1025,71 @@ const TopUp = () => {
...
@@ -941,48 +1025,71 @@ const TopUp = () => {
/
>
/
>
<
/div
>
<
/div
>
<
div
className
=
'grid grid-cols-2 gap-4'
>
<
div
>
{
/* <Button
{
payMethods
.
length
===
2
?
(
type='primary'
<
div
className
=
'grid grid-cols-2 gap-3'
>
onClick={() => preTopUp('zfb')}
{
payMethods
.
map
((
payMethod
)
=>
(
disabled={!enableOnlineTopUp}
<
Button
loading={paymentLoading && payWay === 'zfb'}
key
=
{
payMethod
.
type
}
icon={<SiAlipay size={18} />}
type
=
'primary'
>
onClick
=
{()
=>
preTopUp
(
payMethod
.
type
)}
<span className='ml-2'>{t('支付宝')}</span>
disabled
=
{
!
enableOnlineTopUp
}
</Button>
loading
=
{
paymentLoading
&&
payWay
===
payMethod
.
type
}
<Button
icon
=
{
type='primary'
payMethod
.
type
===
'zfb'
?
(
onClick={() => preTopUp('wx')}
<
SiAlipay
size
=
{
16
}
/
>
disabled={!enableOnlineTopUp}
)
:
payMethod
.
type
===
'wx'
?
(
loading={paymentLoading && payWay === 'wx'}
<
SiWechat
size
=
{
16
}
/
>
icon={<SiWechat size={18} />}
)
:
(
>
<
CreditCard
size
=
{
16
}
/
>
<span className='ml-2'>{t('微信')}</span>
)
</Button> */
}
}
{
payMethods
.
map
((
payMethod
)
=>
(
style
=
{{
<
Button
color
:
payMethod
.
color
,
key
=
{
payMethod
.
type
}
}}
type
=
'primary'
className
=
'h-10'
onClick
=
{()
=>
preTopUp
(
payMethod
.
type
)}
>
disabled
=
{
!
enableOnlineTopUp
}
<
span
className
=
'ml-1'
>
{
payMethod
.
name
}
<
/span
>
loading
=
{
paymentLoading
&&
payWay
===
payMethod
.
type
}
<
/Button
>
icon
=
{
))}
payMethod
.
type
===
'zfb'
?
(
<
/div
>
<
SiAlipay
size
=
{
18
}
/
>
)
:
(
)
:
payMethod
.
type
===
'wx'
?
(
<
div
className
=
'grid grid-cols-4 gap-2'
>
<
SiWechat
size
=
{
18
}
/
>
{
payMethods
.
map
((
payMethod
)
=>
(
)
:
(
<
Card
<
CreditCard
size
=
{
18
}
/
>
key
=
{
payMethod
.
type
}
)
onClick
=
{()
=>
preTopUp
(
payMethod
.
type
)}
}
disabled
=
{
!
enableOnlineTopUp
}
style
=
{{
className
=
{
`cursor-pointer !rounded-xl p-0 transition-all
${
paymentLoading
&&
payWay
===
payMethod
.
type
color
:
payMethod
.
color
,
?
'border-blue-400'
}}
:
'border-gray-200'
>
}
`
}
<
span
className
=
'ml-2'
>
{
payMethod
.
name
}
<
/span
>
bodyStyle
=
{{
<
/Button
>
padding
:
'8px'
,
))}
textAlign
:
'center'
,
opacity
:
!
enableOnlineTopUp
?
0.5
:
1
}}
>
{
paymentLoading
&&
payWay
===
payMethod
.
type
?
(
<
div
className
=
'animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500 mx-auto'
><
/div
>
)
:
(
<>
<
div
className
=
'flex justify-center'
>
{
payMethod
.
type
===
'zfb'
?
(
<
SiAlipay
size
=
{
18
}
color
=
{
payMethod
.
color
}
/
>
)
:
payMethod
.
type
===
'wx'
?
(
<
SiWechat
size
=
{
18
}
color
=
{
payMethod
.
color
}
/
>
)
:
(
<
CreditCard
size
=
{
18
}
color
=
{
payMethod
.
color
}
/
>
)}
<
/div
>
<
div
className
=
'text-xs mt-1'
>
{
payMethod
.
name
}
<
/div
>
<
/
>
)}
<
/Card
>
))}
<
/div
>
)}
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
...
...
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