update the data struct of API KEYS

This commit is contained in:
RockYang 2023-05-02 12:06:02 +08:00
parent 523cd4d4c9
commit 556abf5276
5 changed files with 83 additions and 19 deletions

View File

@ -215,7 +215,7 @@ func (s *Server) sendMessage(ctx context.Context, session types.ChatSession, rol
logger.Infof("API Key %s is deactivated", apiKey) logger.Infof("API Key %s is deactivated", apiKey)
// 移除当前 API key // 移除当前 API key
for i, v := range s.Config.Chat.ApiKeys { for i, v := range s.Config.Chat.ApiKeys {
if v == apiKey { if v.Value == apiKey {
s.Config.Chat.ApiKeys = append(s.Config.Chat.ApiKeys[:i], s.Config.Chat.ApiKeys[i+1:]...) s.Config.Chat.ApiKeys = append(s.Config.Chat.ApiKeys[:i], s.Config.Chat.ApiKeys[i+1:]...)
} }
} }
@ -313,30 +313,26 @@ func (s *Server) sendMessage(ctx context.Context, session types.ChatSession, rol
// 随机获取一个 API Key如果请求失败则更换 API Key 重试 // 随机获取一个 API Key如果请求失败则更换 API Key 重试
func (s *Server) getApiKey(failedKey string) string { func (s *Server) getApiKey(failedKey string) string {
var keys = make([]string, 0) var keys = make([]types.APIKey, 0)
for _, v := range s.Config.Chat.ApiKeys { for _, key := range s.Config.Chat.ApiKeys {
// 过滤掉刚刚失败的 Key // 过滤掉刚刚失败的 Key
if v == failedKey { if key.Value == failedKey {
continue continue
} }
// 获取 API Key 的上次调用时间,控制调用频率 // 保持每分钟访问不超过 15 次,控制调用频率
var lastAccess int64 if key.LastUsed > 0 && time.Now().Unix()-key.LastUsed <= 4 {
if t, ok := s.ApiKeyAccessStat[v]; ok {
lastAccess = t
}
// 保持每分钟访问不超过 15 次
if time.Now().Unix()-lastAccess <= 4 {
continue continue
} }
keys = append(keys, v) keys = append(keys, key)
} }
// 从可用的 Key 中随机选一个
rand.NewSource(time.Now().UnixNano()) rand.NewSource(time.Now().UnixNano())
if len(keys) > 0 { if len(keys) > 0 {
key := keys[rand.Intn(len(keys))] key := keys[rand.Intn(len(keys))]
s.ApiKeyAccessStat[key] = time.Now().Unix() key.LastUsed = time.Now().Unix()
return key return key.Value
} }
return "" return ""
} }

View File

@ -41,7 +41,6 @@ type Server struct {
// 保存 Websocket 会话 Username, 每个 Username 只能连接一次 // 保存 Websocket 会话 Username, 每个 Username 只能连接一次
// 防止第三方直接连接 socket 调用 OpenAI API // 防止第三方直接连接 socket 调用 OpenAI API
ChatSession map[string]types.ChatSession //map[sessionId]User ChatSession map[string]types.ChatSession //map[sessionId]User
ApiKeyAccessStat map[string]int64 // 记录每个 API Key 的最后访问之间,保持在 15/min 之内
ChatClients map[string]*WsClient // Websocket 连接集合 ChatClients map[string]*WsClient // Websocket 连接集合
ReqCancelFunc map[string]context.CancelFunc // HttpClient 请求取消 handle function ReqCancelFunc map[string]context.CancelFunc // HttpClient 请求取消 handle function
DebugMode bool // 是否开启调试模式 DebugMode bool // 是否开启调试模式
@ -70,7 +69,6 @@ func NewServer(configPath string) (*Server, error) {
ChatSession: make(map[string]types.ChatSession), ChatSession: make(map[string]types.ChatSession),
ChatClients: make(map[string]*WsClient), ChatClients: make(map[string]*WsClient),
ReqCancelFunc: make(map[string]context.CancelFunc), ReqCancelFunc: make(map[string]context.CancelFunc),
ApiKeyAccessStat: make(map[string]int64),
}, nil }, nil
} }

View File

@ -37,7 +37,7 @@ type Manager struct {
// Chat configs struct // Chat configs struct
type Chat struct { type Chat struct {
ApiURL string ApiURL string
ApiKeys []string ApiKeys []APIKey
Model string Model string
Temperature float32 Temperature float32
MaxTokens int MaxTokens int
@ -45,6 +45,11 @@ type Chat struct {
ChatContextExpireTime int // 聊天上下文过期时间,单位:秒 ChatContextExpireTime int // 聊天上下文过期时间,单位:秒
} }
type APIKey struct {
Value string `json:"value"` // Key value
LastUsed int64 `json:"last_used"` // 最后使用时间
}
// Session configs struct // Session configs struct
type Session struct { type Session struct {
SecretKey string // session encryption key SecretKey string // session encryption key

View File

@ -30,7 +30,7 @@ func NewDefaultConfig() *types.Config {
}, },
Chat: types.Chat{ Chat: types.Chat{
ApiURL: "https://api.openai.com/v1/chat/completions", ApiURL: "https://api.openai.com/v1/chat/completions",
ApiKeys: []string{""}, ApiKeys: make([]types.APIKey, 0),
Model: "gpt-3.5-turbo", Model: "gpt-3.5-turbo",
MaxTokens: 1024, MaxTokens: 1024,
Temperature: 0.9, Temperature: 0.9,

View File

@ -29,19 +29,75 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row>
<el-col :span="12">
<div class="grid-content">
<el-form-item label="Max Tokens">
<el-input v-model="form['max_tokens']" placeholder="回复的最大字数最大4096"/>
</el-form-item>
</div>
</el-col>
<el-col :span="12">
<div class="grid-content">
<el-form-item label="上下文超时">
<el-input v-model="form['chat_context_expire_time']" placeholder="默认60min"/>
</el-form-item>
</div>
</el-col>
</el-row>
<el-form-item label="对话上下文">
<el-switch v-model="form['enable_context']" />
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="save">Create</el-button> <el-button type="primary" @click="save">保存</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-divider content-position="center">API KEY 管理</el-divider>
<el-row class="api-key-box">
<el-button type="primary" @click="save">
<el-icon class="el-icon--right"><Plus /></el-icon>
</el-button>
</el-row>
<el-row>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="key" label="API-KEY" />
<el-table-column prop="last_used" label="最后使用" width="180">
<template #default="scope">
<span>{{scope.row['last_used']}}</span>
<el-tag>未使用</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="180">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)"
>Edit</el-button
>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>Delete</el-button
>
</template>
</el-table-column>
</el-table>
</el-row>
</div> </div>
</template> </template>
<script> <script>
import {defineComponent} from "vue"; import {defineComponent} from "vue";
import {Plus} from "@element-plus/icons-vue";
export default defineComponent({ export default defineComponent({
name: 'SysConfig', name: 'SysConfig',
components: {Plus},
data() { data() {
return { return {
title: "系统管理", title: "系统管理",
@ -59,5 +115,14 @@ export default defineComponent({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.system-config { .system-config {
.api-key-box {
padding-bottom: 10px;
justify-content: end;
.el-icon--right {
margin-right 5px;
}
}
} }
</style> </style>