From 94fa2810ccc26a40efdc4766c31836d74a63fcc6 Mon Sep 17 00:00:00 2001
From: Lilo <1622461+detecti1@users.noreply.github.com>
Date: Thu, 9 Jan 2025 15:20:12 +0800
Subject: [PATCH 1/5] Add channel name (tooltip / detail) to logs
---
model/log.go | 9 +++++++++
web/src/components/LogsTable.js | 25 ++++++++++++++++---------
2 files changed, 25 insertions(+), 9 deletions(-)
diff --git a/model/log.go b/model/log.go
index 4a633a6a..dd5a3162 100644
--- a/model/log.go
+++ b/model/log.go
@@ -27,6 +27,7 @@ type Log struct {
UseTime int `json:"use_time" gorm:"default:0"`
IsStream bool `json:"is_stream" gorm:"default:false"`
ChannelId int `json:"channel" gorm:"index"`
+ ChannelName string `json:"channel_name" gorm:"->"`
TokenId int `json:"token_id" gorm:"default:0;index"`
Group string `json:"group" gorm:"index"`
Other string `json:"other"`
@@ -130,6 +131,10 @@ func GetAllLogs(logType int, startTimestamp int64, endTimestamp int64, modelName
} else {
tx = LOG_DB.Where("type = ?", logType)
}
+
+ tx = tx.Joins("LEFT JOIN channels ON logs.channel_id = channels.id")
+ tx = tx.Select("logs.*, channels.name as channel_name")
+
if modelName != "" {
tx = tx.Where("model_name like ?", modelName)
}
@@ -169,6 +174,10 @@ func GetUserLogs(userId int, logType int, startTimestamp int64, endTimestamp int
} else {
tx = LOG_DB.Where("user_id = ? and type = ?", userId, logType)
}
+
+ tx = tx.Joins("LEFT JOIN channels ON logs.channel_id = channels.id")
+ tx = tx.Select("logs.*, channels.name as channel_name")
+
if modelName != "" {
tx = tx.Where("model_name like ?", modelName)
}
diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js
index 452585a1..6faee1df 100644
--- a/web/src/components/LogsTable.js
+++ b/web/src/components/LogsTable.js
@@ -157,13 +157,15 @@ const LogsTable = () => {
record.type === 0 || record.type === 2 ? (
{
-
- {' '}
- {text}{' '}
-
+
+
+ {' '}
+ {text}{' '}
+
+
}
) : (
@@ -543,6 +545,12 @@ const LogsTable = () => {
// key: '渠道重试',
// value: content,
// })
+ }
+ if (isAdminUser && (logs[i].type === 0 || logs[i].type === 2)) {
+ expandDataLocal.push({
+ key: t('渠道信息'),
+ value: `${logs[i].channel} - ${logs[i].channel_name || '[未知]'}`
+ });
}
if (other?.ws || other?.audio) {
expandDataLocal.push({
@@ -595,13 +603,12 @@ const LogsTable = () => {
key: t('计费过程'),
value: content,
});
- }
+ }
expandDatesLocal[logs[i].key] = expandDataLocal;
}
setExpandData(expandDatesLocal);
-
setLogs(logs);
};
From f7277933d418d020b6d1db94c302f5bb4e2c384b Mon Sep 17 00:00:00 2001
From: Lilo <1622461+detecti1@users.noreply.github.com>
Date: Thu, 9 Jan 2025 16:46:16 +0800
Subject: [PATCH 2/5] Fix JSON parsing error when record.other is empty string
---
web/src/components/LogsTable.js | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/web/src/components/LogsTable.js b/web/src/components/LogsTable.js
index 6faee1df..7acc2cc4 100644
--- a/web/src/components/LogsTable.js
+++ b/web/src/components/LogsTable.js
@@ -236,7 +236,12 @@ const LogsTable = () => {
>
);
} else {
- let other = JSON.parse(record.other);
+ let other = null;
+ try {
+ other = JSON.parse(record.other);
+ } catch (e) {
+ console.error(`Failed to parse record.other: "${record.other}".`, e);
+ }
if (other === null) {
return <>>;
}
From 2abf05b31409fbeeebcda8d33362577565e4827e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=B2=88=E6=B5=A9?=
Date: Fri, 17 Jan 2025 18:12:05 +0800
Subject: [PATCH 3/5] fix: incorrect whisper audio usage
---
Dockerfile | 2 +-
common/utils.go | 33 ++++++++++
relay/channel/openai/relay-openai.go | 97 +++++++++++++---------------
3 files changed, 78 insertions(+), 54 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index eebf982a..44a7837a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -24,7 +24,7 @@ FROM alpine
RUN apk update \
&& apk upgrade \
- && apk add --no-cache ca-certificates tzdata \
+ && apk add --no-cache ca-certificates tzdata ffmpeg\
&& update-ca-certificates 2>/dev/null || true
COPY --from=builder2 /build/one-api /
diff --git a/common/utils.go b/common/utils.go
index 26c8236b..fb769a7c 100644
--- a/common/utils.go
+++ b/common/utils.go
@@ -1,14 +1,19 @@
package common
import (
+ "bytes"
+ "context"
crand "crypto/rand"
"encoding/base64"
"fmt"
+ "github.com/pkg/errors"
"html/template"
+ "io"
"log"
"math/big"
"math/rand"
"net"
+ "os"
"os/exec"
"runtime"
"strconv"
@@ -207,3 +212,31 @@ func RandomSleep() {
// Sleep for 0-3000 ms
time.Sleep(time.Duration(rand.Intn(3000)) * time.Millisecond)
}
+
+// SaveTmpFile saves data to a temporary file. The filename would be apppended with a random string.
+func SaveTmpFile(filename string, data io.Reader) (string, error) {
+ f, err := os.CreateTemp(os.TempDir(), filename)
+ if err != nil {
+ return "", errors.Wrapf(err, "failed to create temporary file %s", filename)
+ }
+ defer f.Close()
+
+ _, err = io.Copy(f, data)
+ if err != nil {
+ return "", errors.Wrapf(err, "failed to copy data to temporary file %s", filename)
+ }
+
+ return f.Name(), nil
+}
+
+// GetAudioDuration returns the duration of an audio file in seconds.
+func GetAudioDuration(ctx context.Context, filename string) (float64, error) {
+ // ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {{input}}
+ c := exec.CommandContext(ctx, "ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", filename)
+ output, err := c.Output()
+ if err != nil {
+ return 0, errors.Wrap(err, "failed to get audio duration")
+ }
+
+ return strconv.ParseFloat(string(bytes.TrimSpace(output)), 64)
+}
diff --git a/relay/channel/openai/relay-openai.go b/relay/channel/openai/relay-openai.go
index d8b1aef3..537ccb32 100644
--- a/relay/channel/openai/relay-openai.go
+++ b/relay/channel/openai/relay-openai.go
@@ -5,7 +5,10 @@ import (
"bytes"
"encoding/json"
"fmt"
+ "github.com/pkg/errors"
"io"
+ "math"
+ "mime/multipart"
"net/http"
"one-api/common"
"one-api/constant"
@@ -13,6 +16,7 @@ import (
relaycommon "one-api/relay/common"
relayconstant "one-api/relay/constant"
"one-api/service"
+ "os"
"strings"
"sync"
"time"
@@ -316,6 +320,11 @@ func OpenaiTTSHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
}
func OpenaiSTTHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo, responseFormat string) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) {
+ // count tokens by audio file duration
+ audioTokens, err := countAudioTokens(c)
+ if err != nil {
+ return service.OpenAIErrorWrapper(err, "count_audio_tokens_failed", http.StatusInternalServerError), nil
+ }
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return service.OpenAIErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil
@@ -340,70 +349,52 @@ func OpenaiSTTHandler(c *gin.Context, resp *http.Response, info *relaycommon.Rel
}
resp.Body.Close()
- var text string
- switch responseFormat {
- case "json":
- text, err = getTextFromJSON(responseBody)
- case "text":
- text, err = getTextFromText(responseBody)
- case "srt":
- text, err = getTextFromSRT(responseBody)
- case "verbose_json":
- text, err = getTextFromVerboseJSON(responseBody)
- case "vtt":
- text, err = getTextFromVTT(responseBody)
- }
-
usage := &dto.Usage{}
- usage.PromptTokens = info.PromptTokens
- usage.CompletionTokens, _ = service.CountTextToken(text, info.UpstreamModelName)
+ usage.PromptTokens = audioTokens
+ usage.CompletionTokens = 0
usage.TotalTokens = usage.PromptTokens + usage.CompletionTokens
return nil, usage
}
-func getTextFromVTT(body []byte) (string, error) {
- return getTextFromSRT(body)
-}
-
-func getTextFromVerboseJSON(body []byte) (string, error) {
- var whisperResponse dto.WhisperVerboseJSONResponse
- if err := json.Unmarshal(body, &whisperResponse); err != nil {
- return "", fmt.Errorf("unmarshal_response_body_failed err :%w", err)
+func countAudioTokens(c *gin.Context) (int, error) {
+ body, err := common.GetRequestBody(c)
+ if err != nil {
+ return 0, errors.WithStack(err)
}
- return whisperResponse.Text, nil
-}
-func getTextFromSRT(body []byte) (string, error) {
- scanner := bufio.NewScanner(strings.NewReader(string(body)))
- var builder strings.Builder
- var textLine bool
- for scanner.Scan() {
- line := scanner.Text()
- if textLine {
- builder.WriteString(line)
- textLine = false
- continue
- } else if strings.Contains(line, "-->") {
- textLine = true
- continue
- }
+ var reqBody struct {
+ File *multipart.FileHeader `form:"file" binding:"required"`
}
- if err := scanner.Err(); err != nil {
- return "", err
+ c.Request.Body = io.NopCloser(bytes.NewReader(body))
+ if err = c.ShouldBind(&reqBody); err != nil {
+ return 0, errors.WithStack(err)
}
- return builder.String(), nil
-}
-func getTextFromText(body []byte) (string, error) {
- return strings.TrimSuffix(string(body), "\n"), nil
-}
-
-func getTextFromJSON(body []byte) (string, error) {
- var whisperResponse dto.AudioResponse
- if err := json.Unmarshal(body, &whisperResponse); err != nil {
- return "", fmt.Errorf("unmarshal_response_body_failed err :%w", err)
+ reqFp, err := reqBody.File.Open()
+ if err != nil {
+ return 0, errors.WithStack(err)
}
- return whisperResponse.Text, nil
+
+ tmpFp, err := os.CreateTemp("", "audio-*")
+ if err != nil {
+ return 0, errors.WithStack(err)
+ }
+ defer os.Remove(tmpFp.Name())
+
+ _, err = io.Copy(tmpFp, reqFp)
+ if err != nil {
+ return 0, errors.WithStack(err)
+ }
+ if err = tmpFp.Close(); err != nil {
+ return 0, errors.WithStack(err)
+ }
+
+ duration, err := common.GetAudioDuration(c.Request.Context(), tmpFp.Name())
+ if err != nil {
+ return 0, errors.WithStack(err)
+ }
+
+ return int(math.Round(math.Ceil(duration) / 60.0 * 1000)), nil // 1 minute 相当于 1k tokens
}
func OpenaiRealtimeHandler(c *gin.Context, info *relaycommon.RelayInfo) (*dto.OpenAIErrorWithStatusCode, *dto.RealtimeUsage) {
From 6e2c871015afd08eb89d13111853004769157587 Mon Sep 17 00:00:00 2001
From: HynoR <20227709+HynoR@users.noreply.github.com>
Date: Tue, 21 Jan 2025 00:53:10 +0800
Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E6=A8=A1?=
=?UTF-8?q?=E5=9E=8B=E5=92=8C=E6=A8=A1=E5=9E=8B=E5=80=8D=E7=8E=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
common/model-ratio.go | 5 ++++-
relay/channel/deepseek/constants.go | 2 +-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/common/model-ratio.go b/common/model-ratio.go
index e9e80e47..8fd996d6 100644
--- a/common/model-ratio.go
+++ b/common/model-ratio.go
@@ -401,10 +401,13 @@ func GetCompletionRatio(name string) float64 {
case "command-r-plus-08-2024":
return 4
default:
- return 2
+ return 4
}
}
if strings.HasPrefix(name, "deepseek") {
+ if name == "deepseek-reasoner" {
+ return 4
+ }
return 2
}
if strings.HasPrefix(name, "ERNIE-Speed-") {
diff --git a/relay/channel/deepseek/constants.go b/relay/channel/deepseek/constants.go
index b17c2c41..1d7b1e32 100644
--- a/relay/channel/deepseek/constants.go
+++ b/relay/channel/deepseek/constants.go
@@ -1,7 +1,7 @@
package deepseek
var ModelList = []string{
- "deepseek-chat", "deepseek-coder",
+ "deepseek-chat", "deepseek-reasoner",
}
var ChannelName = "deepseek"
From 53a941a6c0e2c7efc3be52366e6e97778923ec30 Mon Sep 17 00:00:00 2001
From: "1808837298@qq.com" <1808837298@qq.com>
Date: Tue, 21 Jan 2025 16:24:00 +0800
Subject: [PATCH 5/5] CI: update workflows
---
.github/workflows/docker-image-amd64.yml | 2 +-
.github/workflows/linux-release.yml | 2 +-
.github/workflows/macos-release.yml | 2 +-
.github/workflows/windows-release.yml | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/docker-image-amd64.yml b/.github/workflows/docker-image-amd64.yml
index 433561ea..36236df2 100644
--- a/.github/workflows/docker-image-amd64.yml
+++ b/.github/workflows/docker-image-amd64.yml
@@ -12,7 +12,7 @@ on:
jobs:
push_to_registries:
name: Push Docker image to multiple registries
- runs-on: self-hosted
+ runs-on: ubuntu-latest
permissions:
packages: write
contents: read
diff --git a/.github/workflows/linux-release.yml b/.github/workflows/linux-release.yml
index 6dd6868b..a3db3e79 100644
--- a/.github/workflows/linux-release.yml
+++ b/.github/workflows/linux-release.yml
@@ -9,7 +9,7 @@ on:
- '!*-alpha*'
jobs:
release:
- runs-on: self-hosted
+ runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
diff --git a/.github/workflows/macos-release.yml b/.github/workflows/macos-release.yml
index 8ccc0a98..cdb577ba 100644
--- a/.github/workflows/macos-release.yml
+++ b/.github/workflows/macos-release.yml
@@ -9,7 +9,7 @@ on:
- '!*-alpha*'
jobs:
release:
- runs-on: self-hosted
+ runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
diff --git a/.github/workflows/windows-release.yml b/.github/workflows/windows-release.yml
index 602940c7..b90eb592 100644
--- a/.github/workflows/windows-release.yml
+++ b/.github/workflows/windows-release.yml
@@ -9,7 +9,7 @@ on:
- '!*-alpha*'
jobs:
release:
- runs-on: self-hosted
+ runs-on: ubuntu-latest
defaults:
run:
shell: bash