From d6f6403fd38e95da933c3d932fa771f593da5865 Mon Sep 17 00:00:00 2001 From: QuentinHsu Date: Sun, 11 Aug 2024 11:18:08 +0800 Subject: [PATCH 01/17] chore: update @so1ve/prettier-config to version 3.1.0 --- web/package.json | 2 +- web/pnpm-lock.yaml | 104 ++++++++++++++++++++++----------------------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/web/package.json b/web/package.json index e814d65..3bf290f 100644 --- a/web/package.json +++ b/web/package.json @@ -50,7 +50,7 @@ ] }, "devDependencies": { - "@so1ve/prettier-config": "^2.0.0", + "@so1ve/prettier-config": "^3.1.0", "@vitejs/plugin-react": "^4.2.1", "prettier": "^3.0.0", "typescript": "4.4.2", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index d7ea662..fd26d58 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -13,10 +13,10 @@ importers: version: 2.53.2(react@18.2.0) '@douyinfe/semi-ui': specifier: ^2.55.3 - version: 2.55.3(react-dom@18.2.0)(react@18.2.0) + version: 2.55.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@visactor/react-vchart': specifier: ~1.8.8 - version: 1.8.11(react-dom@18.2.0)(react@18.2.0) + version: 1.8.11(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@visactor/vchart': specifier: ~1.8.8 version: 1.8.11 @@ -49,26 +49,26 @@ importers: version: 1.0.4 react-router-dom: specifier: ^6.3.0 - version: 6.22.2(react-dom@18.2.0)(react@18.2.0) + version: 6.22.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-telegram-login: specifier: ^1.1.2 version: 1.1.2(react@18.2.0) react-toastify: specifier: ^9.0.8 - version: 9.1.3(react-dom@18.2.0)(react@18.2.0) + version: 9.1.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-turnstile: specifier: ^1.0.5 - version: 1.1.3(react-dom@18.2.0)(react@18.2.0) + version: 1.1.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) semantic-ui-offline: specifier: ^2.5.0 version: 2.5.0 semantic-ui-react: specifier: ^2.1.3 - version: 2.1.5(react-dom@18.2.0)(react@18.2.0) + version: 2.1.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) devDependencies: '@so1ve/prettier-config': - specifier: ^2.0.0 - version: 2.0.0(prettier@3.2.5) + specifier: ^3.1.0 + version: 3.1.0(prettier@3.2.5) '@vitejs/plugin-react': specifier: ^4.2.1 version: 4.2.1(vite@5.2.5) @@ -88,8 +88,8 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@astrojs/compiler@1.8.2': - resolution: {integrity: sha512-o/ObKgtMzl8SlpIdzaxFnt7SATKPxu4oIP/1NL+HDJRzxfJcAkOTAb/ZKMRyULbz4q+1t2/DAebs2Z1QairkZw==} + '@astrojs/compiler@2.10.2': + resolution: {integrity: sha512-bvH+v8AirwpRWCkYJEyWYdc5Cs/BjG2ZTxIJzttHilXgfKJAdW2496KsUQKzf5j2tOHtaHXKKn9hb9WZiBGpEg==} '@babel/code-frame@7.23.5': resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} @@ -578,13 +578,13 @@ packages: react: ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 - '@so1ve/prettier-config@2.0.0': - resolution: {integrity: sha512-s6qsH5Rf4Bl+J0LU9rKmSWe/rYRdsYw0ELyXhDDDqEaTWtah4NpHKJuVWARuKqj0TWLBeWmyWUoIH/Bkp/DHaw==} + '@so1ve/prettier-config@3.1.0': + resolution: {integrity: sha512-9GJ1yXKBC4DzqCTTaZoBf8zw7WWkVuXcccZt1Aqk4lj6ab/GiNUnjPGajUVYLjaqAEOKqM7jUSUfTjk2JTjCAg==} peerDependencies: prettier: ^3.0.0 - '@so1ve/prettier-plugin-toml@2.0.0': - resolution: {integrity: sha512-GvuFdTqhs3qxbhKTiCXWMXITmNLSdndUp7ql1yJbzzWaGqAdb3UH+R+0ZhtAEctBSx90MWAWW3kkW/Iba02tCg==} + '@so1ve/prettier-plugin-toml@3.1.0': + resolution: {integrity: sha512-8WZAGjAVNIJlkfWL6wHKxlUuEBY45fdd5qY5bR/Z6r/txgzKXk/r9qi1DTwc17gi/WcNuRrcRugecRT+mWbIYg==} peerDependencies: prettier: ^3.0.0 @@ -1127,12 +1127,12 @@ packages: resolution: {integrity: sha512-WxtodH/wWavfw3MR7yK/GrS4pASEQ+iSTkdtSxPJWvqzG55ir5nvbLt9rw5AOiEcqqPCRM92WCtR1rk3TG3JSQ==} hasBin: true - prettier-plugin-astro@0.13.0: - resolution: {integrity: sha512-5HrJNnPmZqTUNoA97zn4gNQv9BgVhv+et03314WpQ9H9N8m2L9OSV798olwmG2YLXPl1iSstlJCR1zB3x5xG4g==} + prettier-plugin-astro@0.14.1: + resolution: {integrity: sha512-RiBETaaP9veVstE4vUwSIcdATj6dKmXljouXc/DDNwBSPTp8FRkLGDSGFClKsAFeeg+13SB0Z1JZvbD76bigJw==} engines: {node: ^14.15.0 || >=16.0.0} - prettier-plugin-curly-and-jsdoc@2.0.0: - resolution: {integrity: sha512-uSjWOWmX8+yrCrfhJSI58ODqtX7lXx07M8JYeOC1hfRv+vCttfiDlZoM27mNChGitJNKI+pCBvMMBYh8JiV0HQ==} + prettier-plugin-curly-and-jsdoc@3.1.0: + resolution: {integrity: sha512-4QMOHnLlkP2jTRWS0MFH6j+cuOiXLvXOqCLKbtwwVd8PPyq8NenW5AAwfwqiTNHBQG/DmzViPphRrwgN0XkUVQ==} peerDependencies: prettier: ^3.0.0 @@ -1442,7 +1442,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.24 - '@astrojs/compiler@1.8.2': {} + '@astrojs/compiler@2.10.2': {} '@babel/code-frame@7.23.5': dependencies: @@ -1590,7 +1590,7 @@ snapshots: react: 18.2.0 tslib: 2.6.2 - '@dnd-kit/core@6.1.0(react-dom@18.2.0)(react@18.2.0)': + '@dnd-kit/core@6.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@dnd-kit/accessibility': 3.1.0(react@18.2.0) '@dnd-kit/utilities': 3.2.2(react@18.2.0) @@ -1598,9 +1598,9 @@ snapshots: react-dom: 18.2.0(react@18.2.0) tslib: 2.6.2 - '@dnd-kit/sortable@7.0.2(@dnd-kit/core@6.1.0)(react@18.2.0)': + '@dnd-kit/sortable@7.0.2(@dnd-kit/core@6.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)': dependencies: - '@dnd-kit/core': 6.1.0(react-dom@18.2.0)(react@18.2.0) + '@dnd-kit/core': 6.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@dnd-kit/utilities': 3.2.2(react@18.2.0) react: 18.2.0 tslib: 2.6.2 @@ -1652,10 +1652,10 @@ snapshots: dependencies: glob: 7.2.3 - '@douyinfe/semi-ui@2.55.3(react-dom@18.2.0)(react@18.2.0)': + '@douyinfe/semi-ui@2.55.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: - '@dnd-kit/core': 6.1.0(react-dom@18.2.0)(react@18.2.0) - '@dnd-kit/sortable': 7.0.2(@dnd-kit/core@6.1.0)(react@18.2.0) + '@dnd-kit/core': 6.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@dnd-kit/sortable': 7.0.2(@dnd-kit/core@6.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0) '@dnd-kit/utilities': 3.2.2(react@18.2.0) '@douyinfe/semi-animation': 2.55.3 '@douyinfe/semi-animation-react': 2.55.3 @@ -1673,8 +1673,8 @@ snapshots: prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-resizable: 3.0.5(react-dom@18.2.0)(react@18.2.0) - react-window: 1.8.10(react-dom@18.2.0)(react@18.2.0) + react-resizable: 3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react-window: 1.8.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0) scroll-into-view-if-needed: 2.2.31 utility-types: 3.11.0 @@ -1747,13 +1747,13 @@ snapshots: '@esbuild/win32-x64@0.20.2': optional: true - '@fluentui/react-component-event-listener@0.63.1(react-dom@18.2.0)(react@18.2.0)': + '@fluentui/react-component-event-listener@0.63.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.0 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@fluentui/react-component-ref@0.63.1(react-dom@18.2.0)(react@18.2.0)': + '@fluentui/react-component-ref@0.63.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.24.0 react: 18.2.0 @@ -1871,22 +1871,22 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.13.0': optional: true - '@semantic-ui-react/event-stack@3.1.3(react-dom@18.2.0)(react@18.2.0)': + '@semantic-ui-react/event-stack@3.1.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: exenv: 1.2.2 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - '@so1ve/prettier-config@2.0.0(prettier@3.2.5)': + '@so1ve/prettier-config@3.1.0(prettier@3.2.5)': dependencies: - '@so1ve/prettier-plugin-toml': 2.0.0(prettier@3.2.5) + '@so1ve/prettier-plugin-toml': 3.1.0(prettier@3.2.5) prettier: 3.2.5 - prettier-plugin-astro: 0.13.0 - prettier-plugin-curly-and-jsdoc: 2.0.0(prettier@3.2.5) + prettier-plugin-astro: 0.14.1 + prettier-plugin-curly-and-jsdoc: 3.1.0(prettier@3.2.5) prettier-plugin-pkgsort: 0.2.1(prettier@3.2.5) - '@so1ve/prettier-plugin-toml@2.0.0(prettier@3.2.5)': + '@so1ve/prettier-plugin-toml@3.1.0(prettier@3.2.5)': dependencies: prettier: 3.2.5 @@ -1951,7 +1951,7 @@ snapshots: '@types/parse-json@4.0.2': {} - '@visactor/react-vchart@1.8.11(react-dom@18.2.0)(react@18.2.0)': + '@visactor/react-vchart@1.8.11(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@visactor/vchart': 1.8.11 '@visactor/vgrammar-core': 0.10.11 @@ -2528,13 +2528,13 @@ snapshots: sort-object-keys: 1.1.3 sort-order: 1.1.2 - prettier-plugin-astro@0.13.0: + prettier-plugin-astro@0.14.1: dependencies: - '@astrojs/compiler': 1.8.2 + '@astrojs/compiler': 2.10.2 prettier: 3.2.5 sass-formatter: 0.7.9 - prettier-plugin-curly-and-jsdoc@2.0.0(prettier@3.2.5): + prettier-plugin-curly-and-jsdoc@3.1.0(prettier@3.2.5): dependencies: prettier: 3.2.5 @@ -2559,7 +2559,7 @@ snapshots: react: 18.2.0 scheduler: 0.23.0 - react-draggable@4.4.6(react-dom@18.2.0)(react@18.2.0): + react-draggable@4.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: clsx: 1.2.1 prop-types: 15.8.1 @@ -2581,7 +2581,7 @@ snapshots: react-is@18.2.0: {} - react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0)(react@18.2.0): + react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@popperjs/core': 2.11.8 react: 18.2.0 @@ -2591,15 +2591,15 @@ snapshots: react-refresh@0.14.0: {} - react-resizable@3.0.5(react-dom@18.2.0)(react@18.2.0): + react-resizable@3.0.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: prop-types: 15.8.1 react: 18.2.0 - react-draggable: 4.4.6(react-dom@18.2.0)(react@18.2.0) + react-draggable: 4.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0) transitivePeerDependencies: - react-dom - react-router-dom@6.22.2(react-dom@18.2.0)(react@18.2.0): + react-router-dom@6.22.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@remix-run/router': 1.15.2 react: 18.2.0 @@ -2615,18 +2615,18 @@ snapshots: dependencies: react: 18.2.0 - react-toastify@9.1.3(react-dom@18.2.0)(react@18.2.0): + react-toastify@9.1.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: clsx: 1.2.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-turnstile@1.1.3(react-dom@18.2.0)(react@18.2.0): + react-turnstile@1.1.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - react-window@1.8.10(react-dom@18.2.0)(react@18.2.0): + react-window@1.8.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime': 7.24.0 memoize-one: 5.2.1 @@ -2708,13 +2708,13 @@ snapshots: fs-extra: 4.0.3 jquery: 3.7.1 - semantic-ui-react@2.1.5(react-dom@18.2.0)(react@18.2.0): + semantic-ui-react@2.1.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@babel/runtime': 7.24.0 - '@fluentui/react-component-event-listener': 0.63.1(react-dom@18.2.0)(react@18.2.0) - '@fluentui/react-component-ref': 0.63.1(react-dom@18.2.0)(react@18.2.0) + '@fluentui/react-component-event-listener': 0.63.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@fluentui/react-component-ref': 0.63.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@popperjs/core': 2.11.8 - '@semantic-ui-react/event-stack': 3.1.3(react-dom@18.2.0)(react@18.2.0) + '@semantic-ui-react/event-stack': 3.1.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0) clsx: 1.2.1 keyboard-key: 1.1.0 lodash: 4.17.21 @@ -2723,7 +2723,7 @@ snapshots: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-is: 18.2.0 - react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0)(react@18.2.0) + react-popper: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) shallowequal: 1.1.0 semver@6.3.1: {} From a2af637e7fae794ebd0e7fe0db88ddffd4ab8331 Mon Sep 17 00:00:00 2001 From: OswinWu Date: Sun, 11 Aug 2024 11:21:34 +0800 Subject: [PATCH 02/17] =?UTF-8?q?fix:=20log=E5=88=86=E9=A1=B5=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/log.go | 17 +++++++++++------ model/log.go | 16 ++++++++++++---- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/controller/log.go b/controller/log.go index 3902810..b90c2b5 100644 --- a/controller/log.go +++ b/controller/log.go @@ -1,18 +1,19 @@ package controller import ( - "github.com/gin-gonic/gin" "net/http" "one-api/common" "one-api/model" "strconv" + + "github.com/gin-gonic/gin" ) func GetAllLogs(c *gin.Context) { p, _ := strconv.Atoi(c.Query("p")) pageSize, _ := strconv.Atoi(c.Query("page_size")) - if p < 0 { - p = 0 + if p < 1 { + p = 1 } if pageSize < 0 { pageSize = common.ItemsPerPage @@ -24,7 +25,7 @@ func GetAllLogs(c *gin.Context) { tokenName := c.Query("token_name") modelName := c.Query("model_name") channel, _ := strconv.Atoi(c.Query("channel")) - logs, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, p*pageSize, pageSize, channel) + logs, total, err := model.GetAllLogs(logType, startTimestamp, endTimestamp, modelName, username, tokenName, (p-1)*pageSize, pageSize, channel) if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, @@ -35,9 +36,13 @@ func GetAllLogs(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", - "data": logs, + "data": map[string]any{ + "items": logs, + "total": total, + "page": p, + "page_size": pageSize, + }, }) - return } func GetUserLogs(c *gin.Context) { diff --git a/model/log.go b/model/log.go index d2e5b84..f907f43 100644 --- a/model/log.go +++ b/model/log.go @@ -3,11 +3,12 @@ package model import ( "context" "fmt" - "github.com/bytedance/gopkg/util/gopool" - "gorm.io/gorm" "one-api/common" "strings" "time" + + "github.com/bytedance/gopkg/util/gopool" + "gorm.io/gorm" ) type Log struct { @@ -95,7 +96,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke } } -func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int) (logs []*Log, err error) { +func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int) (logs []*Log, total int64, err error) { var tx *gorm.DB if logType == LogTypeUnknown { tx = DB @@ -120,8 +121,15 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName if channel != 0 { tx = tx.Where("channel_id = ?", channel) } + err = tx.Model(&Log{}).Count(&total).Error + if err != nil { + return nil, 0, err + } err = tx.Order("id desc").Limit(num).Offset(startIdx).Find(&logs).Error - return logs, err + if err != nil { + return nil, 0, err + } + return logs, total, err } func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int64, modelName string, tokenName string, startIdx int, num int) (logs []*Log, err error) { From 9edb7c4adef44e750f300b134bef13b0059cd6ef Mon Sep 17 00:00:00 2001 From: QuentinHsu Date: Sun, 11 Aug 2024 11:25:32 +0800 Subject: [PATCH 03/17] fix: log pagination --- web/src/components/LogsTable.js | 133 +++++++++++--------------------- 1 file changed, 46 insertions(+), 87 deletions(-) diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js index 2557c4b..072f2ad 100644 --- a/web/src/components/LogsTable.js +++ b/web/src/components/LogsTable.js @@ -1,11 +1,12 @@ import React, { useEffect, useState } from 'react'; import { API, - copy, getTodayStartTimestamp, + copy, + getTodayStartTimestamp, isAdmin, showError, showSuccess, - timestamp2string + timestamp2string, } from '../helpers'; import { @@ -29,7 +30,7 @@ import { stringToColor, } from '../helpers/render'; import Paragraph from '@douyinfe/semi-ui/lib/es/typography/paragraph'; -import {getLogOther} from "../helpers/other.js"; +import { getLogOther } from '../helpers/other.js'; const { Header } = Layout; @@ -144,27 +145,27 @@ function renderUseTime(type) { function renderFirstUseTime(type) { let time = parseFloat(type) / 1000.0; - time = time.toFixed(1) + time = time.toFixed(1); if (time < 3) { return ( - - {' '} - {time} s{' '} - + + {' '} + {time} s{' '} + ); } else if (time < 10) { return ( - - {' '} - {time} s{' '} - + + {' '} + {time} s{' '} + ); } else { return ( - - {' '} - {time} s{' '} - + + {' '} + {time} s{' '} + ); } } @@ -281,22 +282,22 @@ const LogsTable = () => { if (record.is_stream) { let other = getLogOther(record.other); return ( -
- - {renderUseTime(text)} - {renderFirstUseTime(other.frt)} - {renderIsStream(record.is_stream)} - -
+
+ + {renderUseTime(text)} + {renderFirstUseTime(other.frt)} + {renderIsStream(record.is_stream)} + +
); } else { return ( -
- - {renderUseTime(text)} - {renderIsStream(record.is_stream)} - -
+
+ + {renderUseTime(text)} + {renderIsStream(record.is_stream)} + +
); } }, @@ -344,7 +345,7 @@ const LogsTable = () => { if (record.other !== '') { let other = JSON.parse(record.other); if (other === null) { - return <> + return <>; } if (other.admin_info !== undefined) { if ( @@ -414,8 +415,6 @@ const LogsTable = () => { const [activePage, setActivePage] = useState(1); const [logCount, setLogCount] = useState(ITEMS_PER_PAGE); const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE); - const [searchKeyword, setSearchKeyword] = useState(''); - const [searching, setSearching] = useState(false); const [logType, setLogType] = useState(0); const isAdminUser = isAdmin(); let now = new Date(); @@ -451,9 +450,7 @@ const LogsTable = () => { let localEndTimestamp = Date.parse(end_timestamp) / 1000; let url = `/api/log/self/stat?type=${logType}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}`; url = encodeURI(url); - let res = await API.get( - url, - ); + let res = await API.get(url); const { success, message, data } = res.data; if (success) { setStat(data); @@ -467,9 +464,7 @@ const LogsTable = () => { let localEndTimestamp = Date.parse(end_timestamp) / 1000; let url = `/api/log/stat?type=${logType}&username=${username}&token_name=${token_name}&model_name=${model_name}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&channel=${channel}`; url = encodeURI(url); - let res = await API.get( - url, - ); + let res = await API.get(url); const { success, message, data } = res.data; if (success) { setStat(data); @@ -521,10 +516,7 @@ const LogsTable = () => { logs[i].timestamp2string = timestamp2string(logs[i].created_at); logs[i].key = '' + logs[i].id; } - // data.key = '' + data.id setLogs(logs); - setLogCount(logs.length + ITEMS_PER_PAGE); - // console.log(logCount); }; const loadLogs = async (startIdx, pageSize, logType = 0) => { @@ -542,37 +534,28 @@ const LogsTable = () => { const res = await API.get(url); const { success, message, data } = res.data; if (success) { - if (startIdx === 0) { - setLogsFormat(data); - } else { - let newLogs = [...logs]; - newLogs.splice(startIdx * pageSize, data.length, ...data); - setLogsFormat(newLogs); - } + const newPageData = data.items; + setActivePage(data.page); + setPageSize(data.page_size); + setLogCount(data.total); + + setLogsFormat(newPageData); } else { showError(message); } setLoading(false); }; - const pageData = logs.slice( - (activePage - 1) * pageSize, - activePage * pageSize, - ); - const handlePageChange = (page) => { setActivePage(page); - if (page === Math.ceil(logs.length / pageSize) + 1) { - // In this case we have to load more data and then append them. - loadLogs(page - 1, pageSize, logType).then((r) => {}); - } + loadLogs(page, pageSize, logType).then((r) => {}); }; const handlePageSizeChange = async (size) => { localStorage.setItem('page-size', size + ''); setPageSize(size); setActivePage(1); - loadLogs(0, size) + loadLogs(activePage, size) .then() .catch((reason) => { showError(reason); @@ -580,27 +563,24 @@ const LogsTable = () => { }; const refresh = async () => { - // setLoading(true); setActivePage(1); handleEyeClick(); - await loadLogs(0, pageSize, logType); + await loadLogs(activePage, pageSize, logType); }; const copyText = async (text) => { if (await copy(text)) { showSuccess('已复制:' + text); } else { - // setSearchKeyword(text); Modal.error({ title: '无法复制到剪贴板,请手动复制', content: text }); } }; useEffect(() => { - // console.log('default effect') const localPageSize = parseInt(localStorage.getItem('page-size')) || ITEMS_PER_PAGE; setPageSize(localPageSize); - loadLogs(0, localPageSize) + loadLogs(activePage, localPageSize) .then() .catch((reason) => { showError(reason); @@ -608,25 +588,6 @@ const LogsTable = () => { handleEyeClick(); }, []); - const searchLogs = async () => { - if (searchKeyword === '') { - // if keyword is blank, load files instead. - await loadLogs(0, pageSize); - setActivePage(1); - return; - } - setSearching(true); - const res = await API.get(`/api/log/self/search?keyword=${searchKeyword}`); - const { success, message, data } = res.data; - if (success) { - setLogs(data); - setActivePage(1); - } else { - showError(message); - } - setSearching(false); - }; - return ( <> @@ -719,15 +680,13 @@ const LogsTable = () => { > 查询 - - - + { pageSizeOpts: [10, 20, 50, 100], showSizeChanger: true, onPageSizeChange: (size) => { - handlePageSizeChange(size).then(); + handlePageSizeChange(size); }, onPageChange: handlePageChange, }} From 0c01f49bc5822599e1f65041d8510d8a33b1d59d Mon Sep 17 00:00:00 2001 From: liuzhifei <2679431923@qq.com> Date: Tue, 13 Aug 2024 10:28:35 +0800 Subject: [PATCH 04/17] add log db --- main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main.go b/main.go index efee772..c7d6942 100644 --- a/main.go +++ b/main.go @@ -42,6 +42,11 @@ func main() { if err != nil { common.FatalLog("failed to initialize database: " + err.Error()) } + // Initialize SQL Database + err = model.InitLogDB() + if err != nil { + common.FatalLog("failed to initialize database: " + err.Error()) + } defer func() { err := model.CloseDB() if err != nil { From d82bd2035466fdf1af40ac1353a2aa984caccd2c Mon Sep 17 00:00:00 2001 From: liuzhifei <2679431923@qq.com> Date: Tue, 13 Aug 2024 10:29:55 +0800 Subject: [PATCH 05/17] support log db --- model/log.go | 26 ++++---- model/main.go | 173 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 134 insertions(+), 65 deletions(-) diff --git a/model/log.go b/model/log.go index 1076145..79cc71b 100644 --- a/model/log.go +++ b/model/log.go @@ -38,7 +38,7 @@ const ( ) func GetLogByKey(key string) (logs []*Log, err error) { - err = DB.Joins("left join tokens on tokens.id = logs.token_id").Where("tokens.key = ?", strings.TrimPrefix(key, "sk-")).Find(&logs).Error + err = LOG_DB.Joins("left join tokens on tokens.id = logs.token_id").Where("tokens.key = ?", strings.TrimPrefix(key, "sk-")).Find(&logs).Error return logs, err } @@ -54,7 +54,7 @@ func RecordLog(userId int, logType int, content string) { Type: logType, Content: content, } - err := DB.Create(log).Error + err := LOG_DB.Create(log).Error if err != nil { common.SysError("failed to record log: " + err.Error()) } @@ -84,7 +84,7 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke IsStream: isStream, Other: otherStr, } - err := DB.Create(log).Error + err := LOG_DB.Create(log).Error if err != nil { common.LogError(ctx, "failed to record log: "+err.Error()) } @@ -98,9 +98,9 @@ func RecordConsumeLog(ctx context.Context, userId int, channelId int, promptToke func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, startIdx int, num int, channel int) (logs []*Log, err error) { var tx *gorm.DB if logType == LogTypeUnknown { - tx = DB + tx = LOG_DB } else { - tx = DB.Where("type = ?", logType) + tx = LOG_DB.Where("type = ?", logType) } if modelName != "" { tx = tx.Where("model_name like ?", modelName) @@ -127,9 +127,9 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int64, modelName string, tokenName string, startIdx int, num int) (logs []*Log, err error) { var tx *gorm.DB if logType == LogTypeUnknown { - tx = DB.Where("user_id = ?", userId) + tx = LOG_DB.Where("user_id = ?", userId) } else { - tx = DB.Where("user_id = ? and type = ?", userId, logType) + tx = LOG_DB.Where("user_id = ? and type = ?", userId, logType) } if modelName != "" { tx = tx.Where("model_name like ?", modelName) @@ -157,12 +157,12 @@ func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int } func SearchAllLogs(keyword string) (logs []*Log, err error) { - err = DB.Where("type = ? or content LIKE ?", keyword, keyword+"%").Order("id desc").Limit(common.MaxRecentItems).Find(&logs).Error + err = LOG_DB.Where("type = ? or content LIKE ?", keyword, keyword+"%").Order("id desc").Limit(common.MaxRecentItems).Find(&logs).Error return logs, err } func SearchUserLogs(userId int, keyword string) (logs []*Log, err error) { - err = DB.Where("user_id = ? and type = ?", userId, keyword).Order("id desc").Limit(common.MaxRecentItems).Omit("id").Find(&logs).Error + err = LOG_DB.Where("user_id = ? and type = ?", userId, keyword).Order("id desc").Limit(common.MaxRecentItems).Omit("id").Find(&logs).Error return logs, err } @@ -173,10 +173,10 @@ type Stat struct { } func SumUsedQuota(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, channel int) (stat Stat) { - tx := DB.Table("logs").Select("sum(quota) quota") + tx := LOG_DB.Table("logs").Select("sum(quota) quota") // 为rpm和tpm创建单独的查询 - rpmTpmQuery := DB.Table("logs").Select("count(*) rpm, sum(prompt_tokens) + sum(completion_tokens) tpm") + rpmTpmQuery := LOG_DB.Table("logs").Select("count(*) rpm, sum(prompt_tokens) + sum(completion_tokens) tpm") if username != "" { tx = tx.Where("username = ?", username) @@ -215,7 +215,7 @@ func SumUsedQuota(logType int, startTimestamp int64, endTimestamp int64, modelNa } func SumUsedToken(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string) (token int) { - tx := DB.Table("logs").Select("ifnull(sum(prompt_tokens),0) + ifnull(sum(completion_tokens),0)") + tx := LOG_DB.Table("logs").Select("ifnull(sum(prompt_tokens),0) + ifnull(sum(completion_tokens),0)") if username != "" { tx = tx.Where("username = ?", username) } @@ -236,6 +236,6 @@ func SumUsedToken(logType int, startTimestamp int64, endTimestamp int64, modelNa } func DeleteOldLog(targetTimestamp int64) (int64, error) { - result := DB.Where("created_at < ?", targetTimestamp).Delete(&Log{}) + result := LOG_DB.Where("created_at < ?", targetTimestamp).Delete(&Log{}) return result.RowsAffected, result.Error } diff --git a/model/main.go b/model/main.go index a70f21b..01eb6c9 100644 --- a/model/main.go +++ b/model/main.go @@ -15,6 +15,8 @@ import ( var DB *gorm.DB +var LOG_DB *gorm.DB + func createRootAccountIfNeed() error { var user User //if user.Status != common.UserStatusEnabled { @@ -38,9 +40,9 @@ func createRootAccountIfNeed() error { return nil } -func chooseDB() (*gorm.DB, error) { - if os.Getenv("SQL_DSN") != "" { - dsn := os.Getenv("SQL_DSN") +func chooseDB(envName string) (*gorm.DB, error) { + dsn := os.Getenv(envName) + if dsn != "" { if strings.HasPrefix(dsn, "postgres://") { // Use PostgreSQL common.SysLog("using PostgreSQL as database") @@ -52,6 +54,13 @@ func chooseDB() (*gorm.DB, error) { PrepareStmt: true, // precompile SQL }) } + if strings.HasPrefix(dsn, "local") { + common.SysLog("SQL_DSN not set, using SQLite as database") + common.UsingSQLite = true + return gorm.Open(sqlite.Open(common.SQLitePath), &gorm.Config{ + PrepareStmt: true, // precompile SQL + }) + } // Use MySQL common.SysLog("using MySQL as database") // check parseTime @@ -76,7 +85,7 @@ func chooseDB() (*gorm.DB, error) { } func InitDB() (err error) { - db, err := chooseDB() + db, err := chooseDB("SQL_DSN") if err == nil { if common.DebugEnabled { db = db.Debug() @@ -100,52 +109,7 @@ func InitDB() (err error) { // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY status VARCHAR(20);") // TODO: delete this line when most users have upgraded //} common.SysLog("database migration started") - err = db.AutoMigrate(&Channel{}) - if err != nil { - return err - } - err = db.AutoMigrate(&Token{}) - if err != nil { - return err - } - err = db.AutoMigrate(&User{}) - if err != nil { - return err - } - err = db.AutoMigrate(&Option{}) - if err != nil { - return err - } - err = db.AutoMigrate(&Redemption{}) - if err != nil { - return err - } - err = db.AutoMigrate(&Ability{}) - if err != nil { - return err - } - err = db.AutoMigrate(&Log{}) - if err != nil { - return err - } - err = db.AutoMigrate(&Midjourney{}) - if err != nil { - return err - } - err = db.AutoMigrate(&TopUp{}) - if err != nil { - return err - } - err = db.AutoMigrate(&QuotaData{}) - if err != nil { - return err - } - err = db.AutoMigrate(&Task{}) - if err != nil { - return err - } - common.SysLog("database migrated") - err = createRootAccountIfNeed() + err = migrateDB() return err } else { common.FatalLog(err) @@ -153,8 +117,103 @@ func InitDB() (err error) { return err } -func CloseDB() error { - sqlDB, err := DB.DB() +func InitLogDB() (err error) { + if os.Getenv("LOG_SQL_DSN") == "" { + LOG_DB = DB + return + } + db, err := chooseDB("LOG_SQL_DSN") + if err == nil { + if common.DebugEnabled { + db = db.Debug() + } + LOG_DB = db + sqlDB, err := LOG_DB.DB() + if err != nil { + return err + } + sqlDB.SetMaxIdleConns(common.GetEnvOrDefault("SQL_MAX_IDLE_CONNS", 100)) + sqlDB.SetMaxOpenConns(common.GetEnvOrDefault("SQL_MAX_OPEN_CONNS", 1000)) + sqlDB.SetConnMaxLifetime(time.Second * time.Duration(common.GetEnvOrDefault("SQL_MAX_LIFETIME", 60))) + + if !common.IsMasterNode { + return nil + } + //if common.UsingMySQL { + // _, _ = sqlDB.Exec("DROP INDEX idx_channels_key ON channels;") // TODO: delete this line when most users have upgraded + // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY action VARCHAR(40);") // TODO: delete this line when most users have upgraded + // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY progress VARCHAR(30);") // TODO: delete this line when most users have upgraded + // _, _ = sqlDB.Exec("ALTER TABLE midjourneys MODIFY status VARCHAR(20);") // TODO: delete this line when most users have upgraded + //} + common.SysLog("database migration started") + err = migrateLOGDB() + return err + } else { + common.FatalLog(err) + } + return err +} + +func migrateDB() error { + err := DB.AutoMigrate(&Channel{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&Token{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&User{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&Option{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&Redemption{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&Ability{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&Log{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&Midjourney{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&TopUp{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&QuotaData{}) + if err != nil { + return err + } + err = DB.AutoMigrate(&Task{}) + if err != nil { + return err + } + common.SysLog("database migrated") + err = createRootAccountIfNeed() + return err +} + +func migrateLOGDB() error { + var err error + if err = LOG_DB.AutoMigrate(&Log{}); err != nil { + return err + } + return nil +} + +func closeDB(db *gorm.DB) error { + sqlDB, err := db.DB() if err != nil { return err } @@ -162,6 +221,16 @@ func CloseDB() error { return err } +func CloseDB() error { + if LOG_DB != DB { + err := closeDB(LOG_DB) + if err != nil { + return err + } + } + return closeDB(DB) +} + var ( lastPingTime time.Time pingMutex sync.Mutex From 6cb0eb4b3900529fb4dc3a5815e2ee65f8512980 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Tue, 13 Aug 2024 17:54:24 +0800 Subject: [PATCH 06/17] feat: update claude tools calling --- relay/channel/claude/dto.go | 6 +++--- relay/channel/claude/relay-claude.go | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/relay/channel/claude/dto.go b/relay/channel/claude/dto.go index e2a898e..8f289e3 100644 --- a/relay/channel/claude/dto.go +++ b/relay/channel/claude/dto.go @@ -31,9 +31,9 @@ type ClaudeMessage struct { } type Tool struct { - Name string `json:"name"` - Description string `json:"description,omitempty"` - InputSchema InputSchema `json:"input_schema"` + Name string `json:"name"` + Description string `json:"description,omitempty"` + InputSchema map[string]interface{} `json:"input_schema"` } type InputSchema struct { diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go index 031f825..47abe31 100644 --- a/relay/channel/claude/relay-claude.go +++ b/relay/channel/claude/relay-claude.go @@ -63,15 +63,21 @@ func RequestOpenAI2ClaudeMessage(textRequest dto.GeneralOpenAIRequest) (*ClaudeR for _, tool := range textRequest.Tools { if params, ok := tool.Function.Parameters.(map[string]any); ok { - claudeTools = append(claudeTools, Tool{ + claudeTool := Tool{ Name: tool.Function.Name, Description: tool.Function.Description, - InputSchema: InputSchema{ - Type: params["type"].(string), - Properties: params["properties"], - Required: params["required"], - }, - }) + } + claudeTool.InputSchema = make(map[string]interface{}) + claudeTool.InputSchema["type"] = params["type"].(string) + claudeTool.InputSchema["properties"] = params["properties"] + claudeTool.InputSchema["required"] = params["required"] + for s, a := range params { + if s == "type" || s == "properties" || s == "required" { + continue + } + claudeTool.InputSchema[s] = a + } + claudeTools = append(claudeTools, claudeTool) } } From 1988c418420b15da873a3d148ecfcf66a1969ff6 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Wed, 14 Aug 2024 15:47:08 +0800 Subject: [PATCH 07/17] feat: update chatgpt-4o-latest model ratio --- common/model-ratio.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/model-ratio.go b/common/model-ratio.go index 3bdd5f7..284fb80 100644 --- a/common/model-ratio.go +++ b/common/model-ratio.go @@ -37,6 +37,7 @@ var defaultModelRatio = map[string]float64{ "gpt-4-turbo-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 + "chatgpt-4o-latest": 2.5, // $0.01 / 1K tokens "gpt-4o": 2.5, // $0.01 / 1K tokens "gpt-4o-2024-05-13": 2.5, // $0.01 / 1K tokens "gpt-4o-2024-08-06": 1.25, // $0.01 / 1K tokens @@ -334,6 +335,9 @@ func GetCompletionRatio(name string) float64 { } return 2 } + if name == "chatgpt-4o-latest" { + return 3 + } if strings.Contains(name, "claude-instant-1") { return 3 } else if strings.Contains(name, "claude-2") { From f9392ca904674151e65e6278d11fbb4bc4f1a3a5 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Wed, 14 Aug 2024 15:49:33 +0800 Subject: [PATCH 08/17] =?UTF-8?q?feat:=20=E9=81=BF=E5=85=8D=E6=9A=B4?= =?UTF-8?q?=E9=9C=B2=E5=86=85=E9=83=A8=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/error.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/service/error.go b/service/error.go index 3410de8..c760134 100644 --- a/service/error.go +++ b/service/error.go @@ -28,13 +28,11 @@ func MidjourneyErrorWithStatusCodeWrapper(code int, desc string, statusCode int) // OpenAIErrorWrapper wraps an error into an OpenAIErrorWithStatusCode func OpenAIErrorWrapper(err error, code string, statusCode int) *dto.OpenAIErrorWithStatusCode { text := err.Error() - // 定义一个正则表达式匹配URL - if strings.Contains(text, "Post") || strings.Contains(text, "dial") { + lowerText := strings.ToLower(text) + if strings.Contains(lowerText, "post") || strings.Contains(lowerText, "dial") || strings.Contains(lowerText, "http") { common.SysLog(fmt.Sprintf("error: %s", text)) text = "请求上游地址失败" } - //避免暴露内部错误 - openAIError := dto.OpenAIError{ Message: text, Type: "new_api_error", @@ -113,14 +111,12 @@ func TaskErrorWrapperLocal(err error, code string, statusCode int) *dto.TaskErro func TaskErrorWrapper(err error, code string, statusCode int) *dto.TaskError { text := err.Error() - - // 定义一个正则表达式匹配URL - if strings.Contains(text, "Post") || strings.Contains(text, "dial") { + lowerText := strings.ToLower(text) + if strings.Contains(lowerText, "post") || strings.Contains(lowerText, "dial") || strings.Contains(lowerText, "http") { common.SysLog(fmt.Sprintf("error: %s", text)) text = "请求上游地址失败" } //避免暴露内部错误 - taskError := &dto.TaskError{ Code: code, Message: text, From 748e34fd10b3965e72599f8caef83b56780b79c5 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Wed, 14 Aug 2024 15:51:48 +0800 Subject: [PATCH 09/17] feat: update openai models list --- relay/channel/openai/constant.go | 1 + 1 file changed, 1 insertion(+) diff --git a/relay/channel/openai/constant.go b/relay/channel/openai/constant.go index 81eb93c..ac2d673 100644 --- a/relay/channel/openai/constant.go +++ b/relay/channel/openai/constant.go @@ -8,6 +8,7 @@ var ModelList = []string{ "gpt-4-32k", "gpt-4-32k-0613", "gpt-4-turbo-preview", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-vision-preview", + "chatgpt-4o-latest", "gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", "text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large", From 8cd8cc29bcbbfcab24ab443b20091fa051f2940c Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Wed, 14 Aug 2024 22:43:57 +0800 Subject: [PATCH 10/17] fix: log page 'Cannot read properties of undefined (reading 'length')' --- controller/log.go | 13 +++++++++---- model/log.go | 8 ++++++-- web/src/components/LogsTable.js | 1 + 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/controller/log.go b/controller/log.go index b90c2b5..ca0e1ec 100644 --- a/controller/log.go +++ b/controller/log.go @@ -48,8 +48,8 @@ func GetAllLogs(c *gin.Context) { func GetUserLogs(c *gin.Context) { p, _ := strconv.Atoi(c.Query("p")) pageSize, _ := strconv.Atoi(c.Query("page_size")) - if p < 0 { - p = 0 + if p < 1 { + p = 1 } if pageSize < 0 { pageSize = common.ItemsPerPage @@ -63,7 +63,7 @@ func GetUserLogs(c *gin.Context) { endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64) tokenName := c.Query("token_name") modelName := c.Query("model_name") - logs, err := model.GetUserLogs(userId, logType, startTimestamp, endTimestamp, modelName, tokenName, p*pageSize, pageSize) + logs, total, err := model.GetUserLogs(userId, logType, startTimestamp, endTimestamp, modelName, tokenName, (p-1)*pageSize, pageSize) if err != nil { c.JSON(http.StatusOK, gin.H{ "success": false, @@ -74,7 +74,12 @@ func GetUserLogs(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", - "data": logs, + "data": map[string]any{ + "items": logs, + "total": total, + "page": p, + "page_size": pageSize, + }, }) return } diff --git a/model/log.go b/model/log.go index 681a4be..5493a05 100644 --- a/model/log.go +++ b/model/log.go @@ -132,7 +132,7 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName return logs, total, err } -func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int64, modelName string, tokenName string, startIdx int, num int) (logs []*Log, err error) { +func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int64, modelName string, tokenName string, startIdx int, num int) (logs []*Log, total int64, err error) { var tx *gorm.DB if logType == LogTypeUnknown { tx = LOG_DB.Where("user_id = ?", userId) @@ -151,6 +151,10 @@ func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int if endTimestamp != 0 { tx = tx.Where("created_at <= ?", endTimestamp) } + err = tx.Model(&Log{}).Count(&total).Error + if err != nil { + return nil, 0, err + } err = tx.Order("id desc").Limit(num).Offset(startIdx).Omit("id").Find(&logs).Error for i := range logs { var otherMap map[string]interface{} @@ -161,7 +165,7 @@ func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int } logs[i].Other = common.MapToJsonStr(otherMap) } - return logs, err + return logs, total, err } func SearchAllLogs(keyword string) (logs []*Log, err error) { diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js index 072f2ad..0d19423 100644 --- a/web/src/components/LogsTable.js +++ b/web/src/components/LogsTable.js @@ -534,6 +534,7 @@ const LogsTable = () => { const res = await API.get(url); const { success, message, data } = res.data; if (success) { + console.log(data); const newPageData = data.items; setActivePage(data.page); setPageSize(data.page_size); From 0c46d0c7af0201ec27089f4a6d380319a553a47a Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Wed, 14 Aug 2024 22:44:33 +0800 Subject: [PATCH 11/17] chore: remove useless code --- web/src/components/LogsTable.js | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js index 0d19423..072f2ad 100644 --- a/web/src/components/LogsTable.js +++ b/web/src/components/LogsTable.js @@ -534,7 +534,6 @@ const LogsTable = () => { const res = await API.get(url); const { success, message, data } = res.data; if (success) { - console.log(data); const newPageData = data.items; setActivePage(data.page); setPageSize(data.page_size); From b3d8e3e9aea60b4c7183e584b7f1976095ada090 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Fri, 16 Aug 2024 14:59:32 +0800 Subject: [PATCH 12/17] fix: lobechat #430 --- web/src/components/TokensTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/TokensTable.js b/web/src/components/TokensTable.js index 6dbb31b..9cece1e 100644 --- a/web/src/components/TokensTable.js +++ b/web/src/components/TokensTable.js @@ -425,7 +425,7 @@ const TokensTable = () => { url = `opencat://team/join?domain=${encodedServerAddress}&token=sk-${key}`; break; case 'lobe': - url = `https://chat-preview.lobehub.com/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${encodedServerAddress}"}}}`; + url = `https://chat-preview.lobehub.com/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${encodedServerAddress}/v1"}}}`; break; case 'next-mj': url = From a5ec11e463edff47569df465a235ee49f1efb16e Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Fri, 16 Aug 2024 16:16:38 +0800 Subject: [PATCH 13/17] fix: add email missing Message-ID --- common/email.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/common/email.go b/common/email.go index 62c9048..b23bb23 100644 --- a/common/email.go +++ b/common/email.go @@ -9,6 +9,11 @@ import ( "time" ) +func generateMessageID() string { + domain := strings.Split(SMTPFrom, "@")[1] + return fmt.Sprintf("<%d.%s@%s>", time.Now().UnixNano(), GetRandomString(12), domain) +} + func SendEmail(subject string, receiver string, content string) error { if SMTPFrom == "" { // for compatibility SMTPFrom = SMTPAccount @@ -18,8 +23,9 @@ func SendEmail(subject string, receiver string, content string) error { "From: %s<%s>\r\n"+ "Subject: %s\r\n"+ "Date: %s\r\n"+ + "Message-ID: %s\r\n"+ // 添加 Message-ID 头 "Content-Type: text/html; charset=UTF-8\r\n\r\n%s\r\n", - receiver, SystemName, SMTPFrom, encodedSubject, time.Now().Format(time.RFC1123Z), content)) + receiver, SystemName, SMTPFrom, encodedSubject, time.Now().Format(time.RFC1123Z), generateMessageID(), content)) auth := smtp.PlainAuth("", SMTPAccount, SMTPToken, SMTPServer) addr := fmt.Sprintf("%s:%d", SMTPServer, SMTPPort) to := strings.Split(receiver, ";") From d0f76a5c61ded22f4ebb31b8d053d4ad10a24318 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Fri, 16 Aug 2024 17:25:03 +0800 Subject: [PATCH 14/17] feat: support gpt-4o-gizmo-* (close #436) --- common/model-ratio.go | 24 ++++++++++++++++-------- model/cache.go | 3 +++ relay/relay-text.go | 4 ++++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/common/model-ratio.go b/common/model-ratio.go index 284fb80..946ee46 100644 --- a/common/model-ratio.go +++ b/common/model-ratio.go @@ -23,10 +23,11 @@ const ( var defaultModelRatio = map[string]float64{ //"midjourney": 50, - "gpt-4-gizmo-*": 15, - "gpt-4-all": 15, - "gpt-4o-all": 15, - "gpt-4": 15, + "gpt-4-gizmo-*": 15, + "gpt-4o-gizmo-*": 2.5, + "gpt-4-all": 15, + "gpt-4o-all": 15, + "gpt-4": 15, //"gpt-4-0314": 15, //deprecated "gpt-4-0613": 15, "gpt-4-32k": 30, @@ -187,8 +188,8 @@ var defaultModelPrice = map[string]float64{ } var ( - modelPriceMap = make(map[string]float64) - modelPriceMapMutex = sync.RWMutex{} + modelPriceMap map[string]float64 = nil + modelPriceMapMutex = sync.RWMutex{} ) var ( modelRatioMap map[string]float64 = nil @@ -197,8 +198,9 @@ var ( var CompletionRatio map[string]float64 = nil var defaultCompletionRatio = map[string]float64{ - "gpt-4-gizmo-*": 2, - "gpt-4-all": 2, + "gpt-4-gizmo-*": 2, + "gpt-4o-gizmo-*": 3, + "gpt-4-all": 2, } func GetModelPriceMap() map[string]float64 { @@ -232,6 +234,9 @@ func GetModelPrice(name string, printErr bool) (float64, bool) { if strings.HasPrefix(name, "gpt-4-gizmo") { name = "gpt-4-gizmo-*" } + if strings.HasPrefix(name, "gpt-4o-gizmo") { + name = "gpt-4o-gizmo-*" + } price, ok := modelPriceMap[name] if !ok { if printErr { @@ -312,6 +317,9 @@ func GetCompletionRatio(name string) float64 { if strings.HasPrefix(name, "gpt-4-gizmo") { name = "gpt-4-gizmo-*" } + if strings.HasPrefix(name, "gpt-4o-gizmo") { + name = "gpt-4o-gizmo-*" + } if strings.HasPrefix(name, "gpt-3.5") { if name == "gpt-3.5-turbo" || strings.HasSuffix(name, "0125") { // https://openai.com/blog/new-embedding-models-and-api-updates diff --git a/model/cache.go b/model/cache.go index 2977bb6..7f5411e 100644 --- a/model/cache.go +++ b/model/cache.go @@ -270,6 +270,9 @@ func CacheGetRandomSatisfiedChannel(group string, model string, retry int) (*Cha if strings.HasPrefix(model, "gpt-4-gizmo") { model = "gpt-4-gizmo-*" } + if strings.HasPrefix(model, "gpt-4o-gizmo") { + model = "gpt-4o-gizmo-*" + } // if memory cache is disabled, get channel directly from database if !common.MemoryCacheEnabled { diff --git a/relay/relay-text.go b/relay/relay-text.go index 93d202d..7c6840f 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -354,6 +354,10 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelN logModel = "gpt-4-gizmo-*" logContent += fmt.Sprintf(",模型 %s", modelName) } + if strings.HasPrefix(logModel, "gpt-4o-gizmo") { + logModel = "gpt-4o-gizmo-*" + logContent += fmt.Sprintf(",模型 %s", modelName) + } if extraContent != "" { logContent += ", " + extraContent } From 7c4d9d225ee505457400cc4edc401f9f3555df9b Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Fri, 16 Aug 2024 18:27:26 +0800 Subject: [PATCH 15/17] feat: support SiliconFlow (close #437, close #403) --- common/constants.go | 2 + dto/rerank.go | 13 +-- relay/channel/siliconflow/adaptor.go | 80 +++++++++++++++++++ relay/channel/siliconflow/constant.go | 51 ++++++++++++ relay/channel/siliconflow/dto.go | 17 ++++ .../channel/siliconflow/relay-siliconflow.go | 44 ++++++++++ relay/constant/api_type.go | 3 + relay/relay-text.go | 2 +- relay/relay_adaptor.go | 3 + web/src/constants/channel.constants.js | 35 ++++---- 10 files changed, 227 insertions(+), 23 deletions(-) create mode 100644 relay/channel/siliconflow/adaptor.go create mode 100644 relay/channel/siliconflow/constant.go create mode 100644 relay/channel/siliconflow/dto.go create mode 100644 relay/channel/siliconflow/relay-siliconflow.go diff --git a/common/constants.go b/common/constants.go index 97e8583..d63955e 100644 --- a/common/constants.go +++ b/common/constants.go @@ -213,6 +213,7 @@ const ( ChannelTypeDify = 37 ChannelTypeJina = 38 ChannelCloudflare = 39 + ChannelTypeSiliconFlow = 40 ChannelTypeDummy // this one is only for count, do not add any channel after this @@ -259,4 +260,5 @@ var ChannelBaseURLs = []string{ "", //37 "https://api.jina.ai", //38 "https://api.cloudflare.com", //39 + "https://api.siliconflow.cn", //40 } diff --git a/dto/rerank.go b/dto/rerank.go index 0ee44b1..dfa7963 100644 --- a/dto/rerank.go +++ b/dto/rerank.go @@ -1,14 +1,17 @@ package dto type RerankRequest struct { - Documents []any `json:"documents"` - Query string `json:"query"` - Model string `json:"model"` - TopN int `json:"top_n"` + Documents []any `json:"documents"` + Query string `json:"query"` + Model string `json:"model"` + TopN int `json:"top_n"` + ReturnDocuments bool `json:"return_documents,omitempty"` + MaxChunkPerDoc int `json:"max_chunk_per_doc,omitempty"` + OverLapTokens int `json:"overlap_tokens,omitempty"` } type RerankResponseDocument struct { - Document any `json:"document"` + Document any `json:"document,omitempty"` Index int `json:"index"` RelevanceScore float64 `json:"relevance_score"` } diff --git a/relay/channel/siliconflow/adaptor.go b/relay/channel/siliconflow/adaptor.go new file mode 100644 index 0000000..1614905 --- /dev/null +++ b/relay/channel/siliconflow/adaptor.go @@ -0,0 +1,80 @@ +package siliconflow + +import ( + "errors" + "fmt" + "github.com/gin-gonic/gin" + "io" + "net/http" + "one-api/dto" + "one-api/relay/channel" + "one-api/relay/channel/openai" + relaycommon "one-api/relay/common" + "one-api/relay/constant" +) + +type Adaptor struct { +} + +func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) { + //TODO implement me + return nil, errors.New("not implemented") +} + +func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (any, error) { + //TODO implement me + return nil, errors.New("not implemented") +} + +func (a *Adaptor) Init(info *relaycommon.RelayInfo) { +} + +func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) { + if info.RelayMode == constant.RelayModeRerank { + return fmt.Sprintf("%s/v1/rerank", info.BaseUrl), nil + } else if info.RelayMode == constant.RelayModeEmbeddings { + return fmt.Sprintf("%s/v1/embeddings ", info.BaseUrl), nil + } else if info.RelayMode == constant.RelayModeChatCompletions { + return fmt.Sprintf("%s/v1/chat/completions", info.BaseUrl), nil + } + return "", errors.New("invalid relay mode") +} + +func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, info *relaycommon.RelayInfo) error { + channel.SetupApiRequestHeader(info, c, req) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", info.ApiKey)) + return nil +} + +func (a *Adaptor) ConvertRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) (any, error) { + return request, nil +} + +func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, requestBody io.Reader) (*http.Response, error) { + return channel.DoApiRequest(a, c, info, requestBody) +} + +func (a *Adaptor) ConvertRerankRequest(c *gin.Context, relayMode int, request dto.RerankRequest) (any, error) { + return request, nil +} + +func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage *dto.Usage, err *dto.OpenAIErrorWithStatusCode) { + if info.RelayMode == constant.RelayModeRerank { + err, usage = siliconflowRerankHandler(c, resp) + } else if info.RelayMode == constant.RelayModeChatCompletions { + if info.IsStream { + err, usage = openai.OaiStreamHandler(c, resp, info) + } else { + err, usage = openai.OpenaiHandler(c, resp, info.PromptTokens, info.UpstreamModelName) + } + } + return +} + +func (a *Adaptor) GetModelList() []string { + return ModelList +} + +func (a *Adaptor) GetChannelName() string { + return ChannelName +} diff --git a/relay/channel/siliconflow/constant.go b/relay/channel/siliconflow/constant.go new file mode 100644 index 0000000..819f4aa --- /dev/null +++ b/relay/channel/siliconflow/constant.go @@ -0,0 +1,51 @@ +package siliconflow + +var ModelList = []string{ + "THUDM/glm-4-9b-chat", + //"stabilityai/stable-diffusion-xl-base-1.0", + //"TencentARC/PhotoMaker", + "InstantX/InstantID", + //"stabilityai/stable-diffusion-2-1", + //"stabilityai/sd-turbo", + //"stabilityai/sdxl-turbo", + "ByteDance/SDXL-Lightning", + "deepseek-ai/deepseek-llm-67b-chat", + "Qwen/Qwen1.5-14B-Chat", + "Qwen/Qwen1.5-7B-Chat", + "Qwen/Qwen1.5-110B-Chat", + "Qwen/Qwen1.5-32B-Chat", + "01-ai/Yi-1.5-6B-Chat", + "01-ai/Yi-1.5-9B-Chat-16K", + "01-ai/Yi-1.5-34B-Chat-16K", + "THUDM/chatglm3-6b", + "deepseek-ai/DeepSeek-V2-Chat", + "Qwen/Qwen2-72B-Instruct", + "Qwen/Qwen2-7B-Instruct", + "Qwen/Qwen2-57B-A14B-Instruct", + //"stabilityai/stable-diffusion-3-medium", + "deepseek-ai/DeepSeek-Coder-V2-Instruct", + "Qwen/Qwen2-1.5B-Instruct", + "internlm/internlm2_5-7b-chat", + "BAAI/bge-large-en-v1.5", + "BAAI/bge-large-zh-v1.5", + "Pro/Qwen/Qwen2-7B-Instruct", + "Pro/Qwen/Qwen2-1.5B-Instruct", + "Pro/Qwen/Qwen1.5-7B-Chat", + "Pro/THUDM/glm-4-9b-chat", + "Pro/THUDM/chatglm3-6b", + "Pro/01-ai/Yi-1.5-9B-Chat-16K", + "Pro/01-ai/Yi-1.5-6B-Chat", + "Pro/google/gemma-2-9b-it", + "Pro/internlm/internlm2_5-7b-chat", + "Pro/meta-llama/Meta-Llama-3-8B-Instruct", + "Pro/mistralai/Mistral-7B-Instruct-v0.2", + "black-forest-labs/FLUX.1-schnell", + "iic/SenseVoiceSmall", + "netease-youdao/bce-embedding-base_v1", + "BAAI/bge-m3", + "internlm/internlm2_5-20b-chat", + "Qwen/Qwen2-Math-72B-Instruct", + "netease-youdao/bce-reranker-base_v1", + "BAAI/bge-reranker-v2-m3", +} +var ChannelName = "siliconflow" diff --git a/relay/channel/siliconflow/dto.go b/relay/channel/siliconflow/dto.go new file mode 100644 index 0000000..58cf81c --- /dev/null +++ b/relay/channel/siliconflow/dto.go @@ -0,0 +1,17 @@ +package siliconflow + +import "one-api/dto" + +type SFTokens struct { + InputTokens int `json:"input_tokens"` + OutputTokens int `json:"output_tokens"` +} + +type SFMeta struct { + Tokens SFTokens `json:"tokens"` +} + +type SFRerankResponse struct { + Results []dto.RerankResponseDocument `json:"results"` + Meta SFMeta `json:"meta"` +} diff --git a/relay/channel/siliconflow/relay-siliconflow.go b/relay/channel/siliconflow/relay-siliconflow.go new file mode 100644 index 0000000..a01e745 --- /dev/null +++ b/relay/channel/siliconflow/relay-siliconflow.go @@ -0,0 +1,44 @@ +package siliconflow + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "io" + "net/http" + "one-api/dto" + "one-api/service" +) + +func siliconflowRerankHandler(c *gin.Context, resp *http.Response) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) { + responseBody, err := io.ReadAll(resp.Body) + if err != nil { + return service.OpenAIErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil + } + err = resp.Body.Close() + if err != nil { + return service.OpenAIErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil + } + var siliconflowResp SFRerankResponse + err = json.Unmarshal(responseBody, &siliconflowResp) + if err != nil { + return service.OpenAIErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil + } + usage := &dto.Usage{ + PromptTokens: siliconflowResp.Meta.Tokens.InputTokens, + CompletionTokens: siliconflowResp.Meta.Tokens.OutputTokens, + TotalTokens: siliconflowResp.Meta.Tokens.InputTokens + siliconflowResp.Meta.Tokens.OutputTokens, + } + rerankResp := &dto.RerankResponse{ + Results: siliconflowResp.Results, + Usage: *usage, + } + + jsonResponse, err := json.Marshal(rerankResp) + if err != nil { + return service.OpenAIErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), nil + } + c.Writer.Header().Set("Content-Type", "application/json") + c.Writer.WriteHeader(resp.StatusCode) + _, err = c.Writer.Write(jsonResponse) + return nil, usage +} diff --git a/relay/constant/api_type.go b/relay/constant/api_type.go index 6bd93c4..36a6b6b 100644 --- a/relay/constant/api_type.go +++ b/relay/constant/api_type.go @@ -23,6 +23,7 @@ const ( APITypeDify APITypeJina APITypeCloudflare + APITypeSiliconFlow APITypeDummy // this one is only for count, do not add any channel after this ) @@ -66,6 +67,8 @@ func ChannelType2APIType(channelType int) (int, bool) { apiType = APITypeJina case common.ChannelCloudflare: apiType = APITypeCloudflare + case common.ChannelTypeSiliconFlow: + apiType = APITypeSiliconFlow } if apiType == -1 { return APITypeOpenAI, false diff --git a/relay/relay-text.go b/relay/relay-text.go index 7c6840f..3c5393a 100644 --- a/relay/relay-text.go +++ b/relay/relay-text.go @@ -317,7 +317,7 @@ func postConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelN totalTokens := promptTokens + completionTokens var logContent string if !usePrice { - logContent = fmt.Sprintf("模型倍率 %.2f,分组倍率 %.2f,补全倍率 %.2f", modelRatio, groupRatio, completionRatio) + logContent = fmt.Sprintf("模型倍率 %.2f,补全倍率 %.2f,分组倍率 %.2f", modelRatio, completionRatio, groupRatio) } else { logContent = fmt.Sprintf("模型价格 %.2f,分组倍率 %.2f", modelPrice, groupRatio) } diff --git a/relay/relay_adaptor.go b/relay/relay_adaptor.go index 4c0aef1..96fb737 100644 --- a/relay/relay_adaptor.go +++ b/relay/relay_adaptor.go @@ -16,6 +16,7 @@ import ( "one-api/relay/channel/openai" "one-api/relay/channel/palm" "one-api/relay/channel/perplexity" + "one-api/relay/channel/siliconflow" "one-api/relay/channel/task/suno" "one-api/relay/channel/tencent" "one-api/relay/channel/xunfei" @@ -62,6 +63,8 @@ func GetAdaptor(apiType int) channel.Adaptor { return &jina.Adaptor{} case constant.APITypeCloudflare: return &cloudflare.Adaptor{} + case constant.APITypeSiliconFlow: + return &siliconflow.Adaptor{} } return nil } diff --git a/web/src/constants/channel.constants.js b/web/src/constants/channel.constants.js index 88614b0..ee53f7b 100644 --- a/web/src/constants/channel.constants.js +++ b/web/src/constants/channel.constants.js @@ -5,21 +5,21 @@ export const CHANNEL_OPTIONS = [ text: 'Midjourney Proxy', value: 2, color: 'light-blue', - label: 'Midjourney Proxy', + label: 'Midjourney Proxy' }, { key: 5, text: 'Midjourney Proxy Plus', value: 5, color: 'blue', - label: 'Midjourney Proxy Plus', + label: 'Midjourney Proxy Plus' }, { key: 36, text: 'Suno API', value: 36, color: 'purple', - label: 'Suno API', + label: 'Suno API' }, { key: 4, text: 'Ollama', value: 4, color: 'grey', label: 'Ollama' }, { @@ -27,77 +27,77 @@ export const CHANNEL_OPTIONS = [ text: 'Anthropic Claude', value: 14, color: 'indigo', - label: 'Anthropic Claude', + label: 'Anthropic Claude' }, { key: 33, text: 'AWS Claude', value: 33, color: 'indigo', - label: 'AWS Claude', + label: 'AWS Claude' }, { key: 3, text: 'Azure OpenAI', value: 3, color: 'teal', - label: 'Azure OpenAI', + label: 'Azure OpenAI' }, { key: 24, text: 'Google Gemini', value: 24, color: 'orange', - label: 'Google Gemini', + label: 'Google Gemini' }, { key: 34, text: 'Cohere', value: 34, color: 'purple', - label: 'Cohere', + label: 'Cohere' }, { key: 15, text: '百度文心千帆', value: 15, color: 'blue', - label: '百度文心千帆', + label: '百度文心千帆' }, { key: 17, text: '阿里通义千问', value: 17, color: 'orange', - label: '阿里通义千问', + label: '阿里通义千问' }, { key: 18, text: '讯飞星火认知', value: 18, color: 'blue', - label: '讯飞星火认知', + label: '讯飞星火认知' }, { key: 16, text: '智谱 ChatGLM', value: 16, color: 'violet', - label: '智谱 ChatGLM', + label: '智谱 ChatGLM' }, { key: 26, text: '智谱 GLM-4V', value: 26, color: 'purple', - label: '智谱 GLM-4V', + label: '智谱 GLM-4V' }, { key: 11, text: 'Google PaLM2', value: 11, color: 'orange', - label: 'Google PaLM2', + label: 'Google PaLM2' }, { key: 39, text: 'Cloudflare', value: 39, color: 'grey', label: 'Cloudflare' }, { key: 25, text: 'Moonshot', value: 25, color: 'green', label: 'Moonshot' }, @@ -107,19 +107,20 @@ export const CHANNEL_OPTIONS = [ { key: 35, text: 'MiniMax', value: 35, color: 'green', label: 'MiniMax' }, { key: 37, text: 'Dify', value: 37, color: 'teal', label: 'Dify' }, { key: 38, text: 'Jina', value: 38, color: 'blue', label: 'Jina' }, + { key: 40, text: 'SiliconCloud', value: 40, color: 'purple', label: 'SiliconCloud' }, { key: 8, text: '自定义渠道', value: 8, color: 'pink', label: '自定义渠道' }, { key: 22, text: '知识库:FastGPT', value: 22, color: 'blue', - label: '知识库:FastGPT', + label: '知识库:FastGPT' }, { key: 21, text: '知识库:AI Proxy', value: 21, color: 'purple', - label: '知识库:AI Proxy', - }, + label: '知识库:AI Proxy' + } ]; From 484a8595e4558e4e5f2879367f2598e47e1e18a7 Mon Sep 17 00:00:00 2001 From: OswinWu Date: Fri, 23 Aug 2024 17:16:09 +0800 Subject: [PATCH 16/17] =?UTF-8?q?fix:=20=E5=A4=9A=E5=9C=B0=E5=8C=BAoutlook?= =?UTF-8?q?=E9=82=AE=E7=AE=B1=E5=92=8Cofb=E9=82=AE=E7=AE=B1Auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/email-outlook-auth.go | 8 ++++++++ common/email.go | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/common/email-outlook-auth.go b/common/email-outlook-auth.go index 723a10b..f6a71b8 100644 --- a/common/email-outlook-auth.go +++ b/common/email-outlook-auth.go @@ -3,6 +3,7 @@ package common import ( "errors" "net/smtp" + "strings" ) type outlookAuth struct { @@ -30,3 +31,10 @@ func (a *outlookAuth) Next(fromServer []byte, more bool) ([]byte, error) { } return nil, nil } + +func isOutlookServer(server string) bool { + // 兼容多地区的outlook邮箱和ofb邮箱 + // 其实应该加一个Option来区分是否用LOGIN的方式登录 + // 先临时兼容一下 + return strings.Contains(server, "outlook") || strings.Contains(server, "onmicrosoft") +} diff --git a/common/email.go b/common/email.go index b23bb23..905f385 100644 --- a/common/email.go +++ b/common/email.go @@ -68,7 +68,7 @@ func SendEmail(subject string, receiver string, content string) error { if err != nil { return err } - } else if strings.HasSuffix(SMTPAccount, "outlook.com") { + } else if isOutlookServer(SMTPAccount) { auth = LoginAuth(SMTPAccount, SMTPToken) err = smtp.SendMail(addr, auth, SMTPAccount, to, mail) } else { From 144513f1d841fd178403b1ec6b117f06b053b442 Mon Sep 17 00:00:00 2001 From: CalciumIon <1808837298@qq.com> Date: Fri, 23 Aug 2024 23:21:37 +0800 Subject: [PATCH 17/17] feat: rerank model mapping (close #444) --- relay/relay_rerank.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/relay/relay_rerank.go b/relay/relay_rerank.go index 9885fd3..4242155 100644 --- a/relay/relay_rerank.go +++ b/relay/relay_rerank.go @@ -38,6 +38,23 @@ func RerankHelper(c *gin.Context, relayMode int) *dto.OpenAIErrorWithStatusCode if len(rerankRequest.Documents) == 0 { return service.OpenAIErrorWrapperLocal(fmt.Errorf("documents is empty"), "invalid_documents", http.StatusBadRequest) } + + // map model name + modelMapping := c.GetString("model_mapping") + //isModelMapped := false + if modelMapping != "" && modelMapping != "{}" { + modelMap := make(map[string]string) + err := json.Unmarshal([]byte(modelMapping), &modelMap) + if err != nil { + return service.OpenAIErrorWrapperLocal(err, "unmarshal_model_mapping_failed", http.StatusInternalServerError) + } + if modelMap[rerankRequest.Model] != "" { + rerankRequest.Model = modelMap[rerankRequest.Model] + // set upstream model name + //isModelMapped = true + } + } + relayInfo.UpstreamModelName = rerankRequest.Model modelPrice, success := common.GetModelPrice(rerankRequest.Model, false) groupRatio := common.GetGroupRatio(relayInfo.Group)