Merge pull request #1268 from QuantumNous/alpha

fix: gemini relay empty response
This commit is contained in:
Calcium-Ion
2025-06-20 02:31:11 +08:00
committed by GitHub
6 changed files with 264 additions and 119 deletions

View File

@@ -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) {
} }
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
} }

View File

@@ -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)

View File

@@ -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) {
} }
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()

View File

@@ -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,

View File

@@ -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
} }

View File

@@ -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 = () => {
/> />
</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 = () => {
/> />
</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>