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
1042408f
authored
May 13, 2024
by
QuentinHsu
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'main' into refactor-settings-operation
parents
2b03ce53
699e29e2
Show whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
364 additions
and
124 deletions
+364
-124
common/constants.go
+2
-0
common/model-ratio.go
+37
-2
common/utils.go
+9
-0
controller/channel-test.go
+1
-1
controller/misc.go
+1
-1
controller/model.go
+42
-5
model/log.go
+4
-1
model/option.go
+3
-0
model/user.go
+7
-4
relay/channel/ai360/constants.go
+2
-0
relay/channel/ollama/constants.go
+3
-1
relay/channel/openai/constant.go
+1
-1
relay/constant/api_type.go
+11
-1
relay/relay-audio.go
+4
-1
relay/relay-image.go
+4
-1
relay/relay-mj.go
+8
-2
relay/relay-text.go
+6
-1
router/api-router.go
+1
-0
service/channel.go
+4
-1
web/src/components/ChannelsTable.js
+10
-6
web/src/components/LogsTable.js
+36
-2
web/src/components/MjLogsTable.js
+34
-0
web/src/components/OperationSetting.js
+23
-0
web/src/components/utils.js
+29
-0
web/src/constants/channel.constants.js
+2
-2
web/src/helpers/render.js
+26
-0
web/src/pages/Channel/EditChannel.js
+13
-90
web/src/pages/User/EditUser.js
+41
-1
No files found.
common/constants.go
View file @
1042408f
...
@@ -208,6 +208,8 @@ const (
...
@@ -208,6 +208,8 @@ const (
ChannelTypeLingYiWanWu
=
31
ChannelTypeLingYiWanWu
=
31
ChannelTypeAws
=
33
ChannelTypeAws
=
33
ChannelTypeCohere
=
34
ChannelTypeCohere
=
34
ChannelTypeDummy
// this one is only for count, do not add any channel after this
)
)
var
ChannelBaseURLs
=
[]
string
{
var
ChannelBaseURLs
=
[]
string
{
...
...
common/model-ratio.go
View file @
1042408f
...
@@ -28,6 +28,7 @@ var DefaultModelRatio = map[string]float64{
...
@@ -28,6 +28,7 @@ var DefaultModelRatio = map[string]float64{
"gpt-4-vision-preview"
:
5
,
// $0.01 / 1K tokens
"gpt-4-vision-preview"
:
5
,
// $0.01 / 1K tokens
"gpt-4-1106-vision-preview"
:
5
,
// $0.01 / 1K tokens
"gpt-4-1106-vision-preview"
:
5
,
// $0.01 / 1K tokens
"gpt-4-turbo"
:
5
,
// $0.01 / 1K tokens
"gpt-4-turbo"
:
5
,
// $0.01 / 1K tokens
"gpt-4-turbo-2024-04-09"
:
5
,
// $0.01 / 1K tokens
"gpt-3.5-turbo"
:
0.25
,
// $0.0015 / 1K tokens
"gpt-3.5-turbo"
:
0.25
,
// $0.0015 / 1K tokens
//"gpt-3.5-turbo-0301": 0.75, //deprecated
//"gpt-3.5-turbo-0301": 0.75, //deprecated
"gpt-3.5-turbo-0613"
:
0.75
,
"gpt-3.5-turbo-0613"
:
0.75
,
...
@@ -111,6 +112,8 @@ var DefaultModelRatio = map[string]float64{
...
@@ -111,6 +112,8 @@ var DefaultModelRatio = map[string]float64{
"command-light-nightly"
:
0.5
,
"command-light-nightly"
:
0.5
,
"command-r"
:
0.25
,
"command-r"
:
0.25
,
"command-r-plus "
:
1.5
,
"command-r-plus "
:
1.5
,
"deepseek-chat"
:
0.07
,
"deepseek-coder"
:
0.07
,
}
}
var
DefaultModelPrice
=
map
[
string
]
float64
{
var
DefaultModelPrice
=
map
[
string
]
float64
{
...
@@ -135,6 +138,12 @@ var DefaultModelPrice = map[string]float64{
...
@@ -135,6 +138,12 @@ var DefaultModelPrice = map[string]float64{
var
modelPrice
map
[
string
]
float64
=
nil
var
modelPrice
map
[
string
]
float64
=
nil
var
modelRatio
map
[
string
]
float64
=
nil
var
modelRatio
map
[
string
]
float64
=
nil
var
CompletionRatio
map
[
string
]
float64
=
nil
var
DefaultCompletionRatio
=
map
[
string
]
float64
{
"gpt-4-gizmo-*"
:
2
,
"gpt-4-all"
:
2
,
}
func
ModelPrice2JSONString
()
string
{
func
ModelPrice2JSONString
()
string
{
if
modelPrice
==
nil
{
if
modelPrice
==
nil
{
modelPrice
=
DefaultModelPrice
modelPrice
=
DefaultModelPrice
...
@@ -199,6 +208,22 @@ func GetModelRatio(name string) float64 {
...
@@ -199,6 +208,22 @@ func GetModelRatio(name string) float64 {
return
ratio
return
ratio
}
}
func
CompletionRatio2JSONString
()
string
{
if
CompletionRatio
==
nil
{
CompletionRatio
=
DefaultCompletionRatio
}
jsonBytes
,
err
:=
json
.
Marshal
(
CompletionRatio
)
if
err
!=
nil
{
SysError
(
"error marshalling completion ratio: "
+
err
.
Error
())
}
return
string
(
jsonBytes
)
}
func
UpdateCompletionRatioByJSONString
(
jsonStr
string
)
error
{
CompletionRatio
=
make
(
map
[
string
]
float64
)
return
json
.
Unmarshal
([]
byte
(
jsonStr
),
&
CompletionRatio
)
}
func
GetCompletionRatio
(
name
string
)
float64
{
func
GetCompletionRatio
(
name
string
)
float64
{
if
strings
.
HasPrefix
(
name
,
"gpt-3.5"
)
{
if
strings
.
HasPrefix
(
name
,
"gpt-3.5"
)
{
if
name
==
"gpt-3.5-turbo"
||
strings
.
HasSuffix
(
name
,
"0125"
)
{
if
name
==
"gpt-3.5-turbo"
||
strings
.
HasSuffix
(
name
,
"0125"
)
{
...
@@ -211,7 +236,7 @@ func GetCompletionRatio(name string) float64 {
...
@@ -211,7 +236,7 @@ func GetCompletionRatio(name string) float64 {
}
}
return
4.0
/
3.0
return
4.0
/
3.0
}
}
if
strings
.
HasPrefix
(
name
,
"gpt-4"
)
{
if
strings
.
HasPrefix
(
name
,
"gpt-4"
)
&&
name
!=
"gpt-4-all"
&&
!
strings
.
HasPrefix
(
name
,
"gpt-4-gizmo"
)
{
if
strings
.
HasPrefix
(
name
,
"gpt-4-turbo"
)
||
strings
.
HasSuffix
(
name
,
"preview"
)
{
if
strings
.
HasPrefix
(
name
,
"gpt-4-turbo"
)
||
strings
.
HasSuffix
(
name
,
"preview"
)
{
return
3
return
3
}
}
...
@@ -240,9 +265,19 @@ func GetCompletionRatio(name string) float64 {
...
@@ -240,9 +265,19 @@ func GetCompletionRatio(name string) float64 {
return
2
return
2
}
}
}
}
if
strings
.
HasPrefix
(
name
,
"deepseek"
)
{
return
2
}
switch
name
{
switch
name
{
case
"llama2-70b-4096"
:
case
"llama2-70b-4096"
:
return
0.8
/
0.7
return
0.8
/
0.64
case
"llama3-8b-8192"
:
return
2
case
"llama3-70b-8192"
:
return
0.79
/
0.59
}
if
ratio
,
ok
:=
CompletionRatio
[
name
];
ok
{
return
ratio
}
}
return
1
return
1
}
}
common/utils.go
View file @
1042408f
package
common
package
common
import
(
import
(
"encoding/json"
"fmt"
"fmt"
"github.com/google/uuid"
"github.com/google/uuid"
"html/template"
"html/template"
...
@@ -241,3 +242,11 @@ func RandomSleep() {
...
@@ -241,3 +242,11 @@ func RandomSleep() {
// Sleep for 0-3000 ms
// Sleep for 0-3000 ms
time
.
Sleep
(
time
.
Duration
(
rand
.
Intn
(
3000
))
*
time
.
Millisecond
)
time
.
Sleep
(
time
.
Duration
(
rand
.
Intn
(
3000
))
*
time
.
Millisecond
)
}
}
func
MapToJsonStr
(
m
map
[
string
]
interface
{})
string
{
bytes
,
err
:=
json
.
Marshal
(
m
)
if
err
!=
nil
{
return
""
}
return
string
(
bytes
)
}
controller/channel-test.go
View file @
1042408f
...
@@ -208,7 +208,7 @@ func testAllChannels(notify bool) error {
...
@@ -208,7 +208,7 @@ func testAllChannels(notify bool) error {
if
isChannelEnabled
&&
service
.
ShouldDisableChannel
(
openaiErr
,
-
1
)
&&
ban
{
if
isChannelEnabled
&&
service
.
ShouldDisableChannel
(
openaiErr
,
-
1
)
&&
ban
{
service
.
DisableChannel
(
channel
.
Id
,
channel
.
Name
,
err
.
Error
())
service
.
DisableChannel
(
channel
.
Id
,
channel
.
Name
,
err
.
Error
())
}
}
if
!
isChannelEnabled
&&
service
.
ShouldEnableChannel
(
err
,
openaiErr
)
{
if
!
isChannelEnabled
&&
service
.
ShouldEnableChannel
(
err
,
openaiErr
,
channel
.
Status
)
{
service
.
EnableChannel
(
channel
.
Id
,
channel
.
Name
)
service
.
EnableChannel
(
channel
.
Id
,
channel
.
Name
)
}
}
channel
.
UpdateResponseTime
(
milliseconds
)
channel
.
UpdateResponseTime
(
milliseconds
)
...
...
controller/misc.go
View file @
1042408f
...
@@ -147,7 +147,7 @@ func SendEmailVerification(c *gin.Context) {
...
@@ -147,7 +147,7 @@ func SendEmailVerification(c *gin.Context) {
}
}
}
}
if
common
.
EmailAliasRestrictionEnabled
{
if
common
.
EmailAliasRestrictionEnabled
{
containsSpecialSymbols
:=
strings
.
Contains
(
localPart
,
"+"
)
||
strings
.
Co
unt
(
localPart
,
"."
)
>
1
containsSpecialSymbols
:=
strings
.
Contains
(
localPart
,
"+"
)
||
strings
.
Co
ntains
(
localPart
,
"."
)
if
containsSpecialSymbols
{
if
containsSpecialSymbols
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"success"
:
false
,
...
...
controller/model.go
View file @
1042408f
...
@@ -4,13 +4,15 @@ import (
...
@@ -4,13 +4,15 @@ import (
"fmt"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
"net/http"
"net/http"
"one-api/common"
"one-api/constant"
"one-api/constant"
"one-api/dto"
"one-api/dto"
"one-api/model"
"one-api/model"
"one-api/relay"
"one-api/relay"
"one-api/relay/channel/ai360"
"one-api/relay/channel/ai360"
"one-api/relay/channel/moonshot"
"one-api/relay/channel/lingyiwanwu"
"one-api/relay/channel/lingyiwanwu"
"one-api/relay/channel/moonshot"
relaycommon
"one-api/relay/common"
relayconstant
"one-api/relay/constant"
relayconstant
"one-api/relay/constant"
)
)
...
@@ -43,8 +45,9 @@ type OpenAIModels struct {
...
@@ -43,8 +45,9 @@ type OpenAIModels struct {
var
openAIModels
[]
OpenAIModels
var
openAIModels
[]
OpenAIModels
var
openAIModelsMap
map
[
string
]
OpenAIModels
var
openAIModelsMap
map
[
string
]
OpenAIModels
var
channelId2Models
map
[
int
][]
string
func
init
()
{
func
getPermission
()
[]
OpenAIModelPermission
{
var
permission
[]
OpenAIModelPermission
var
permission
[]
OpenAIModelPermission
permission
=
append
(
permission
,
OpenAIModelPermission
{
permission
=
append
(
permission
,
OpenAIModelPermission
{
Id
:
"modelperm-LwHkVFn8AcMItP432fKKDIKJ"
,
Id
:
"modelperm-LwHkVFn8AcMItP432fKKDIKJ"
,
...
@@ -60,7 +63,12 @@ func init() {
...
@@ -60,7 +63,12 @@ func init() {
Group
:
nil
,
Group
:
nil
,
IsBlocking
:
false
,
IsBlocking
:
false
,
})
})
return
permission
}
func
init
()
{
// https://platform.openai.com/docs/models/model-endpoint-compatibility
// https://platform.openai.com/docs/models/model-endpoint-compatibility
permission
:=
getPermission
()
for
i
:=
0
;
i
<
relayconstant
.
APITypeDummy
;
i
++
{
for
i
:=
0
;
i
<
relayconstant
.
APITypeDummy
;
i
++
{
if
i
==
relayconstant
.
APITypeAIProxyLibrary
{
if
i
==
relayconstant
.
APITypeAIProxyLibrary
{
continue
continue
...
@@ -85,7 +93,7 @@ func init() {
...
@@ -85,7 +93,7 @@ func init() {
Id
:
modelName
,
Id
:
modelName
,
Object
:
"model"
,
Object
:
"model"
,
Created
:
1626777600
,
Created
:
1626777600
,
OwnedBy
:
"360"
,
OwnedBy
:
ai360
.
ChannelName
,
Permission
:
permission
,
Permission
:
permission
,
Root
:
modelName
,
Root
:
modelName
,
Parent
:
nil
,
Parent
:
nil
,
...
@@ -128,6 +136,17 @@ func init() {
...
@@ -128,6 +136,17 @@ func init() {
for
_
,
model
:=
range
openAIModels
{
for
_
,
model
:=
range
openAIModels
{
openAIModelsMap
[
model
.
Id
]
=
model
openAIModelsMap
[
model
.
Id
]
=
model
}
}
channelId2Models
=
make
(
map
[
int
][]
string
)
for
i
:=
1
;
i
<=
common
.
ChannelTypeDummy
;
i
++
{
apiType
:=
relayconstant
.
ChannelType2APIType
(
i
)
if
apiType
==
-
1
||
apiType
==
relayconstant
.
APITypeAIProxyLibrary
{
continue
}
meta
:=
&
relaycommon
.
RelayInfo
{
ChannelType
:
i
}
adaptor
:=
relay
.
GetAdaptor
(
apiType
)
adaptor
.
Init
(
meta
,
dto
.
GeneralOpenAIRequest
{})
channelId2Models
[
i
]
=
adaptor
.
GetModelList
()
}
}
}
func
ListModels
(
c
*
gin
.
Context
)
{
func
ListModels
(
c
*
gin
.
Context
)
{
...
@@ -142,24 +161,42 @@ func ListModels(c *gin.Context) {
...
@@ -142,24 +161,42 @@ func ListModels(c *gin.Context) {
}
}
models
:=
model
.
GetGroupModels
(
user
.
Group
)
models
:=
model
.
GetGroupModels
(
user
.
Group
)
userOpenAiModels
:=
make
([]
OpenAIModels
,
0
)
userOpenAiModels
:=
make
([]
OpenAIModels
,
0
)
permission
:=
getPermission
()
for
_
,
s
:=
range
models
{
for
_
,
s
:=
range
models
{
if
_
,
ok
:=
openAIModelsMap
[
s
];
ok
{
if
_
,
ok
:=
openAIModelsMap
[
s
];
ok
{
userOpenAiModels
=
append
(
userOpenAiModels
,
openAIModelsMap
[
s
])
userOpenAiModels
=
append
(
userOpenAiModels
,
openAIModelsMap
[
s
])
}
else
{
userOpenAiModels
=
append
(
userOpenAiModels
,
OpenAIModels
{
Id
:
s
,
Object
:
"model"
,
Created
:
1626777600
,
OwnedBy
:
"openai"
,
Permission
:
permission
,
Root
:
s
,
Parent
:
nil
,
})
}
}
}
}
c
.
JSON
(
200
,
gin
.
H
{
c
.
JSON
(
200
,
gin
.
H
{
"
object"
:
"list"
,
"
success"
:
true
,
"data"
:
userOpenAiModels
,
"data"
:
userOpenAiModels
,
})
})
}
}
func
ChannelListModels
(
c
*
gin
.
Context
)
{
func
ChannelListModels
(
c
*
gin
.
Context
)
{
c
.
JSON
(
200
,
gin
.
H
{
c
.
JSON
(
200
,
gin
.
H
{
"
object"
:
"list"
,
"
success"
:
true
,
"data"
:
openAIModels
,
"data"
:
openAIModels
,
})
})
}
}
func
DashboardListModels
(
c
*
gin
.
Context
)
{
c
.
JSON
(
200
,
gin
.
H
{
"success"
:
true
,
"data"
:
channelId2Models
,
})
}
func
RetrieveModel
(
c
*
gin
.
Context
)
{
func
RetrieveModel
(
c
*
gin
.
Context
)
{
modelId
:=
c
.
Param
(
"model"
)
modelId
:=
c
.
Param
(
"model"
)
if
model
,
ok
:=
openAIModelsMap
[
modelId
];
ok
{
if
model
,
ok
:=
openAIModelsMap
[
modelId
];
ok
{
...
...
model/log.go
View file @
1042408f
...
@@ -24,6 +24,7 @@ type Log struct {
...
@@ -24,6 +24,7 @@ type Log struct {
IsStream
bool
`json:"is_stream" gorm:"default:false"`
IsStream
bool
`json:"is_stream" gorm:"default:false"`
ChannelId
int
`json:"channel" gorm:"index"`
ChannelId
int
`json:"channel" gorm:"index"`
TokenId
int
`json:"token_id" gorm:"default:0;index"`
TokenId
int
`json:"token_id" gorm:"default:0;index"`
Other
string
`json:"other"`
}
}
const
(
const
(
...
@@ -57,12 +58,13 @@ func RecordLog(userId int, logType int, content string) {
...
@@ -57,12 +58,13 @@ func RecordLog(userId int, logType int, content string) {
}
}
}
}
func
RecordConsumeLog
(
ctx
context
.
Context
,
userId
int
,
channelId
int
,
promptTokens
int
,
completionTokens
int
,
modelName
string
,
tokenName
string
,
quota
int
,
content
string
,
tokenId
int
,
userQuota
int
,
useTimeSeconds
int
,
isStream
bool
)
{
func
RecordConsumeLog
(
ctx
context
.
Context
,
userId
int
,
channelId
int
,
promptTokens
int
,
completionTokens
int
,
modelName
string
,
tokenName
string
,
quota
int
,
content
string
,
tokenId
int
,
userQuota
int
,
useTimeSeconds
int
,
isStream
bool
,
other
map
[
string
]
interface
{}
)
{
common
.
LogInfo
(
ctx
,
fmt
.
Sprintf
(
"record consume log: userId=%d, 用户调用前余额=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s"
,
userId
,
userQuota
,
channelId
,
promptTokens
,
completionTokens
,
modelName
,
tokenName
,
quota
,
content
))
common
.
LogInfo
(
ctx
,
fmt
.
Sprintf
(
"record consume log: userId=%d, 用户调用前余额=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s"
,
userId
,
userQuota
,
channelId
,
promptTokens
,
completionTokens
,
modelName
,
tokenName
,
quota
,
content
))
if
!
common
.
LogConsumeEnabled
{
if
!
common
.
LogConsumeEnabled
{
return
return
}
}
username
,
_
:=
CacheGetUsername
(
userId
)
username
,
_
:=
CacheGetUsername
(
userId
)
otherStr
:=
common
.
MapToJsonStr
(
other
)
log
:=
&
Log
{
log
:=
&
Log
{
UserId
:
userId
,
UserId
:
userId
,
Username
:
username
,
Username
:
username
,
...
@@ -78,6 +80,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke
...
@@ -78,6 +80,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke
TokenId
:
tokenId
,
TokenId
:
tokenId
,
UseTime
:
useTimeSeconds
,
UseTime
:
useTimeSeconds
,
IsStream
:
isStream
,
IsStream
:
isStream
,
Other
:
otherStr
,
}
}
err
:=
DB
.
Create
(
log
)
.
Error
err
:=
DB
.
Create
(
log
)
.
Error
if
err
!=
nil
{
if
err
!=
nil
{
...
...
model/option.go
View file @
1042408f
...
@@ -83,6 +83,7 @@ func InitOptionMap() {
...
@@ -83,6 +83,7 @@ func InitOptionMap() {
common
.
OptionMap
[
"ModelRatio"
]
=
common
.
ModelRatio2JSONString
()
common
.
OptionMap
[
"ModelRatio"
]
=
common
.
ModelRatio2JSONString
()
common
.
OptionMap
[
"ModelPrice"
]
=
common
.
ModelPrice2JSONString
()
common
.
OptionMap
[
"ModelPrice"
]
=
common
.
ModelPrice2JSONString
()
common
.
OptionMap
[
"GroupRatio"
]
=
common
.
GroupRatio2JSONString
()
common
.
OptionMap
[
"GroupRatio"
]
=
common
.
GroupRatio2JSONString
()
common
.
OptionMap
[
"CompletionRatio"
]
=
common
.
CompletionRatio2JSONString
()
common
.
OptionMap
[
"TopUpLink"
]
=
common
.
TopUpLink
common
.
OptionMap
[
"TopUpLink"
]
=
common
.
TopUpLink
common
.
OptionMap
[
"ChatLink"
]
=
common
.
ChatLink
common
.
OptionMap
[
"ChatLink"
]
=
common
.
ChatLink
common
.
OptionMap
[
"ChatLink2"
]
=
common
.
ChatLink2
common
.
OptionMap
[
"ChatLink2"
]
=
common
.
ChatLink2
...
@@ -290,6 +291,8 @@ func updateOptionMap(key string, value string) (err error) {
...
@@ -290,6 +291,8 @@ func updateOptionMap(key string, value string) (err error) {
err
=
common
.
UpdateModelRatioByJSONString
(
value
)
err
=
common
.
UpdateModelRatioByJSONString
(
value
)
case
"GroupRatio"
:
case
"GroupRatio"
:
err
=
common
.
UpdateGroupRatioByJSONString
(
value
)
err
=
common
.
UpdateGroupRatioByJSONString
(
value
)
case
"CompletionRatio"
:
err
=
common
.
UpdateCompletionRatioByJSONString
(
value
)
case
"ModelPrice"
:
case
"ModelPrice"
:
err
=
common
.
UpdateModelPriceByJSONString
(
value
)
err
=
common
.
UpdateModelPriceByJSONString
(
value
)
case
"TopUpLink"
:
case
"TopUpLink"
:
...
...
model/user.go
View file @
1042408f
...
@@ -253,14 +253,17 @@ func (user *User) Edit(updatePassword bool) error {
...
@@ -253,14 +253,17 @@ func (user *User) Edit(updatePassword bool) error {
}
}
}
}
newUser
:=
*
user
newUser
:=
*
user
DB
.
First
(
&
user
,
user
.
Id
)
updates
:=
map
[
string
]
interface
{}{
err
=
DB
.
Model
(
user
)
.
Updates
(
map
[
string
]
interface
{}{
"username"
:
newUser
.
Username
,
"username"
:
newUser
.
Username
,
"password"
:
newUser
.
Password
,
"display_name"
:
newUser
.
DisplayName
,
"display_name"
:
newUser
.
DisplayName
,
"group"
:
newUser
.
Group
,
"group"
:
newUser
.
Group
,
"quota"
:
newUser
.
Quota
,
"quota"
:
newUser
.
Quota
,
})
.
Error
}
if
updatePassword
{
updates
[
"password"
]
=
newUser
.
Password
}
DB
.
First
(
&
user
,
user
.
Id
)
err
=
DB
.
Model
(
user
)
.
Updates
(
updates
)
.
Error
if
err
==
nil
{
if
err
==
nil
{
if
common
.
RedisEnabled
{
if
common
.
RedisEnabled
{
_
=
common
.
RedisSet
(
fmt
.
Sprintf
(
"user_group:%d"
,
user
.
Id
),
user
.
Group
,
time
.
Duration
(
UserId2GroupCacheSeconds
)
*
time
.
Second
)
_
=
common
.
RedisSet
(
fmt
.
Sprintf
(
"user_group:%d"
,
user
.
Id
),
user
.
Group
,
time
.
Duration
(
UserId2GroupCacheSeconds
)
*
time
.
Second
)
...
...
relay/channel/ai360/constants.go
View file @
1042408f
...
@@ -6,3 +6,5 @@ var ModelList = []string{
...
@@ -6,3 +6,5 @@ var ModelList = []string{
"embedding_s1_v1"
,
"embedding_s1_v1"
,
"semantic_similarity_s1_v1"
,
"semantic_similarity_s1_v1"
,
}
}
var
ChannelName
=
"ai360"
relay/channel/ollama/constants.go
View file @
1042408f
package
ollama
package
ollama
var
ModelList
[]
string
var
ModelList
=
[]
string
{
"llama3-7b"
,
}
var
ChannelName
=
"ollama"
var
ChannelName
=
"ollama"
relay/channel/openai/constant.go
View file @
1042408f
...
@@ -6,7 +6,7 @@ var ModelList = []string{
...
@@ -6,7 +6,7 @@ var ModelList = []string{
"gpt-3.5-turbo-instruct"
,
"gpt-3.5-turbo-instruct"
,
"gpt-4"
,
"gpt-4-0314"
,
"gpt-4-0613"
,
"gpt-4-1106-preview"
,
"gpt-4-0125-preview"
,
"gpt-4"
,
"gpt-4-0314"
,
"gpt-4-0613"
,
"gpt-4-1106-preview"
,
"gpt-4-0125-preview"
,
"gpt-4-32k"
,
"gpt-4-32k-0314"
,
"gpt-4-32k-0613"
,
"gpt-4-32k"
,
"gpt-4-32k-0314"
,
"gpt-4-32k-0613"
,
"gpt-4-turbo-preview"
,
"gpt-4-turbo-preview"
,
"gpt-4-turbo"
,
"gpt-4-turbo-2024-04-09"
,
"gpt-4-vision-preview"
,
"gpt-4-vision-preview"
,
"text-embedding-ada-002"
,
"text-embedding-3-small"
,
"text-embedding-3-large"
,
"text-embedding-ada-002"
,
"text-embedding-3-small"
,
"text-embedding-3-large"
,
"text-curie-001"
,
"text-babbage-001"
,
"text-ada-001"
,
"text-davinci-002"
,
"text-davinci-003"
,
"text-curie-001"
,
"text-babbage-001"
,
"text-ada-001"
,
"text-davinci-002"
,
"text-davinci-003"
,
...
...
relay/constant/api_type.go
View file @
1042408f
...
@@ -25,8 +25,18 @@ const (
...
@@ -25,8 +25,18 @@ const (
)
)
func
ChannelType2APIType
(
channelType
int
)
int
{
func
ChannelType2APIType
(
channelType
int
)
int
{
apiType
:=
APITypeOpenAI
apiType
:=
-
1
switch
channelType
{
switch
channelType
{
case
common
.
ChannelTypeOpenAI
:
apiType
=
APITypeOpenAI
case
common
.
ChannelTypeAzure
:
apiType
=
APITypeOpenAI
case
common
.
ChannelTypeMoonshot
:
apiType
=
APITypeOpenAI
case
common
.
ChannelTypeLingYiWanWu
:
apiType
=
APITypeOpenAI
case
common
.
ChannelType360
:
apiType
=
APITypeOpenAI
case
common
.
ChannelTypeAnthropic
:
case
common
.
ChannelTypeAnthropic
:
apiType
=
APITypeAnthropic
apiType
=
APITypeAnthropic
case
common
.
ChannelTypeBaidu
:
case
common
.
ChannelTypeBaidu
:
...
...
relay/relay-audio.go
View file @
1042408f
...
@@ -196,7 +196,10 @@ func AudioHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusCode {
...
@@ -196,7 +196,10 @@ func AudioHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusCode {
if
quota
!=
0
{
if
quota
!=
0
{
tokenName
:=
c
.
GetString
(
"token_name"
)
tokenName
:=
c
.
GetString
(
"token_name"
)
logContent
:=
fmt
.
Sprintf
(
"模型倍率 %.2f,分组倍率 %.2f"
,
modelRatio
,
groupRatio
)
logContent
:=
fmt
.
Sprintf
(
"模型倍率 %.2f,分组倍率 %.2f"
,
modelRatio
,
groupRatio
)
model
.
RecordConsumeLog
(
ctx
,
userId
,
channelId
,
promptTokens
,
0
,
audioRequest
.
Model
,
tokenName
,
quota
,
logContent
,
tokenId
,
userQuota
,
int
(
useTimeSeconds
),
false
)
other
:=
make
(
map
[
string
]
interface
{})
other
[
"model_ratio"
]
=
modelRatio
other
[
"group_ratio"
]
=
groupRatio
model
.
RecordConsumeLog
(
ctx
,
userId
,
channelId
,
promptTokens
,
0
,
audioRequest
.
Model
,
tokenName
,
quota
,
logContent
,
tokenId
,
userQuota
,
int
(
useTimeSeconds
),
false
,
other
)
model
.
UpdateUserUsedQuotaAndRequestCount
(
userId
,
quota
)
model
.
UpdateUserUsedQuotaAndRequestCount
(
userId
,
quota
)
channelId
:=
c
.
GetInt
(
"channel_id"
)
channelId
:=
c
.
GetInt
(
"channel_id"
)
model
.
UpdateChannelUsedQuota
(
channelId
,
quota
)
model
.
UpdateChannelUsedQuota
(
channelId
,
quota
)
...
...
relay/relay-image.go
View file @
1042408f
...
@@ -191,7 +191,10 @@ func RelayImageHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusC
...
@@ -191,7 +191,10 @@ func RelayImageHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusC
quality
=
"hd"
quality
=
"hd"
}
}
logContent
:=
fmt
.
Sprintf
(
"模型倍率 %.2f,分组倍率 %.2f, 大小 %s, 品质 %s"
,
modelRatio
,
groupRatio
,
imageRequest
.
Size
,
quality
)
logContent
:=
fmt
.
Sprintf
(
"模型倍率 %.2f,分组倍率 %.2f, 大小 %s, 品质 %s"
,
modelRatio
,
groupRatio
,
imageRequest
.
Size
,
quality
)
model
.
RecordConsumeLog
(
ctx
,
userId
,
channelId
,
0
,
0
,
imageRequest
.
Model
,
tokenName
,
quota
,
logContent
,
tokenId
,
userQuota
,
int
(
useTimeSeconds
),
false
)
other
:=
make
(
map
[
string
]
interface
{})
other
[
"model_ratio"
]
=
modelRatio
other
[
"group_ratio"
]
=
groupRatio
model
.
RecordConsumeLog
(
ctx
,
userId
,
channelId
,
0
,
0
,
imageRequest
.
Model
,
tokenName
,
quota
,
logContent
,
tokenId
,
userQuota
,
int
(
useTimeSeconds
),
false
,
other
)
model
.
UpdateUserUsedQuotaAndRequestCount
(
userId
,
quota
)
model
.
UpdateUserUsedQuotaAndRequestCount
(
userId
,
quota
)
channelId
:=
c
.
GetInt
(
"channel_id"
)
channelId
:=
c
.
GetInt
(
"channel_id"
)
model
.
UpdateChannelUsedQuota
(
channelId
,
quota
)
model
.
UpdateChannelUsedQuota
(
channelId
,
quota
)
...
...
relay/relay-mj.go
View file @
1042408f
...
@@ -202,7 +202,10 @@ func RelaySwapFace(c *gin.Context) *dto.MidjourneyResponse {
...
@@ -202,7 +202,10 @@ func RelaySwapFace(c *gin.Context) *dto.MidjourneyResponse {
if
quota
!=
0
{
if
quota
!=
0
{
tokenName
:=
c
.
GetString
(
"token_name"
)
tokenName
:=
c
.
GetString
(
"token_name"
)
logContent
:=
fmt
.
Sprintf
(
"模型固定价格 %.2f,分组倍率 %.2f,操作 %s"
,
modelPrice
,
groupRatio
,
constant
.
MjActionSwapFace
)
logContent
:=
fmt
.
Sprintf
(
"模型固定价格 %.2f,分组倍率 %.2f,操作 %s"
,
modelPrice
,
groupRatio
,
constant
.
MjActionSwapFace
)
model
.
RecordConsumeLog
(
ctx
,
userId
,
channelId
,
0
,
0
,
modelName
,
tokenName
,
quota
,
logContent
,
tokenId
,
userQuota
,
0
,
false
)
other
:=
make
(
map
[
string
]
interface
{})
other
[
"model_price"
]
=
modelPrice
other
[
"group_ratio"
]
=
groupRatio
model
.
RecordConsumeLog
(
ctx
,
userId
,
channelId
,
0
,
0
,
modelName
,
tokenName
,
quota
,
logContent
,
tokenId
,
userQuota
,
0
,
false
,
other
)
model
.
UpdateUserUsedQuotaAndRequestCount
(
userId
,
quota
)
model
.
UpdateUserUsedQuotaAndRequestCount
(
userId
,
quota
)
channelId
:=
c
.
GetInt
(
"channel_id"
)
channelId
:=
c
.
GetInt
(
"channel_id"
)
model
.
UpdateChannelUsedQuota
(
channelId
,
quota
)
model
.
UpdateChannelUsedQuota
(
channelId
,
quota
)
...
@@ -498,7 +501,10 @@ func RelayMidjourneySubmit(c *gin.Context, relayMode int) *dto.MidjourneyRespons
...
@@ -498,7 +501,10 @@ func RelayMidjourneySubmit(c *gin.Context, relayMode int) *dto.MidjourneyRespons
if
quota
!=
0
{
if
quota
!=
0
{
tokenName
:=
c
.
GetString
(
"token_name"
)
tokenName
:=
c
.
GetString
(
"token_name"
)
logContent
:=
fmt
.
Sprintf
(
"模型固定价格 %.2f,分组倍率 %.2f,操作 %s"
,
modelPrice
,
groupRatio
,
midjRequest
.
Action
)
logContent
:=
fmt
.
Sprintf
(
"模型固定价格 %.2f,分组倍率 %.2f,操作 %s"
,
modelPrice
,
groupRatio
,
midjRequest
.
Action
)
model
.
RecordConsumeLog
(
ctx
,
userId
,
channelId
,
0
,
0
,
modelName
,
tokenName
,
quota
,
logContent
,
tokenId
,
userQuota
,
0
,
false
)
other
:=
make
(
map
[
string
]
interface
{})
other
[
"model_price"
]
=
modelPrice
other
[
"group_ratio"
]
=
groupRatio
model
.
RecordConsumeLog
(
ctx
,
userId
,
channelId
,
0
,
0
,
modelName
,
tokenName
,
quota
,
logContent
,
tokenId
,
userQuota
,
0
,
false
,
other
)
model
.
UpdateUserUsedQuotaAndRequestCount
(
userId
,
quota
)
model
.
UpdateUserUsedQuotaAndRequestCount
(
userId
,
quota
)
channelId
:=
c
.
GetInt
(
"channel_id"
)
channelId
:=
c
.
GetInt
(
"channel_id"
)
model
.
UpdateChannelUsedQuota
(
channelId
,
quota
)
model
.
UpdateChannelUsedQuota
(
channelId
,
quota
)
...
...
relay/relay-text.go
View file @
1042408f
...
@@ -315,7 +315,12 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, textRe
...
@@ -315,7 +315,12 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, textRe
logModel
=
"gpt-4-gizmo-*"
logModel
=
"gpt-4-gizmo-*"
logContent
+=
fmt
.
Sprintf
(
",模型 %s"
,
textRequest
.
Model
)
logContent
+=
fmt
.
Sprintf
(
",模型 %s"
,
textRequest
.
Model
)
}
}
model
.
RecordConsumeLog
(
ctx
,
relayInfo
.
UserId
,
relayInfo
.
ChannelId
,
promptTokens
,
completionTokens
,
logModel
,
tokenName
,
quota
,
logContent
,
relayInfo
.
TokenId
,
userQuota
,
int
(
useTimeSeconds
),
relayInfo
.
IsStream
)
other
:=
make
(
map
[
string
]
interface
{})
other
[
"model_ratio"
]
=
modelRatio
other
[
"group_ratio"
]
=
groupRatio
other
[
"completion_ratio"
]
=
completionRatio
other
[
"model_price"
]
=
modelPrice
model
.
RecordConsumeLog
(
ctx
,
relayInfo
.
UserId
,
relayInfo
.
ChannelId
,
promptTokens
,
completionTokens
,
logModel
,
tokenName
,
quota
,
logContent
,
relayInfo
.
TokenId
,
userQuota
,
int
(
useTimeSeconds
),
relayInfo
.
IsStream
,
other
)
//if quota != 0 {
//if quota != 0 {
//
//
...
...
router/api-router.go
View file @
1042408f
...
@@ -14,6 +14,7 @@ func SetApiRouter(router *gin.Engine) {
...
@@ -14,6 +14,7 @@ func SetApiRouter(router *gin.Engine) {
apiRouter
.
Use
(
middleware
.
GlobalAPIRateLimit
())
apiRouter
.
Use
(
middleware
.
GlobalAPIRateLimit
())
{
{
apiRouter
.
GET
(
"/status"
,
controller
.
GetStatus
)
apiRouter
.
GET
(
"/status"
,
controller
.
GetStatus
)
apiRouter
.
GET
(
"/models"
,
middleware
.
UserAuth
(),
controller
.
DashboardListModels
)
apiRouter
.
GET
(
"/status/test"
,
middleware
.
AdminAuth
(),
controller
.
TestStatus
)
apiRouter
.
GET
(
"/status/test"
,
middleware
.
AdminAuth
(),
controller
.
TestStatus
)
apiRouter
.
GET
(
"/notice"
,
controller
.
GetNotice
)
apiRouter
.
GET
(
"/notice"
,
controller
.
GetNotice
)
apiRouter
.
GET
(
"/about"
,
controller
.
GetAbout
)
apiRouter
.
GET
(
"/about"
,
controller
.
GetAbout
)
...
...
service/channel.go
View file @
1042408f
...
@@ -63,7 +63,7 @@ func ShouldDisableChannel(err *relaymodel.OpenAIError, statusCode int) bool {
...
@@ -63,7 +63,7 @@ func ShouldDisableChannel(err *relaymodel.OpenAIError, statusCode int) bool {
return
false
return
false
}
}
func
ShouldEnableChannel
(
err
error
,
openAIErr
*
relaymodel
.
OpenAIError
)
bool
{
func
ShouldEnableChannel
(
err
error
,
openAIErr
*
relaymodel
.
OpenAIError
,
status
int
)
bool
{
if
!
common
.
AutomaticEnableChannelEnabled
{
if
!
common
.
AutomaticEnableChannelEnabled
{
return
false
return
false
}
}
...
@@ -73,5 +73,8 @@ func ShouldEnableChannel(err error, openAIErr *relaymodel.OpenAIError) bool {
...
@@ -73,5 +73,8 @@ func ShouldEnableChannel(err error, openAIErr *relaymodel.OpenAIError) bool {
if
openAIErr
!=
nil
{
if
openAIErr
!=
nil
{
return
false
return
false
}
}
if
status
!=
common
.
ChannelStatusAutoDisabled
{
return
false
}
return
true
return
true
}
}
web/src/components/ChannelsTable.js
View file @
1042408f
...
@@ -31,6 +31,7 @@ import {
...
@@ -31,6 +31,7 @@ import {
}
from
'@douyinfe/semi-ui'
;
}
from
'@douyinfe/semi-ui'
;
import
EditChannel
from
'../pages/Channel/EditChannel'
;
import
EditChannel
from
'../pages/Channel/EditChannel'
;
import
{
IconTreeTriangleDown
}
from
'@douyinfe/semi-icons'
;
import
{
IconTreeTriangleDown
}
from
'@douyinfe/semi-icons'
;
import
{
loadChannelModels
}
from
'./utils.js'
;
function
renderTimestamp
(
timestamp
)
{
function
renderTimestamp
(
timestamp
)
{
return
<>
{
timestamp2string
(
timestamp
)}
<
/>
;
return
<>
{
timestamp2string
(
timestamp
)}
<
/>
;
...
@@ -354,27 +355,29 @@ const ChannelsTable = () => {
...
@@ -354,27 +355,29 @@ const ChannelsTable = () => {
};
};
const
copySelectedChannel
=
async
(
id
)
=>
{
const
copySelectedChannel
=
async
(
id
)
=>
{
const
channelToCopy
=
channels
.
find
(
channel
=>
String
(
channel
.
id
)
===
String
(
id
));
const
channelToCopy
=
channels
.
find
(
console
.
log
(
channelToCopy
)
(
channel
)
=>
String
(
channel
.
id
)
===
String
(
id
),
);
console
.
log
(
channelToCopy
);
channelToCopy
.
name
+=
'_复制'
;
channelToCopy
.
name
+=
'_复制'
;
channelToCopy
.
created_time
=
null
;
channelToCopy
.
created_time
=
null
;
channelToCopy
.
balance
=
0
;
channelToCopy
.
balance
=
0
;
channelToCopy
.
used_quota
=
0
;
channelToCopy
.
used_quota
=
0
;
if
(
!
channelToCopy
)
{
if
(
!
channelToCopy
)
{
showError
(
"渠道未找到,请刷新页面后重试。"
);
showError
(
'渠道未找到,请刷新页面后重试。'
);
return
;
return
;
}
}
try
{
try
{
const
newChannel
=
{...
channelToCopy
,
id
:
undefined
};
const
newChannel
=
{
...
channelToCopy
,
id
:
undefined
};
const
response
=
await
API
.
post
(
'/api/channel/'
,
newChannel
);
const
response
=
await
API
.
post
(
'/api/channel/'
,
newChannel
);
if
(
response
.
data
.
success
)
{
if
(
response
.
data
.
success
)
{
showSuccess
(
"渠道复制成功"
);
showSuccess
(
'渠道复制成功'
);
await
refresh
();
await
refresh
();
}
else
{
}
else
{
showError
(
response
.
data
.
message
);
showError
(
response
.
data
.
message
);
}
}
}
catch
(
error
)
{
}
catch
(
error
)
{
showError
(
"渠道复制失败: "
+
error
.
message
);
showError
(
'渠道复制失败: '
+
error
.
message
);
}
}
};
};
...
@@ -395,6 +398,7 @@ const ChannelsTable = () => {
...
@@ -395,6 +398,7 @@ const ChannelsTable = () => {
showError
(
reason
);
showError
(
reason
);
});
});
fetchGroups
().
then
();
fetchGroups
().
then
();
loadChannelModels
().
then
();
},
[]);
},
[]);
const
manageChannel
=
async
(
id
,
action
,
record
,
value
)
=>
{
const
manageChannel
=
async
(
id
,
action
,
record
,
value
)
=>
{
...
...
web/src/components/LogsTable.js
View file @
1042408f
...
@@ -19,9 +19,15 @@ import {
...
@@ -19,9 +19,15 @@ import {
Spin
,
Spin
,
Table
,
Table
,
Tag
,
Tag
,
Tooltip
,
}
from
'@douyinfe/semi-ui'
;
}
from
'@douyinfe/semi-ui'
;
import
{
ITEMS_PER_PAGE
}
from
'../constants'
;
import
{
ITEMS_PER_PAGE
}
from
'../constants'
;
import
{
renderNumber
,
renderQuota
,
stringToColor
}
from
'../helpers/render'
;
import
{
renderModelPrice
,
renderNumber
,
renderQuota
,
stringToColor
,
}
from
'../helpers/render'
;
import
Paragraph
from
'@douyinfe/semi-ui/lib/es/typography/paragraph'
;
import
Paragraph
from
'@douyinfe/semi-ui/lib/es/typography/paragraph'
;
const
{
Header
}
=
Layout
;
const
{
Header
}
=
Layout
;
...
@@ -292,16 +298,44 @@ const LogsTable = () => {
...
@@ -292,16 +298,44 @@ const LogsTable = () => {
title
:
'详情'
,
title
:
'详情'
,
dataIndex
:
'content'
,
dataIndex
:
'content'
,
render
:
(
text
,
record
,
index
)
=>
{
render
:
(
text
,
record
,
index
)
=>
{
if
(
record
.
other
===
''
)
{
return
(
return
(
<
Paragraph
<
Paragraph
ellipsis
=
{{
ellipsis
=
{{
rows
:
2
,
rows
:
2
,
showTooltip
:
{
type
:
'popover'
,
opts
:
{
style
:
{
width
:
240
}
}
},
showTooltip
:
{
type
:
'popover'
,
opts
:
{
style
:
{
width
:
240
}
},
},
}}
style
=
{{
maxWidth
:
240
}}
>
{
text
}
<
/Paragraph
>
);
}
let
other
=
JSON
.
parse
(
record
.
other
);
let
content
=
renderModelPrice
(
other
.
model_ratio
,
other
.
model_price
,
other
.
completion_ratio
,
other
.
group_ratio
,
);
return
(
<
Tooltip
content
=
{
content
}
>
<
Paragraph
ellipsis
=
{{
rows
:
2
,
showTooltip
:
{
type
:
'popover'
,
opts
:
{
style
:
{
width
:
240
}
},
},
}}
}}
style
=
{{
maxWidth
:
240
}}
style
=
{{
maxWidth
:
240
}}
>
>
{
text
}
{
text
}
<
/Paragraph
>
<
/Paragraph
>
<
/Tooltip
>
);
);
},
},
},
},
...
...
web/src/components/MjLogsTable.js
View file @
1042408f
...
@@ -236,6 +236,31 @@ const renderTimestamp = (timestampInSeconds) => {
...
@@ -236,6 +236,31 @@ const renderTimestamp = (timestampInSeconds) => {
return
`
${
year
}
-
${
month
}
-
${
day
}
${
hours
}
:
${
minutes
}
:
${
seconds
}
`
;
// 格式化输出
return
`
${
year
}
-
${
month
}
-
${
day
}
${
hours
}
:
${
minutes
}
:
${
seconds
}
`
;
// 格式化输出
};
};
// 修改renderDuration函数以包含颜色逻辑
function
renderDuration
(
submit_time
,
finishTime
)
{
// 确保startTime和finishTime都是有效的时间戳
if
(
!
submit_time
||
!
finishTime
)
return
'N/A'
;
// 将时间戳转换为Date对象
const
start
=
new
Date
(
submit_time
);
const
finish
=
new
Date
(
finishTime
);
// 计算时间差(毫秒)
const
durationMs
=
finish
-
start
;
// 将时间差转换为秒,并保留一位小数
const
durationSec
=
(
durationMs
/
1000
).
toFixed
(
1
);
// 设置颜色:大于60秒则为红色,小于等于60秒则为绿色
const
color
=
durationSec
>
60
?
'red'
:
'green'
;
// 返回带有样式的颜色标签
return
(
<
Tag
color
=
{
color
}
size
=
"large"
>
{
durationSec
}
秒
<
/Tag
>
);
}
const
LogsTable
=
()
=>
{
const
LogsTable
=
()
=>
{
const
[
isModalOpen
,
setIsModalOpen
]
=
useState
(
false
);
const
[
isModalOpen
,
setIsModalOpen
]
=
useState
(
false
);
...
@@ -249,6 +274,15 @@ const LogsTable = () => {
...
@@ -249,6 +274,15 @@ const LogsTable = () => {
},
},
},
},
{
{
title
:
'花费时间'
,
dataIndex
:
'finish_time'
,
// 以finish_time作为dataIndex
key
:
'finish_time'
,
render
:
(
finish
,
record
)
=>
{
// 假设record.start_time是存在的,并且finish是完成时间的时间戳
return
renderDuration
(
record
.
submit_time
,
finish
);
},
},
{
title
:
'渠道'
,
title
:
'渠道'
,
dataIndex
:
'channel_id'
,
dataIndex
:
'channel_id'
,
className
:
isAdmin
()
?
'tableShow'
:
'tableHiddle'
,
className
:
isAdmin
()
?
'tableShow'
:
'tableHiddle'
,
...
...
web/src/components/OperationSetting.js
View file @
1042408f
...
@@ -29,6 +29,7 @@ const OperationSetting = () => {
...
@@ -29,6 +29,7 @@ const OperationSetting = () => {
PreConsumedQuota
:
0
,
PreConsumedQuota
:
0
,
StreamCacheQueueLength
:
0
,
StreamCacheQueueLength
:
0
,
ModelRatio
:
''
,
ModelRatio
:
''
,
CompletionRatio
:
''
,
ModelPrice
:
''
,
ModelPrice
:
''
,
GroupRatio
:
''
,
GroupRatio
:
''
,
TopUpLink
:
''
,
TopUpLink
:
''
,
...
@@ -69,6 +70,7 @@ const OperationSetting = () => {
...
@@ -69,6 +70,7 @@ const OperationSetting = () => {
if
(
if
(
item
.
key
===
'ModelRatio'
||
item
.
key
===
'ModelRatio'
||
item
.
key
===
'GroupRatio'
||
item
.
key
===
'GroupRatio'
||
item
.
key
===
'CompletionRatio'
||
item
.
key
===
'ModelPrice'
item
.
key
===
'ModelPrice'
)
{
)
{
item
.
value
=
JSON
.
stringify
(
JSON
.
parse
(
item
.
value
),
null
,
2
);
item
.
value
=
JSON
.
stringify
(
JSON
.
parse
(
item
.
value
),
null
,
2
);
...
@@ -166,6 +168,13 @@ const OperationSetting = () => {
...
@@ -166,6 +168,13 @@ const OperationSetting = () => {
}
}
await
updateOption
(
'ModelRatio'
,
inputs
.
ModelRatio
);
await
updateOption
(
'ModelRatio'
,
inputs
.
ModelRatio
);
}
}
if
(
originInputs
[
'CompletionRatio'
]
!==
inputs
.
CompletionRatio
)
{
if
(
!
verifyJSON
(
inputs
.
CompletionRatio
))
{
showError
(
'模型补全倍率不是合法的 JSON 字符串'
);
return
;
}
await
updateOption
(
'CompletionRatio'
,
inputs
.
CompletionRatio
);
}
if
(
originInputs
[
'GroupRatio'
]
!==
inputs
.
GroupRatio
)
{
if
(
originInputs
[
'GroupRatio'
]
!==
inputs
.
GroupRatio
)
{
if
(
!
verifyJSON
(
inputs
.
GroupRatio
))
{
if
(
!
verifyJSON
(
inputs
.
GroupRatio
))
{
showError
(
'分组倍率不是合法的 JSON 字符串'
);
showError
(
'分组倍率不是合法的 JSON 字符串'
);
...
@@ -306,6 +315,20 @@ const OperationSetting = () => {
...
@@ -306,6 +315,20 @@ const OperationSetting = () => {
<
/Form.Group
>
<
/Form.Group
>
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
TextArea
<
Form
.
TextArea
label
=
'模型补全倍率(仅对自定义模型有效)'
name
=
'CompletionRatio'
onChange
=
{
handleInputChange
}
style
=
{{
minHeight
:
250
,
fontFamily
:
'JetBrains Mono, Consolas'
,
}}
autoComplete
=
'new-password'
value
=
{
inputs
.
CompletionRatio
}
placeholder
=
'为一个 JSON 文本,键为分组名称,值为倍率'
/>
<
/Form.Group
>
<
Form
.
Group
widths
=
'equal'
>
<
Form
.
TextArea
label
=
'分组倍率'
label
=
'分组倍率'
name
=
'GroupRatio'
name
=
'GroupRatio'
onChange
=
{
handleInputChange
}
onChange
=
{
handleInputChange
}
...
...
web/src/components/utils.js
View file @
1042408f
...
@@ -18,3 +18,32 @@ export async function onGitHubOAuthClicked(github_client_id) {
...
@@ -18,3 +18,32 @@ export async function onGitHubOAuthClicked(github_client_id) {
`https://github.com/login/oauth/authorize?client_id=
${
github_client_id
}
&state=
${
state
}
&scope=user:email`
,
`https://github.com/login/oauth/authorize?client_id=
${
github_client_id
}
&state=
${
state
}
&scope=user:email`
,
);
);
}
}
let
channelModels
=
undefined
;
export
async
function
loadChannelModels
()
{
const
res
=
await
API
.
get
(
'/api/models'
);
const
{
success
,
data
}
=
res
.
data
;
if
(
!
success
)
{
return
;
}
channelModels
=
data
;
localStorage
.
setItem
(
'channel_models'
,
JSON
.
stringify
(
data
));
}
export
function
getChannelModels
(
type
)
{
if
(
channelModels
!==
undefined
&&
type
in
channelModels
)
{
if
(
!
channelModels
[
type
])
{
return
[];
}
return
channelModels
[
type
];
}
let
models
=
localStorage
.
getItem
(
'channel_models'
);
if
(
!
models
)
{
return
[];
}
channelModels
=
JSON
.
parse
(
models
);
if
(
type
in
channelModels
)
{
return
channelModels
[
type
];
}
return
[];
}
web/src/constants/channel.constants.js
View file @
1042408f
...
@@ -86,13 +86,13 @@ export const CHANNEL_OPTIONS = [
...
@@ -86,13 +86,13 @@ export const CHANNEL_OPTIONS = [
label
:
'智谱 ChatGLM'
,
label
:
'智谱 ChatGLM'
,
},
},
{
{
key
:
1
6
,
key
:
2
6
,
text
:
'智谱 GLM-4V'
,
text
:
'智谱 GLM-4V'
,
value
:
26
,
value
:
26
,
color
:
'purple'
,
color
:
'purple'
,
label
:
'智谱 GLM-4V'
,
label
:
'智谱 GLM-4V'
,
},
},
{
key
:
16
,
text
:
'Moonshot'
,
value
:
25
,
color
:
'green'
,
label
:
'Moonshot'
},
{
key
:
25
,
text
:
'Moonshot'
,
value
:
25
,
color
:
'green'
,
label
:
'Moonshot'
},
{
key
:
19
,
text
:
'360 智脑'
,
value
:
19
,
color
:
'blue'
,
label
:
'360 智脑'
},
{
key
:
19
,
text
:
'360 智脑'
,
value
:
19
,
color
:
'blue'
,
label
:
'360 智脑'
},
{
key
:
23
,
text
:
'腾讯混元'
,
value
:
23
,
color
:
'teal'
,
label
:
'腾讯混元'
},
{
key
:
23
,
text
:
'腾讯混元'
,
value
:
23
,
color
:
'teal'
,
label
:
'腾讯混元'
},
{
key
:
31
,
text
:
'零一万物'
,
value
:
31
,
color
:
'green'
,
label
:
'零一万物'
},
{
key
:
31
,
text
:
'零一万物'
,
value
:
31
,
color
:
'green'
,
label
:
'零一万物'
},
...
...
web/src/helpers/render.js
View file @
1042408f
...
@@ -135,6 +135,32 @@ export function renderQuota(quota, digits = 2) {
...
@@ -135,6 +135,32 @@ export function renderQuota(quota, digits = 2) {
return
renderNumber
(
quota
);
return
renderNumber
(
quota
);
}
}
export
function
renderModelPrice
(
modelRatio
,
modelPrice
=
-
1
,
completionRatio
,
groupRatio
,
)
{
// 1 ratio = $0.002 / 1K tokens
if
(
modelPrice
!==
-
1
)
{
return
'模型价格:$'
+
modelPrice
*
groupRatio
;
}
else
{
if
(
completionRatio
===
undefined
)
{
completionRatio
=
0
;
}
let
inputRatioPrice
=
modelRatio
*
0.002
*
groupRatio
;
let
completionRatioPrice
=
modelRatio
*
completionRatio
*
0.002
*
groupRatio
;
return
(
'输入:$'
+
inputRatioPrice
.
toFixed
(
3
)
+
'/1K tokens,补全:$'
+
completionRatioPrice
.
toFixed
(
3
)
+
'/1K tokens'
);
}
}
export
function
renderQuotaWithPrompt
(
quota
,
digits
)
{
export
function
renderQuotaWithPrompt
(
quota
,
digits
)
{
let
displayInCurrency
=
localStorage
.
getItem
(
'display_in_currency'
);
let
displayInCurrency
=
localStorage
.
getItem
(
'display_in_currency'
);
displayInCurrency
=
displayInCurrency
===
'true'
;
displayInCurrency
=
displayInCurrency
===
'true'
;
...
...
web/src/pages/Channel/EditChannel.js
View file @
1042408f
...
@@ -23,6 +23,7 @@ import {
...
@@ -23,6 +23,7 @@ import {
Banner
,
Banner
,
}
from
'@douyinfe/semi-ui'
;
}
from
'@douyinfe/semi-ui'
;
import
{
Divider
}
from
'semantic-ui-react'
;
import
{
Divider
}
from
'semantic-ui-react'
;
import
{
getChannelModels
,
loadChannelModels
}
from
'../../components/utils.js'
;
const
MODEL_MAPPING_EXAMPLE
=
{
const
MODEL_MAPPING_EXAMPLE
=
{
'gpt-3.5-turbo-0301'
:
'gpt-3.5-turbo'
,
'gpt-3.5-turbo-0301'
:
'gpt-3.5-turbo'
,
...
@@ -87,97 +88,9 @@ const EditChannel = (props) => {
...
@@ -87,97 +88,9 @@ const EditChannel = (props) => {
const
[
customModel
,
setCustomModel
]
=
useState
(
''
);
const
[
customModel
,
setCustomModel
]
=
useState
(
''
);
const
handleInputChange
=
(
name
,
value
)
=>
{
const
handleInputChange
=
(
name
,
value
)
=>
{
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
setInputs
((
inputs
)
=>
({
...
inputs
,
[
name
]:
value
}));
if
(
name
===
'type'
&&
inputs
.
models
.
length
===
0
)
{
if
(
name
===
'type'
)
{
let
localModels
=
[];
let
localModels
=
[];
switch
(
value
)
{
switch
(
value
)
{
case
33
:
case
14
:
localModels
=
[
'claude-instant-1.2'
,
'claude-2'
,
'claude-2.0'
,
'claude-2.1'
,
'claude-3-opus-20240229'
,
'claude-3-sonnet-20240229'
,
'claude-3-haiku-20240307'
,
];
break
;
case
11
:
localModels
=
[
'PaLM-2'
];
break
;
case
15
:
localModels
=
[
'ERNIE-Bot'
,
'ERNIE-Bot-turbo'
,
'ERNIE-Bot-4'
,
'Embedding-V1'
,
];
break
;
case
17
:
localModels
=
[
'qwen-turbo'
,
'qwen-plus'
,
'qwen-max'
,
'qwen-max-longcontext'
,
'text-embedding-v1'
,
];
break
;
case
16
:
localModels
=
[
'chatglm_pro'
,
'chatglm_std'
,
'chatglm_lite'
];
break
;
case
18
:
localModels
=
[
'SparkDesk'
,
'SparkDesk-v1.1'
,
'SparkDesk-v2.1'
,
'SparkDesk-v3.1'
,
'SparkDesk-v3.5'
,
];
break
;
case
19
:
localModels
=
[
'360GPT_S2_V9'
,
'embedding-bert-512-v1'
,
'embedding_s1_v1'
,
'semantic_similarity_s1_v1'
,
];
break
;
case
23
:
localModels
=
[
'hunyuan'
];
break
;
case
24
:
localModels
=
[
'gemini-1.0-pro-001'
,
'gemini-1.0-pro-vision-001'
,
'gemini-1.5-pro'
,
'gemini-1.5-pro-latest'
,
'gemini-pro'
,
'gemini-pro-vision'
,
];
break
;
case
34
:
localModels
=
[
'command-r'
,
'command-r-plus'
,
'command-light'
,
'command-light-nightly'
,
'command'
,
'command-nightly'
,
];
break
;
case
25
:
localModels
=
[
'moonshot-v1-8k'
,
'moonshot-v1-32k'
,
'moonshot-v1-128k'
,
];
break
;
case
26
:
localModels
=
[
'glm-4'
,
'glm-4v'
,
'glm-3-turbo'
];
break
;
case
31
:
localModels
=
[
'yi-34b-chat-0205'
,
'yi-34b-chat-200k'
,
'yi-vl-plus'
];
break
;
case
2
:
case
2
:
localModels
=
[
localModels
=
[
'mj_imagine'
,
'mj_imagine'
,
...
@@ -207,9 +120,15 @@ const EditChannel = (props) => {
...
@@ -207,9 +120,15 @@ const EditChannel = (props) => {
'mj_pan'
,
'mj_pan'
,
];
];
break
;
break
;
default
:
localModels
=
getChannelModels
(
value
);
break
;
}
}
if
(
inputs
.
models
.
length
===
0
)
{
setInputs
((
inputs
)
=>
({
...
inputs
,
models
:
localModels
}));
setInputs
((
inputs
)
=>
({
...
inputs
,
models
:
localModels
}));
}
}
setBasicModels
(
localModels
);
}
//setAutoBan
//setAutoBan
};
};
...
@@ -244,6 +163,7 @@ const EditChannel = (props) => {
...
@@ -244,6 +163,7 @@ const EditChannel = (props) => {
}
else
{
}
else
{
setAutoBan
(
true
);
setAutoBan
(
true
);
}
}
setBasicModels
(
getChannelModels
(
data
.
type
));
// console.log(data);
// console.log(data);
}
else
{
}
else
{
showError
(
message
);
showError
(
message
);
...
@@ -312,6 +232,9 @@ const EditChannel = (props) => {
...
@@ -312,6 +232,9 @@ const EditChannel = (props) => {
loadChannel
().
then
(()
=>
{});
loadChannel
().
then
(()
=>
{});
}
else
{
}
else
{
setInputs
(
originInputs
);
setInputs
(
originInputs
);
let
localModels
=
getChannelModels
(
inputs
.
type
);
setBasicModels
(
localModels
);
setInputs
((
inputs
)
=>
({
...
inputs
,
models
:
localModels
}));
}
}
},
[
props
.
editingChannel
.
id
]);
},
[
props
.
editingChannel
.
id
]);
...
@@ -596,7 +519,7 @@ const EditChannel = (props) => {
...
@@ -596,7 +519,7 @@ const EditChannel = (props) => {
handleInputChange
(
'models'
,
basicModels
);
handleInputChange
(
'models'
,
basicModels
);
}}
}}
>
>
填入
基础
模型
填入
相关
模型
<
/Button
>
<
/Button
>
<
Button
<
Button
type
=
'secondary'
type
=
'secondary'
...
...
web/src/pages/User/EditUser.js
View file @
1042408f
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
React
,
{
useEffect
,
useState
}
from
'react'
;
import
{
useNavigate
}
from
'react-router-dom'
;
import
{
useNavigate
}
from
'react-router-dom'
;
import
{
API
,
isMobile
,
showError
,
showSuccess
}
from
'../../helpers'
;
import
{
API
,
isMobile
,
showError
,
showSuccess
}
from
'../../helpers'
;
import
{
renderQuotaWithPrompt
}
from
'../../helpers/render'
;
import
{
renderQuota
,
renderQuota
WithPrompt
}
from
'../../helpers/render'
;
import
Title
from
'@douyinfe/semi-ui/lib/es/typography/title'
;
import
Title
from
'@douyinfe/semi-ui/lib/es/typography/title'
;
import
{
import
{
Button
,
Button
,
Divider
,
Divider
,
Input
,
Input
,
Modal
,
Select
,
Select
,
SideSheet
,
SideSheet
,
Space
,
Space
,
...
@@ -17,6 +18,8 @@ import {
...
@@ -17,6 +18,8 @@ import {
const
EditUser
=
(
props
)
=>
{
const
EditUser
=
(
props
)
=>
{
const
userId
=
props
.
editingUser
.
id
;
const
userId
=
props
.
editingUser
.
id
;
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
addQuotaModalOpen
,
setIsModalOpen
]
=
useState
(
false
);
const
[
addQuotaLocal
,
setAddQuotaLocal
]
=
useState
(
''
);
const
[
inputs
,
setInputs
]
=
useState
({
const
[
inputs
,
setInputs
]
=
useState
({
username
:
''
,
username
:
''
,
display_name
:
''
,
display_name
:
''
,
...
@@ -107,6 +110,16 @@ const EditUser = (props) => {
...
@@ -107,6 +110,16 @@ const EditUser = (props) => {
setLoading
(
false
);
setLoading
(
false
);
};
};
const
addLocalQuota
=
()
=>
{
let
newQuota
=
parseInt
(
quota
)
+
parseInt
(
addQuotaLocal
);
setInputs
((
inputs
)
=>
({
...
inputs
,
quota
:
newQuota
}));
};
const
openAddQuotaModal
=
()
=>
{
setAddQuotaLocal
(
'0'
);
setIsModalOpen
(
true
);
};
return
(
return
(
<>
<>
<
SideSheet
<
SideSheet
...
@@ -192,6 +205,7 @@ const EditUser = (props) => {
...
@@ -192,6 +205,7 @@ const EditUser = (props) => {
<
div
style
=
{{
marginTop
:
20
}}
>
<
div
style
=
{{
marginTop
:
20
}}
>
<
Typography
.
Text
>
{
`剩余额度
${
renderQuotaWithPrompt
(
quota
)}
`
}
<
/Typography.Text
>
<
Typography
.
Text
>
{
`剩余额度
${
renderQuotaWithPrompt
(
quota
)}
`
}
<
/Typography.Text
>
<
/div
>
<
/div
>
<
Space
>
<
Input
<
Input
name
=
'quota'
name
=
'quota'
placeholder
=
{
'请输入新的剩余额度'
}
placeholder
=
{
'请输入新的剩余额度'
}
...
@@ -200,6 +214,8 @@ const EditUser = (props) => {
...
@@ -200,6 +214,8 @@ const EditUser = (props) => {
type
=
{
'number'
}
type
=
{
'number'
}
autoComplete
=
'new-password'
autoComplete
=
'new-password'
/>
/>
<
Button
onClick
=
{
openAddQuotaModal
}
>
添加额度
<
/Button
>
<
/Space
>
<
/
>
<
/
>
)}
)}
<
Divider
style
=
{{
marginTop
:
20
}}
>
以下信息不可修改
<
/Divider
>
<
Divider
style
=
{{
marginTop
:
20
}}
>
以下信息不可修改
<
/Divider
>
...
@@ -245,6 +261,30 @@ const EditUser = (props) => {
...
@@ -245,6 +261,30 @@ const EditUser = (props) => {
/>
/>
<
/Spin
>
<
/Spin
>
<
/SideSheet
>
<
/SideSheet
>
<
Modal
centered
=
{
true
}
visible
=
{
addQuotaModalOpen
}
onOk
=
{()
=>
{
addLocalQuota
();
setIsModalOpen
(
false
);
}}
onCancel
=
{()
=>
setIsModalOpen
(
false
)}
closable
=
{
null
}
>
<
div
style
=
{{
marginTop
:
20
}}
>
<
Typography
.
Text
>
{
`新额度
${
renderQuota
(
quota
)}
+
${
renderQuota
(
addQuotaLocal
)}
=
${
renderQuota
(
quota
+
parseInt
(
addQuotaLocal
))}
`
}
<
/Typography.Text
>
<
/div
>
<
Input
name
=
'addQuotaLocal'
placeholder
=
{
'需要添加的额度(支持负数)'
}
onChange
=
{(
value
)
=>
{
setAddQuotaLocal
(
value
);
}}
value
=
{
addQuotaLocal
}
type
=
{
'number'
}
autoComplete
=
'new-password'
/>
<
/Modal
>
<
/
>
<
/
>
);
);
};
};
...
...
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