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

View File

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

View File

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

View File

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

View File

@ -29,19 +29,75 @@
</el-col>
</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-button type="primary" @click="save">Create</el-button>
<el-button type="primary" @click="save">保存</el-button>
</el-form-item>
</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>
</template>
<script>
import {defineComponent} from "vue";
import {Plus} from "@element-plus/icons-vue";
export default defineComponent({
name: 'SysConfig',
components: {Plus},
data() {
return {
title: "系统管理",
@ -59,5 +115,14 @@ export default defineComponent({
<style lang="stylus" scoped>
.system-config {
.api-key-box {
padding-bottom: 10px;
justify-content: end;
.el-icon--right {
margin-right 5px;
}
}
}
</style>