From f5a1cd34634b92ea2013cfa16b599d06a4253386 Mon Sep 17 00:00:00 2001 From: JustSong Date: Sat, 23 Sep 2023 22:37:11 +0800 Subject: [PATCH 1/7] feat: add support for gpt-3.5-turbo-instruct (close #545) --- common/model-ratio.go | 1 + controller/model.go | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/common/model-ratio.go b/common/model-ratio.go index eeb23e07..0d341b02 100644 --- a/common/model-ratio.go +++ b/common/model-ratio.go @@ -24,6 +24,7 @@ var ModelRatio = map[string]float64{ "gpt-3.5-turbo-0613": 0.75, "gpt-3.5-turbo-16k": 1.5, // $0.003 / 1K tokens "gpt-3.5-turbo-16k-0613": 1.5, + "gpt-3.5-turbo-instruct": 0.75, // $0.0015 / 1K tokens "text-ada-001": 0.2, "text-babbage-001": 0.25, "text-curie-001": 1, diff --git a/controller/model.go b/controller/model.go index 637ebe10..dedd0f0a 100644 --- a/controller/model.go +++ b/controller/model.go @@ -117,6 +117,15 @@ func init() { Root: "gpt-3.5-turbo-16k-0613", Parent: nil, }, + { + Id: "gpt-3.5-turbo-instruct", + Object: "model", + Created: 1677649963, + OwnedBy: "openai", + Permission: permission, + Root: "gpt-3.5-turbo-instruct", + Parent: nil, + }, { Id: "gpt-4", Object: "model", From fd9846361129452fe5ad8b45b9b401311f5872c9 Mon Sep 17 00:00:00 2001 From: JustSong Date: Sat, 23 Sep 2023 22:57:59 +0800 Subject: [PATCH 2/7] chore: update ali's model name --- common/model-ratio.go | 4 ++-- controller/model.go | 8 ++++---- web/src/pages/Channel/EditChannel.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/model-ratio.go b/common/model-ratio.go index 0d341b02..4b3dd763 100644 --- a/common/model-ratio.go +++ b/common/model-ratio.go @@ -51,8 +51,8 @@ var ModelRatio = map[string]float64{ "chatglm_pro": 0.7143, // ¥0.01 / 1k tokens "chatglm_std": 0.3572, // ¥0.005 / 1k tokens "chatglm_lite": 0.1429, // ¥0.002 / 1k tokens - "qwen-v1": 0.8572, // ¥0.012 / 1k tokens - "qwen-plus-v1": 1, // ¥0.014 / 1k tokens + "qwen-turbo": 0.8572, // ¥0.012 / 1k tokens + "qwen-plus": 10, // ¥0.14 / 1k tokens "text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens "SparkDesk": 1.2858, // ¥0.018 / 1k tokens "360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens diff --git a/controller/model.go b/controller/model.go index dedd0f0a..ae2061b3 100644 --- a/controller/model.go +++ b/controller/model.go @@ -352,21 +352,21 @@ func init() { Parent: nil, }, { - Id: "qwen-v1", + Id: "qwen-turbo", Object: "model", Created: 1677649963, OwnedBy: "ali", Permission: permission, - Root: "qwen-v1", + Root: "qwen-turbo", Parent: nil, }, { - Id: "qwen-plus-v1", + Id: "qwen-plus", Object: "model", Created: 1677649963, OwnedBy: "ali", Permission: permission, - Root: "qwen-plus-v1", + Root: "qwen-plus", Parent: nil, }, { diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js index 4c8dd0c4..9b128591 100644 --- a/web/src/pages/Channel/EditChannel.js +++ b/web/src/pages/Channel/EditChannel.js @@ -67,7 +67,7 @@ const EditChannel = () => { localModels = ['ERNIE-Bot', 'ERNIE-Bot-turbo', 'Embedding-V1']; break; case 17: - localModels = ['qwen-v1', 'qwen-plus-v1', 'text-embedding-v1']; + localModels = ['qwen-turbo', 'qwen-plus', 'text-embedding-v1']; break; case 16: localModels = ['chatglm_pro', 'chatglm_std', 'chatglm_lite']; From f9b748c2caf02879f0ff9c104701e12d71894bff Mon Sep 17 00:00:00 2001 From: JustSong Date: Fri, 29 Sep 2023 11:38:27 +0800 Subject: [PATCH 3/7] chore: add MEMORY_CACHE_ENABLED env variable --- README.md | 20 +++++++++++--------- common/constants.go | 3 ++- main.go | 20 ++++++++++---------- middleware/auth.go | 2 +- model/cache.go | 2 +- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 7f85bcc0..70ff4c15 100644 --- a/README.md +++ b/README.md @@ -309,22 +309,24 @@ graph LR + `SQL_CONN_MAX_LIFETIME`:连接的最大生命周期,默认为 `60`,单位分钟。 4. `FRONTEND_BASE_URL`:设置之后将重定向页面请求到指定的地址,仅限从服务器设置。 + 例子:`FRONTEND_BASE_URL=https://openai.justsong.cn` -5. `SYNC_FREQUENCY`:设置之后将定期与数据库同步配置,单位为秒,未设置则不进行同步。 +5. `MEMORY_CACHE_ENABLED`:启用内存缓存,会导致用户额度的更新存在一定的延迟,可选值为 `true` 和 `false`,未设置则默认为 `false`。 + + 例子:`MEMORY_CACHE_ENABLED=true` +6. `SYNC_FREQUENCY`:在启用缓存的情况下与数据库同步配置的频率,单位为秒,默认为 `600` 秒。 + 例子:`SYNC_FREQUENCY=60` -6. `NODE_TYPE`:设置之后将指定节点类型,可选值为 `master` 和 `slave`,未设置则默认为 `master`。 +7. `NODE_TYPE`:设置之后将指定节点类型,可选值为 `master` 和 `slave`,未设置则默认为 `master`。 + 例子:`NODE_TYPE=slave` -7. `CHANNEL_UPDATE_FREQUENCY`:设置之后将定期更新渠道余额,单位为分钟,未设置则不进行更新。 +8. `CHANNEL_UPDATE_FREQUENCY`:设置之后将定期更新渠道余额,单位为分钟,未设置则不进行更新。 + 例子:`CHANNEL_UPDATE_FREQUENCY=1440` -8. `CHANNEL_TEST_FREQUENCY`:设置之后将定期检查渠道,单位为分钟,未设置则不进行检查。 +9. `CHANNEL_TEST_FREQUENCY`:设置之后将定期检查渠道,单位为分钟,未设置则不进行检查。 + 例子:`CHANNEL_TEST_FREQUENCY=1440` -9. `POLLING_INTERVAL`:批量更新渠道余额以及测试可用性时的请求间隔,单位为秒,默认无间隔。 - + 例子:`POLLING_INTERVAL=5` -10. `BATCH_UPDATE_ENABLED`:启用数据库批量更新聚合,会导致用户额度的更新存在一定的延迟可选值为 `true` 和 `false`,未设置则默认为 `false`。 +10. `POLLING_INTERVAL`:批量更新渠道余额以及测试可用性时的请求间隔,单位为秒,默认无间隔。 + + 例子:`POLLING_INTERVAL=5` +11. `BATCH_UPDATE_ENABLED`:启用数据库批量更新聚合,会导致用户额度的更新存在一定的延迟可选值为 `true` 和 `false`,未设置则默认为 `false`。 + 例子:`BATCH_UPDATE_ENABLED=true` + 如果你遇到了数据库连接数过多的问题,可以尝试启用该选项。 -11. `BATCH_UPDATE_INTERVAL=5`:批量更新聚合的时间间隔,单位为秒,默认为 `5`。 +12. `BATCH_UPDATE_INTERVAL=5`:批量更新聚合的时间间隔,单位为秒,默认为 `5`。 + 例子:`BATCH_UPDATE_INTERVAL=5` -12. 请求频率限制: +13. 请求频率限制: + `GLOBAL_API_RATE_LIMIT`:全局 API 速率限制(除中继请求外),单 ip 三分钟内的最大请求数,默认为 `180`。 + `GLOBAL_WEB_RATE_LIMIT`:全局 Web 速率限制,单 ip 三分钟内的最大请求数,默认为 `60`。 diff --git a/common/constants.go b/common/constants.go index 794a795f..fe412e1a 100644 --- a/common/constants.go +++ b/common/constants.go @@ -56,6 +56,7 @@ var EmailDomainWhitelist = []string{ } var DebugEnabled = os.Getenv("DEBUG") == "true" +var MemoryCacheEnabled = os.Getenv("MEMORY_CACHE_ENABLED") == "true" var LogConsumeEnabled = true @@ -92,7 +93,7 @@ var IsMasterNode = os.Getenv("NODE_TYPE") != "slave" var requestInterval, _ = strconv.Atoi(os.Getenv("POLLING_INTERVAL")) var RequestInterval = time.Duration(requestInterval) * time.Second -var SyncFrequency = 10 * 60 // unit is second, will be overwritten by SYNC_FREQUENCY +var SyncFrequency = GetOrDefault("SYNC_FREQUENCY", 10*60) // unit is second var BatchUpdateEnabled = false var BatchUpdateInterval = GetOrDefault("BATCH_UPDATE_INTERVAL", 5) diff --git a/main.go b/main.go index e8ef4c20..88938516 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "embed" + "fmt" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" @@ -50,18 +51,17 @@ func main() { // Initialize options model.InitOptionMap() if common.RedisEnabled { + // for compatibility with old versions + common.MemoryCacheEnabled = true + } + if common.MemoryCacheEnabled { + common.SysLog("memory cache enabled") + common.SysError(fmt.Sprintf("sync frequency: %d seconds", common.SyncFrequency)) model.InitChannelCache() } - if os.Getenv("SYNC_FREQUENCY") != "" { - frequency, err := strconv.Atoi(os.Getenv("SYNC_FREQUENCY")) - if err != nil { - common.FatalLog("failed to parse SYNC_FREQUENCY: " + err.Error()) - } - common.SyncFrequency = frequency - go model.SyncOptions(frequency) - if common.RedisEnabled { - go model.SyncChannelCache(frequency) - } + if common.MemoryCacheEnabled { + go model.SyncOptions(common.SyncFrequency) + go model.SyncChannelCache(common.SyncFrequency) } if os.Getenv("CHANNEL_UPDATE_FREQUENCY") != "" { frequency, err := strconv.Atoi(os.Getenv("CHANNEL_UPDATE_FREQUENCY")) diff --git a/middleware/auth.go b/middleware/auth.go index dfbc7dbd..b0803612 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -94,7 +94,7 @@ func TokenAuth() func(c *gin.Context) { abortWithMessage(c, http.StatusUnauthorized, err.Error()) return } - userEnabled, err := model.IsUserEnabled(token.UserId) + userEnabled, err := model.CacheIsUserEnabled(token.UserId) if err != nil { abortWithMessage(c, http.StatusInternalServerError, err.Error()) return diff --git a/model/cache.go b/model/cache.go index b9d6b612..a7f5c06f 100644 --- a/model/cache.go +++ b/model/cache.go @@ -186,7 +186,7 @@ func SyncChannelCache(frequency int) { } func CacheGetRandomSatisfiedChannel(group string, model string) (*Channel, error) { - if !common.RedisEnabled { + if !common.MemoryCacheEnabled { return GetRandomSatisfiedChannel(group, model) } channelSyncLock.RLock() From 197d1d7a9d45393f2acbacf809b8357020db9a3a Mon Sep 17 00:00:00 2001 From: JustSong Date: Fri, 29 Sep 2023 17:49:47 +0800 Subject: [PATCH 4/7] docs: update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 70ff4c15..49766d8b 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,9 @@ _✨ 通过标准的 OpenAI API 格式访问所有的大模型,开箱即用 > **Warning** > 使用 Docker 拉取的最新镜像可能是 `alpha` 版本,如果追求稳定性请手动指定版本。 +> **Warning** +> 使用 root 用户初次登录系统后,务必修改默认密码 `123456`! + ## 功能 1. 支持多种大模型: + [x] [OpenAI ChatGPT 系列模型](https://platform.openai.com/docs/guides/gpt/chat-completions-api)(支持 [Azure OpenAI API](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference)) From 594f06e7b00df640630132acb2dc6a659597a9af Mon Sep 17 00:00:00 2001 From: JustSong Date: Fri, 29 Sep 2023 17:56:11 +0800 Subject: [PATCH 5/7] perf: lazy initialization for token encoders (close #566) --- controller/relay-utils.go | 41 ++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/controller/relay-utils.go b/controller/relay-utils.go index 3d5948fc..4775ec88 100644 --- a/controller/relay-utils.go +++ b/controller/relay-utils.go @@ -9,44 +9,53 @@ import ( "net/http" "one-api/common" "strconv" + "strings" ) var stopFinishReason = "stop" +// tokenEncoderMap won't grow after initialization var tokenEncoderMap = map[string]*tiktoken.Tiktoken{} +var defaultTokenEncoder *tiktoken.Tiktoken func InitTokenEncoders() { common.SysLog("initializing token encoders") - fallbackTokenEncoder, err := tiktoken.EncodingForModel("gpt-3.5-turbo") + gpt35TokenEncoder, err := tiktoken.EncodingForModel("gpt-3.5-turbo") if err != nil { - common.FatalLog(fmt.Sprintf("failed to get fallback token encoder: %s", err.Error())) + common.FatalLog(fmt.Sprintf("failed to get gpt-3.5-turbo token encoder: %s", err.Error())) + } + defaultTokenEncoder = gpt35TokenEncoder + gpt4TokenEncoder, err := tiktoken.EncodingForModel("gpt-4") + if err != nil { + common.FatalLog(fmt.Sprintf("failed to get gpt-4 token encoder: %s", err.Error())) } for model, _ := range common.ModelRatio { - tokenEncoder, err := tiktoken.EncodingForModel(model) - if err != nil { - common.SysError(fmt.Sprintf("using fallback encoder for model %s", model)) - tokenEncoderMap[model] = fallbackTokenEncoder - continue + if strings.HasPrefix(model, "gpt-3.5") { + tokenEncoderMap[model] = gpt35TokenEncoder + } else if strings.HasPrefix(model, "gpt-4") { + tokenEncoderMap[model] = gpt4TokenEncoder + } else { + tokenEncoderMap[model] = nil } - tokenEncoderMap[model] = tokenEncoder } common.SysLog("token encoders initialized") } func getTokenEncoder(model string) *tiktoken.Tiktoken { - if tokenEncoder, ok := tokenEncoderMap[model]; ok { + tokenEncoder, ok := tokenEncoderMap[model] + if ok && tokenEncoder != nil { return tokenEncoder } - tokenEncoder, err := tiktoken.EncodingForModel(model) - if err != nil { - common.SysError(fmt.Sprintf("failed to get token encoder for model %s: %s, using encoder for gpt-3.5-turbo", model, err.Error())) - tokenEncoder, err = tiktoken.EncodingForModel("gpt-3.5-turbo") + if ok { + tokenEncoder, err := tiktoken.EncodingForModel(model) if err != nil { - common.FatalLog(fmt.Sprintf("failed to get token encoder for model gpt-3.5-turbo: %s", err.Error())) + common.SysError(fmt.Sprintf("failed to get token encoder for model %s: %s, using encoder for gpt-3.5-turbo", model, err.Error())) + tokenEncoder = defaultTokenEncoder } + tokenEncoderMap[model] = tokenEncoder + return tokenEncoder } - tokenEncoderMap[model] = tokenEncoder - return tokenEncoder + return defaultTokenEncoder } func getTokenNum(tokenEncoder *tiktoken.Tiktoken, text string) int { From f0fc991b447b7061d97737ee758837d21f54d0e0 Mon Sep 17 00:00:00 2001 From: JustSong Date: Fri, 29 Sep 2023 18:07:20 +0800 Subject: [PATCH 6/7] chore: remind user to change default password (close #516) --- web/src/components/LoginForm.js | 16 +++++++++++----- web/src/pages/User/EditUser.js | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/web/src/components/LoginForm.js b/web/src/components/LoginForm.js index b5c4e6f9..a3913220 100644 --- a/web/src/components/LoginForm.js +++ b/web/src/components/LoginForm.js @@ -2,8 +2,8 @@ import React, { useContext, useEffect, useState } from 'react'; import { Button, Divider, Form, Grid, Header, Image, Message, Modal, Segment } from 'semantic-ui-react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; import { UserContext } from '../context/User'; -import { API, getLogo, showError, showSuccess } from '../helpers'; -import { getOAuthState, onGitHubOAuthClicked } from './utils'; +import { API, getLogo, showError, showSuccess, showWarning } from '../helpers'; +import { onGitHubOAuthClicked } from './utils'; const LoginForm = () => { const [inputs, setInputs] = useState({ @@ -68,8 +68,14 @@ const LoginForm = () => { if (success) { userDispatch({ type: 'login', payload: data }); localStorage.setItem('user', JSON.stringify(data)); - navigate('/'); - showSuccess('登录成功!'); + if (username === 'root' && password === '123456') { + navigate('/user/edit'); + showSuccess('登录成功!'); + showWarning('请立刻修改默认密码!'); + } else { + navigate('/token'); + showSuccess('登录成功!'); + } } else { showError(message); } @@ -126,7 +132,7 @@ const LoginForm = () => { circular color='black' icon='github' - onClick={()=>onGitHubOAuthClicked(status.github_client_id)} + onClick={() => onGitHubOAuthClicked(status.github_client_id)} /> ) : ( <> diff --git a/web/src/pages/User/EditUser.js b/web/src/pages/User/EditUser.js index e8f96027..8ae0e556 100644 --- a/web/src/pages/User/EditUser.js +++ b/web/src/pages/User/EditUser.js @@ -102,7 +102,7 @@ const EditUser = () => { label='密码' name='password' type={'password'} - placeholder={'请输入新的密码'} + placeholder={'请输入新的密码,最短 8 位'} onChange={handleInputChange} value={password} autoComplete='new-password' From 53b2cace0b5ea70fc5bbcc05d1d5cc5b9d653c00 Mon Sep 17 00:00:00 2001 From: JustSong Date: Fri, 29 Sep 2023 18:13:57 +0800 Subject: [PATCH 7/7] chore: sync model schema --- model/channel.go | 2 +- model/log.go | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/model/channel.go b/model/channel.go index aa3b8a10..61fe9093 100644 --- a/model/channel.go +++ b/model/channel.go @@ -11,7 +11,7 @@ type Channel struct { Key string `json:"key" gorm:"not null;index"` Status int `json:"status" gorm:"default:1"` Name string `json:"name" gorm:"index"` - Weight int `json:"weight"` + Weight *uint `json:"weight" gorm:"default:0"` CreatedTime int64 `json:"created_time" gorm:"bigint"` TestTime int64 `json:"test_time" gorm:"bigint"` ResponseTime int `json:"response_time"` // in milliseconds diff --git a/model/log.go b/model/log.go index 8e177258..c189e01d 100644 --- a/model/log.go +++ b/model/log.go @@ -9,7 +9,7 @@ import ( type Log struct { Id int `json:"id"` - UserId int `json:"user_id"` + UserId int `json:"user_id" gorm:"index"` CreatedAt int64 `json:"created_at" gorm:"bigint;index"` Type int `json:"type" gorm:"index"` Content string `json:"content"` @@ -19,7 +19,7 @@ type Log struct { Quota int `json:"quota" gorm:"default:0"` PromptTokens int `json:"prompt_tokens" gorm:"default:0"` CompletionTokens int `json:"completion_tokens" gorm:"default:0"` - Channel int `json:"channel" gorm:"default:0"` + ChannelId int `json:"channel" gorm:"index"` } const ( @@ -47,7 +47,6 @@ 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) { common.LogInfo(ctx, fmt.Sprintf("record consume log: userId=%d, channelId=%d, promptTokens=%d, completionTokens=%d, modelName=%s, tokenName=%s, quota=%d, content=%s", userId, channelId, promptTokens, completionTokens, modelName, tokenName, quota, content)) if !common.LogConsumeEnabled { @@ -64,7 +63,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke TokenName: tokenName, ModelName: modelName, Quota: quota, - Channel: channelId, + ChannelId: channelId, } err := DB.Create(log).Error if err != nil {