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
95cbab6b
authored
Sep 27, 2025
by
huanghejian
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/main' into feature/volcengine-base-url
parents
183ae15a
da88e746
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
330 additions
and
37 deletions
+330
-37
.github/workflows/linux-release.yml
+4
-4
.github/workflows/macos-release.yml
+2
-2
.github/workflows/windows-release.yml
+2
-2
common/sys_log.go
+32
-1
common/utils.go
+72
-0
controller/channel.go
+84
-2
dto/gemini.go
+31
-0
main.go
+7
-2
web/src/components/common/markdown/MarkdownRenderer.jsx
+2
-2
web/src/components/table/channels/modals/EditChannelModal.jsx
+29
-7
web/src/components/table/channels/modals/MultiKeyManageModal.jsx
+42
-1
web/src/helpers/api.js
+8
-7
web/src/helpers/utils.jsx
+11
-7
web/src/i18n/locales/en.json
+4
-0
No files found.
.github/workflows/linux-release.yml
View file @
95cbab6b
...
@@ -38,21 +38,21 @@ jobs:
...
@@ -38,21 +38,21 @@ jobs:
-
name
:
Build Backend (amd64)
-
name
:
Build Backend (amd64)
run
:
|
run
:
|
go mod download
go mod download
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o
one
-api
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o
new
-api
-
name
:
Build Backend (arm64)
-
name
:
Build Backend (arm64)
run
:
|
run
:
|
sudo apt-get update
sudo apt-get update
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y gcc-aarch64-linux-gnu
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y gcc-aarch64-linux-gnu
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o
one
-api-arm64
CC=aarch64-linux-gnu-gcc CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)' -extldflags '-static'" -o
new
-api-arm64
-
name
:
Release
-
name
:
Release
uses
:
softprops/action-gh-release@v1
uses
:
softprops/action-gh-release@v1
if
:
startsWith(github.ref, 'refs/tags/')
if
:
startsWith(github.ref, 'refs/tags/')
with
:
with
:
files
:
|
files
:
|
one
-api
new
-api
one
-api-arm64
new
-api-arm64
draft
:
true
draft
:
true
generate_release_notes
:
true
generate_release_notes
:
true
env
:
env
:
...
...
.github/workflows/macos-release.yml
View file @
95cbab6b
...
@@ -39,12 +39,12 @@ jobs:
...
@@ -39,12 +39,12 @@ jobs:
-
name
:
Build Backend
-
name
:
Build Backend
run
:
|
run
:
|
go mod download
go mod download
go build -ldflags "-X 'one-api/common.Version=$(git describe --tags)'" -o
one
-api-macos
go build -ldflags "-X 'one-api/common.Version=$(git describe --tags)'" -o
new
-api-macos
-
name
:
Release
-
name
:
Release
uses
:
softprops/action-gh-release@v1
uses
:
softprops/action-gh-release@v1
if
:
startsWith(github.ref, 'refs/tags/')
if
:
startsWith(github.ref, 'refs/tags/')
with
:
with
:
files
:
one
-api-macos
files
:
new
-api-macos
draft
:
true
draft
:
true
generate_release_notes
:
true
generate_release_notes
:
true
env
:
env
:
...
...
.github/workflows/windows-release.yml
View file @
95cbab6b
...
@@ -41,12 +41,12 @@ jobs:
...
@@ -41,12 +41,12 @@ jobs:
-
name
:
Build Backend
-
name
:
Build Backend
run
:
|
run
:
|
go mod download
go mod download
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)'" -o
one
-api.exe
go build -ldflags "-s -w -X 'one-api/common.Version=$(git describe --tags)'" -o
new
-api.exe
-
name
:
Release
-
name
:
Release
uses
:
softprops/action-gh-release@v1
uses
:
softprops/action-gh-release@v1
if
:
startsWith(github.ref, 'refs/tags/')
if
:
startsWith(github.ref, 'refs/tags/')
with
:
with
:
files
:
one
-api.exe
files
:
new
-api.exe
draft
:
true
draft
:
true
generate_release_notes
:
true
generate_release_notes
:
true
env
:
env
:
...
...
common/sys_log.go
View file @
95cbab6b
...
@@ -2,9 +2,10 @@ package common
...
@@ -2,9 +2,10 @@ package common
import
(
import
(
"fmt"
"fmt"
"github.com/gin-gonic/gin"
"os"
"os"
"time"
"time"
"github.com/gin-gonic/gin"
)
)
func
SysLog
(
s
string
)
{
func
SysLog
(
s
string
)
{
...
@@ -22,3 +23,33 @@ func FatalLog(v ...any) {
...
@@ -22,3 +23,33 @@ func FatalLog(v ...any) {
_
,
_
=
fmt
.
Fprintf
(
gin
.
DefaultErrorWriter
,
"[FATAL] %v | %v
\n
"
,
t
.
Format
(
"2006/01/02 - 15:04:05"
),
v
)
_
,
_
=
fmt
.
Fprintf
(
gin
.
DefaultErrorWriter
,
"[FATAL] %v | %v
\n
"
,
t
.
Format
(
"2006/01/02 - 15:04:05"
),
v
)
os
.
Exit
(
1
)
os
.
Exit
(
1
)
}
}
func
LogStartupSuccess
(
startTime
time
.
Time
,
port
string
)
{
duration
:=
time
.
Since
(
startTime
)
durationMs
:=
duration
.
Milliseconds
()
// Get network IPs
networkIps
:=
GetNetworkIps
()
// Print blank line for spacing
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
"
\n
"
)
// Print the main success message
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
"
\0
33[32m%s %s
\0
33[0m ready in %d ms
\n
"
,
SystemName
,
Version
,
durationMs
)
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
"
\n
"
)
// Skip fancy startup message in container environments
if
!
IsRunningInContainer
()
{
// Print local URL
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
" ➜
\0
33[1mLocal:
\0
33[0m http://localhost:%s/
\n
"
,
port
)
}
// Print network URLs
for
_
,
ip
:=
range
networkIps
{
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
" ➜
\0
33[1mNetwork:
\0
33[0m http://%s:%s/
\n
"
,
ip
,
port
)
}
// Print blank line for spacing
fmt
.
Fprintf
(
gin
.
DefaultWriter
,
"
\n
"
)
}
common/utils.go
View file @
95cbab6b
...
@@ -68,6 +68,78 @@ func GetIp() (ip string) {
...
@@ -68,6 +68,78 @@ func GetIp() (ip string) {
return
return
}
}
func
GetNetworkIps
()
[]
string
{
var
networkIps
[]
string
ips
,
err
:=
net
.
InterfaceAddrs
()
if
err
!=
nil
{
log
.
Println
(
err
)
return
networkIps
}
for
_
,
a
:=
range
ips
{
if
ipNet
,
ok
:=
a
.
(
*
net
.
IPNet
);
ok
&&
!
ipNet
.
IP
.
IsLoopback
()
{
if
ipNet
.
IP
.
To4
()
!=
nil
{
ip
:=
ipNet
.
IP
.
String
()
// Include common private network ranges
if
strings
.
HasPrefix
(
ip
,
"10."
)
||
strings
.
HasPrefix
(
ip
,
"172."
)
||
strings
.
HasPrefix
(
ip
,
"192.168."
)
{
networkIps
=
append
(
networkIps
,
ip
)
}
}
}
}
return
networkIps
}
// IsRunningInContainer detects if the application is running inside a container
func
IsRunningInContainer
()
bool
{
// Method 1: Check for .dockerenv file (Docker containers)
if
_
,
err
:=
os
.
Stat
(
"/.dockerenv"
);
err
==
nil
{
return
true
}
// Method 2: Check cgroup for container indicators
if
data
,
err
:=
os
.
ReadFile
(
"/proc/1/cgroup"
);
err
==
nil
{
content
:=
string
(
data
)
if
strings
.
Contains
(
content
,
"docker"
)
||
strings
.
Contains
(
content
,
"containerd"
)
||
strings
.
Contains
(
content
,
"kubepods"
)
||
strings
.
Contains
(
content
,
"/lxc/"
)
{
return
true
}
}
// Method 3: Check environment variables commonly set by container runtimes
containerEnvVars
:=
[]
string
{
"KUBERNETES_SERVICE_HOST"
,
"DOCKER_CONTAINER"
,
"container"
,
}
for
_
,
envVar
:=
range
containerEnvVars
{
if
os
.
Getenv
(
envVar
)
!=
""
{
return
true
}
}
// Method 4: Check if init process is not the traditional init
if
data
,
err
:=
os
.
ReadFile
(
"/proc/1/comm"
);
err
==
nil
{
comm
:=
strings
.
TrimSpace
(
string
(
data
))
// In containers, process 1 is often not "init" or "systemd"
if
comm
!=
"init"
&&
comm
!=
"systemd"
{
// Additional check: if it's a common container entrypoint
if
strings
.
Contains
(
comm
,
"docker"
)
||
strings
.
Contains
(
comm
,
"containerd"
)
||
strings
.
Contains
(
comm
,
"runc"
)
{
return
true
}
}
}
return
false
}
var
sizeKB
=
1024
var
sizeKB
=
1024
var
sizeMB
=
sizeKB
*
1024
var
sizeMB
=
sizeKB
*
1024
var
sizeGB
=
sizeMB
*
1024
var
sizeGB
=
sizeMB
*
1024
...
...
controller/channel.go
View file @
95cbab6b
...
@@ -188,6 +188,8 @@ func FetchUpstreamModels(c *gin.Context) {
...
@@ -188,6 +188,8 @@ func FetchUpstreamModels(c *gin.Context) {
url
=
fmt
.
Sprintf
(
"%s/v1beta/openai/models"
,
baseURL
)
// Remove key in url since we need to use AuthHeader
url
=
fmt
.
Sprintf
(
"%s/v1beta/openai/models"
,
baseURL
)
// Remove key in url since we need to use AuthHeader
case
constant
.
ChannelTypeAli
:
case
constant
.
ChannelTypeAli
:
url
=
fmt
.
Sprintf
(
"%s/compatible-mode/v1/models"
,
baseURL
)
url
=
fmt
.
Sprintf
(
"%s/compatible-mode/v1/models"
,
baseURL
)
case
constant
.
ChannelTypeZhipu_v4
:
url
=
fmt
.
Sprintf
(
"%s/api/paas/v4/models"
,
baseURL
)
default
:
default
:
url
=
fmt
.
Sprintf
(
"%s/v1/models"
,
baseURL
)
url
=
fmt
.
Sprintf
(
"%s/v1/models"
,
baseURL
)
}
}
...
@@ -1101,8 +1103,8 @@ func CopyChannel(c *gin.Context) {
...
@@ -1101,8 +1103,8 @@ func CopyChannel(c *gin.Context) {
// MultiKeyManageRequest represents the request for multi-key management operations
// MultiKeyManageRequest represents the request for multi-key management operations
type
MultiKeyManageRequest
struct
{
type
MultiKeyManageRequest
struct
{
ChannelId
int
`json:"channel_id"`
ChannelId
int
`json:"channel_id"`
Action
string
`json:"action"`
// "disable_key", "enable_key", "delete_disabled_keys", "get_key_status"
Action
string
`json:"action"`
// "disable_key", "enable_key", "delete_
key", "delete_
disabled_keys", "get_key_status"
KeyIndex
*
int
`json:"key_index,omitempty"`
// for disable_key
and enabl
e_key actions
KeyIndex
*
int
`json:"key_index,omitempty"`
// for disable_key
, enable_key, and delet
e_key actions
Page
int
`json:"page,omitempty"`
// for get_key_status pagination
Page
int
`json:"page,omitempty"`
// for get_key_status pagination
PageSize
int
`json:"page_size,omitempty"`
// for get_key_status pagination
PageSize
int
`json:"page_size,omitempty"`
// for get_key_status pagination
Status
*
int
`json:"status,omitempty"`
// for get_key_status filtering: 1=enabled, 2=manual_disabled, 3=auto_disabled, nil=all
Status
*
int
`json:"status,omitempty"`
// for get_key_status filtering: 1=enabled, 2=manual_disabled, 3=auto_disabled, nil=all
...
@@ -1430,6 +1432,86 @@ func ManageMultiKeys(c *gin.Context) {
...
@@ -1430,6 +1432,86 @@ func ManageMultiKeys(c *gin.Context) {
})
})
return
return
case
"delete_key"
:
if
request
.
KeyIndex
==
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"未指定要删除的密钥索引"
,
})
return
}
keyIndex
:=
*
request
.
KeyIndex
if
keyIndex
<
0
||
keyIndex
>=
channel
.
ChannelInfo
.
MultiKeySize
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"密钥索引超出范围"
,
})
return
}
keys
:=
channel
.
GetKeys
()
var
remainingKeys
[]
string
var
newStatusList
=
make
(
map
[
int
]
int
)
var
newDisabledTime
=
make
(
map
[
int
]
int64
)
var
newDisabledReason
=
make
(
map
[
int
]
string
)
newIndex
:=
0
for
i
,
key
:=
range
keys
{
// 跳过要删除的密钥
if
i
==
keyIndex
{
continue
}
remainingKeys
=
append
(
remainingKeys
,
key
)
// 保留其他密钥的状态信息,重新索引
if
channel
.
ChannelInfo
.
MultiKeyStatusList
!=
nil
{
if
status
,
exists
:=
channel
.
ChannelInfo
.
MultiKeyStatusList
[
i
];
exists
&&
status
!=
1
{
newStatusList
[
newIndex
]
=
status
}
}
if
channel
.
ChannelInfo
.
MultiKeyDisabledTime
!=
nil
{
if
t
,
exists
:=
channel
.
ChannelInfo
.
MultiKeyDisabledTime
[
i
];
exists
{
newDisabledTime
[
newIndex
]
=
t
}
}
if
channel
.
ChannelInfo
.
MultiKeyDisabledReason
!=
nil
{
if
r
,
exists
:=
channel
.
ChannelInfo
.
MultiKeyDisabledReason
[
i
];
exists
{
newDisabledReason
[
newIndex
]
=
r
}
}
newIndex
++
}
if
len
(
remainingKeys
)
==
0
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
false
,
"message"
:
"不能删除最后一个密钥"
,
})
return
}
// Update channel with remaining keys
channel
.
Key
=
strings
.
Join
(
remainingKeys
,
"
\n
"
)
channel
.
ChannelInfo
.
MultiKeySize
=
len
(
remainingKeys
)
channel
.
ChannelInfo
.
MultiKeyStatusList
=
newStatusList
channel
.
ChannelInfo
.
MultiKeyDisabledTime
=
newDisabledTime
channel
.
ChannelInfo
.
MultiKeyDisabledReason
=
newDisabledReason
err
=
channel
.
Update
()
if
err
!=
nil
{
common
.
ApiError
(
c
,
err
)
return
}
model
.
InitChannelCache
()
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"success"
:
true
,
"message"
:
"密钥已删除"
,
})
return
case
"delete_disabled_keys"
:
case
"delete_disabled_keys"
:
keys
:=
channel
.
GetKeys
()
keys
:=
channel
.
GetKeys
()
var
remainingKeys
[]
string
var
remainingKeys
[]
string
...
...
dto/gemini.go
View file @
95cbab6b
...
@@ -14,7 +14,30 @@ type GeminiChatRequest struct {
...
@@ -14,7 +14,30 @@ type GeminiChatRequest struct {
SafetySettings
[]
GeminiChatSafetySettings
`json:"safetySettings,omitempty"`
SafetySettings
[]
GeminiChatSafetySettings
`json:"safetySettings,omitempty"`
GenerationConfig
GeminiChatGenerationConfig
`json:"generationConfig,omitempty"`
GenerationConfig
GeminiChatGenerationConfig
`json:"generationConfig,omitempty"`
Tools
json
.
RawMessage
`json:"tools,omitempty"`
Tools
json
.
RawMessage
`json:"tools,omitempty"`
ToolConfig
*
ToolConfig
`json:"toolConfig,omitempty"`
SystemInstructions
*
GeminiChatContent
`json:"systemInstruction,omitempty"`
SystemInstructions
*
GeminiChatContent
`json:"systemInstruction,omitempty"`
CachedContent
string
`json:"cachedContent,omitempty"`
}
type
ToolConfig
struct
{
FunctionCallingConfig
*
FunctionCallingConfig
`json:"functionCallingConfig,omitempty"`
RetrievalConfig
*
RetrievalConfig
`json:"retrievalConfig,omitempty"`
}
type
FunctionCallingConfig
struct
{
Mode
FunctionCallingConfigMode
`json:"mode,omitempty"`
AllowedFunctionNames
[]
string
`json:"allowedFunctionNames,omitempty"`
}
type
FunctionCallingConfigMode
string
type
RetrievalConfig
struct
{
LatLng
*
LatLng
`json:"latLng,omitempty"`
LanguageCode
string
`json:"languageCode,omitempty"`
}
type
LatLng
struct
{
Latitude
*
float64
`json:"latitude,omitempty"`
Longitude
*
float64
`json:"longitude,omitempty"`
}
}
func
(
r
*
GeminiChatRequest
)
GetTokenCountMeta
()
*
types
.
TokenCountMeta
{
func
(
r
*
GeminiChatRequest
)
GetTokenCountMeta
()
*
types
.
TokenCountMeta
{
...
@@ -239,12 +262,20 @@ type GeminiChatGenerationConfig struct {
...
@@ -239,12 +262,20 @@ type GeminiChatGenerationConfig struct {
StopSequences
[]
string
`json:"stopSequences,omitempty"`
StopSequences
[]
string
`json:"stopSequences,omitempty"`
ResponseMimeType
string
`json:"responseMimeType,omitempty"`
ResponseMimeType
string
`json:"responseMimeType,omitempty"`
ResponseSchema
any
`json:"responseSchema,omitempty"`
ResponseSchema
any
`json:"responseSchema,omitempty"`
ResponseJsonSchema
json
.
RawMessage
`json:"responseJsonSchema,omitempty"`
PresencePenalty
*
float32
`json:"presencePenalty,omitempty"`
FrequencyPenalty
*
float32
`json:"frequencyPenalty,omitempty"`
ResponseLogprobs
bool
`json:"responseLogprobs,omitempty"`
Logprobs
*
int32
`json:"logprobs,omitempty"`
MediaResolution
MediaResolution
`json:"mediaResolution,omitempty"`
Seed
int64
`json:"seed,omitempty"`
Seed
int64
`json:"seed,omitempty"`
ResponseModalities
[]
string
`json:"responseModalities,omitempty"`
ResponseModalities
[]
string
`json:"responseModalities,omitempty"`
ThinkingConfig
*
GeminiThinkingConfig
`json:"thinkingConfig,omitempty"`
ThinkingConfig
*
GeminiThinkingConfig
`json:"thinkingConfig,omitempty"`
SpeechConfig
json
.
RawMessage
`json:"speechConfig,omitempty"`
// RawMessage to allow flexible speech config
SpeechConfig
json
.
RawMessage
`json:"speechConfig,omitempty"`
// RawMessage to allow flexible speech config
}
}
type
MediaResolution
string
type
GeminiChatCandidate
struct
{
type
GeminiChatCandidate
struct
{
Content
GeminiChatContent
`json:"content"`
Content
GeminiChatContent
`json:"content"`
FinishReason
*
string
`json:"finishReason"`
FinishReason
*
string
`json:"finishReason"`
...
...
main.go
View file @
95cbab6b
...
@@ -16,6 +16,7 @@ import (
...
@@ -16,6 +16,7 @@ import (
"one-api/setting/ratio_setting"
"one-api/setting/ratio_setting"
"os"
"os"
"strconv"
"strconv"
"time"
"github.com/bytedance/gopkg/util/gopool"
"github.com/bytedance/gopkg/util/gopool"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions"
...
@@ -33,6 +34,7 @@ var buildFS embed.FS
...
@@ -33,6 +34,7 @@ var buildFS embed.FS
var
indexPage
[]
byte
var
indexPage
[]
byte
func
main
()
{
func
main
()
{
startTime
:=
time
.
Now
()
err
:=
InitResources
()
err
:=
InitResources
()
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -150,6 +152,10 @@ func main() {
...
@@ -150,6 +152,10 @@ func main() {
if
port
==
""
{
if
port
==
""
{
port
=
strconv
.
Itoa
(
*
common
.
Port
)
port
=
strconv
.
Itoa
(
*
common
.
Port
)
}
}
// Log startup success message
common
.
LogStartupSuccess
(
startTime
,
port
)
err
=
server
.
Run
(
":"
+
port
)
err
=
server
.
Run
(
":"
+
port
)
if
err
!=
nil
{
if
err
!=
nil
{
common
.
FatalLog
(
"failed to start HTTP server: "
+
err
.
Error
())
common
.
FatalLog
(
"failed to start HTTP server: "
+
err
.
Error
())
...
@@ -204,4 +210,4 @@ func InitResources() error {
...
@@ -204,4 +210,4 @@ func InitResources() error {
return
err
return
err
}
}
return
nil
return
nil
}
}
\ No newline at end of file
web/src/components/common/markdown/MarkdownRenderer.jsx
View file @
95cbab6b
...
@@ -181,8 +181,8 @@ export function PreCode(props) {
...
@@ -181,8 +181,8 @@ export function PreCode(props) {
e
.
preventDefault
();
e
.
preventDefault
();
e
.
stopPropagation
();
e
.
stopPropagation
();
if
(
ref
.
current
)
{
if
(
ref
.
current
)
{
const
code
=
const
code
Element
=
ref
.
current
.
querySelector
(
'code'
);
ref
.
current
.
querySelector
(
'code'
)?.
innerTex
t
??
''
;
const
code
=
codeElement
?.
textConten
t
??
''
;
copy
(
code
).
then
((
success
)
=>
{
copy
(
code
).
then
((
success
)
=>
{
if
(
success
)
{
if
(
success
)
{
Toast
.
success
(
t
(
'代码已复制到剪贴板'
));
Toast
.
success
(
t
(
'代码已复制到剪贴板'
));
...
...
web/src/components/table/channels/modals/EditChannelModal.jsx
View file @
95cbab6b
...
@@ -85,6 +85,26 @@ const REGION_EXAMPLE = {
...
@@ -85,6 +85,26 @@ const REGION_EXAMPLE = {
'claude-3-5-sonnet-20240620'
:
'europe-west1'
,
'claude-3-5-sonnet-20240620'
:
'europe-west1'
,
};
};
// 支持并且已适配通过接口获取模型列表的渠道类型
const
MODEL_FETCHABLE_TYPES
=
new
Set
([
1
,
4
,
14
,
34
,
17
,
26
,
24
,
47
,
25
,
20
,
23
,
31
,
35
,
40
,
42
,
48
,
]);
function
type2secretPrompt
(
type
)
{
function
type2secretPrompt
(
type
)
{
// inputs.type === 15 ? '按照如下格式输入:APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入:APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥')
// inputs.type === 15 ? '按照如下格式输入:APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入:APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥')
switch
(
type
)
{
switch
(
type
)
{
...
@@ -1904,13 +1924,15 @@ const EditChannelModal = (props) => {
...
@@ -1904,13 +1924,15 @@ const EditChannelModal = (props) => {
>
>
{
t
(
'填入所有模型'
)
}
{
t
(
'填入所有模型'
)
}
</
Button
>
</
Button
>
<
Button
{
MODEL_FETCHABLE_TYPES
.
has
(
inputs
.
type
)
&&
(
size=
'small'
<
Button
type=
'tertiary'
size=
'small'
onClick=
{
()
=>
fetchUpstreamModelList
(
'models'
)
}
type=
'tertiary'
>
onClick=
{
()
=>
fetchUpstreamModelList
(
'models'
)
}
{
t
(
'获取模型列表'
)
}
>
</
Button
>
{
t
(
'获取模型列表'
)
}
</
Button
>
)
}
<
Button
<
Button
size=
'small'
size=
'small'
type=
'warning'
type=
'warning'
...
...
web/src/components/table/channels/modals/MultiKeyManageModal.jsx
View file @
95cbab6b
...
@@ -247,6 +247,32 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
...
@@ -247,6 +247,32 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
}
}
};
};
// Delete a specific key
const
handleDeleteKey
=
async
(
keyIndex
)
=>
{
const
operationId
=
`delete_
${
keyIndex
}
`
;
setOperationLoading
((
prev
)
=>
({
...
prev
,
[
operationId
]:
true
}));
try
{
const
res
=
await
API
.
post
(
'/api/channel/multi_key/manage'
,
{
channel_id
:
channel
.
id
,
action
:
'delete_key'
,
key_index
:
keyIndex
,
});
if
(
res
.
data
.
success
)
{
showSuccess
(
t
(
'密钥已删除'
));
await
loadKeyStatus
(
currentPage
,
pageSize
);
// Reload current page
onRefresh
&&
onRefresh
();
// Refresh parent component
}
else
{
showError
(
res
.
data
.
message
);
}
}
catch
(
error
)
{
showError
(
t
(
'删除密钥失败'
));
}
finally
{
setOperationLoading
((
prev
)
=>
({
...
prev
,
[
operationId
]:
false
}));
}
};
// Handle page change
// Handle page change
const
handlePageChange
=
(
page
)
=>
{
const
handlePageChange
=
(
page
)
=>
{
setCurrentPage
(
page
);
setCurrentPage
(
page
);
...
@@ -384,7 +410,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
...
@@ -384,7 +410,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
title
:
t
(
'操作'
),
title
:
t
(
'操作'
),
key
:
'action'
,
key
:
'action'
,
fixed
:
'right'
,
fixed
:
'right'
,
width
:
1
0
0
,
width
:
1
5
0
,
render
:
(
_
,
record
)
=>
(
render
:
(
_
,
record
)
=>
(
<
Space
>
<
Space
>
{
record
.
status
===
1
?
(
{
record
.
status
===
1
?
(
...
@@ -406,6 +432,21 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
...
@@ -406,6 +432,21 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
{
t
(
'启用'
)
}
{
t
(
'启用'
)
}
</
Button
>
</
Button
>
)
}
)
}
<
Popconfirm
title=
{
t
(
'确定要删除此密钥吗?'
)
}
content=
{
t
(
'此操作不可撤销,将永久删除该密钥'
)
}
onConfirm=
{
()
=>
handleDeleteKey
(
record
.
index
)
}
okType=
{
'danger'
}
position=
{
'topRight'
}
>
<
Button
type=
'danger'
size=
'small'
loading=
{
operationLoading
[
`delete_${record.index}`
]
}
>
{
t
(
'删除'
)
}
</
Button
>
</
Popconfirm
>
</
Space
>
</
Space
>
),
),
},
},
...
...
web/src/helpers/api.js
View file @
95cbab6b
...
@@ -118,7 +118,6 @@ export const buildApiPayload = (
...
@@ -118,7 +118,6 @@ export const buildApiPayload = (
model
:
inputs
.
model
,
model
:
inputs
.
model
,
group
:
inputs
.
group
,
group
:
inputs
.
group
,
messages
:
processedMessages
,
messages
:
processedMessages
,
group
:
inputs
.
group
,
stream
:
inputs
.
stream
,
stream
:
inputs
.
stream
,
};
};
...
@@ -132,13 +131,15 @@ export const buildApiPayload = (
...
@@ -132,13 +131,15 @@ export const buildApiPayload = (
seed
:
'seed'
,
seed
:
'seed'
,
};
};
Object
.
entries
(
parameterMappings
).
forEach
(([
key
,
param
])
=>
{
Object
.
entries
(
parameterMappings
).
forEach
(([
key
,
param
])
=>
{
if
(
const
enabled
=
parameterEnabled
[
key
];
parameterEnabled
[
key
]
&&
const
value
=
inputs
[
param
];
inputs
[
param
]
!==
undefined
&&
const
hasValue
=
value
!==
undefined
&&
value
!==
null
;
inputs
[
param
]
!==
null
)
{
payload
[
param
]
=
inputs
[
param
];
if
(
enabled
&&
hasValue
)
{
payload
[
param
]
=
value
;
}
}
});
});
...
...
web/src/helpers/utils.jsx
View file @
95cbab6b
...
@@ -75,13 +75,17 @@ export async function copy(text) {
...
@@ -75,13 +75,17 @@ export async function copy(text) {
await
navigator
.
clipboard
.
writeText
(
text
);
await
navigator
.
clipboard
.
writeText
(
text
);
}
catch
(
e
)
{
}
catch
(
e
)
{
try
{
try
{
// 构建input 执行 复制命令
// 构建 textarea 执行复制命令,保留多行文本格式
var
_input
=
window
.
document
.
createElement
(
'input'
);
const
textarea
=
window
.
document
.
createElement
(
'textarea'
);
_input
.
value
=
text
;
textarea
.
value
=
text
;
window
.
document
.
body
.
appendChild
(
_input
);
textarea
.
setAttribute
(
'readonly'
,
''
);
_input
.
select
();
textarea
.
style
.
position
=
'fixed'
;
window
.
document
.
execCommand
(
'Copy'
);
textarea
.
style
.
left
=
'-9999px'
;
window
.
document
.
body
.
removeChild
(
_input
);
textarea
.
style
.
top
=
'-9999px'
;
window
.
document
.
body
.
appendChild
(
textarea
);
textarea
.
select
();
window
.
document
.
execCommand
(
'copy'
);
window
.
document
.
body
.
removeChild
(
textarea
);
}
catch
(
e
)
{
}
catch
(
e
)
{
okay
=
false
;
okay
=
false
;
console
.
error
(
e
);
console
.
error
(
e
);
...
...
web/src/i18n/locales/en.json
View file @
95cbab6b
...
@@ -1889,6 +1889,10 @@
...
@@ -1889,6 +1889,10 @@
"确定要删除所有已自动禁用的密钥吗?"
:
"Are you sure you want to delete all automatically disabled keys?"
,
"确定要删除所有已自动禁用的密钥吗?"
:
"Are you sure you want to delete all automatically disabled keys?"
,
"此操作不可撤销,将永久删除已自动禁用的密钥"
:
"This operation cannot be undone, and all automatically disabled keys will be permanently deleted."
,
"此操作不可撤销,将永久删除已自动禁用的密钥"
:
"This operation cannot be undone, and all automatically disabled keys will be permanently deleted."
,
"删除自动禁用密钥"
:
"Delete auto disabled keys"
,
"删除自动禁用密钥"
:
"Delete auto disabled keys"
,
"确定要删除此密钥吗?"
:
"Are you sure you want to delete this key?"
,
"此操作不可撤销,将永久删除该密钥"
:
"This operation cannot be undone, and the key will be permanently deleted."
,
"密钥已删除"
:
"Key has been deleted"
,
"删除密钥失败"
:
"Failed to delete key"
,
"图标"
:
"Icon"
,
"图标"
:
"Icon"
,
"模型图标"
:
"Model icon"
,
"模型图标"
:
"Model icon"
,
"请输入图标名称"
:
"Please enter the icon name"
,
"请输入图标名称"
:
"Please enter the icon name"
,
...
...
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