mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-10-23 09:53:42 +08:00
Compare commits
39 Commits
v0.6.11-al
...
v0.6.11-pr
Author | SHA1 | Date | |
---|---|---|---|
|
3e3b8230ac | ||
|
07808122a6 | ||
|
c96895e35b | ||
|
2552c68249 | ||
|
5c81e40612 | ||
|
0d5318b1b7 | ||
|
db65db2807 | ||
|
e0b7e6a9e2 | ||
|
27c2abe80f | ||
|
2c867251b5 | ||
|
108111ebd3 | ||
|
293ba93ad6 | ||
|
faced40d5b | ||
|
2ae9997f29 | ||
|
e146b14d46 | ||
|
e19045f925 | ||
|
d2903b673d | ||
|
33edcf604c | ||
|
78fd6ef161 | ||
|
9b6d5f76e0 | ||
|
2250f311e1 | ||
|
e43f758623 | ||
|
6ded638f70 | ||
|
ea3331b79a | ||
|
7a97ddc03c | ||
|
d7028b55fd | ||
|
36a03f465b | ||
|
ae16647047 | ||
|
4dbc2ad86d | ||
|
94479c2800 | ||
|
614903b524 | ||
|
1dc255c219 | ||
|
15c27e4b12 | ||
|
a910d3ba25 | ||
|
34f889437d | ||
|
a56cb4a2d5 | ||
|
95009b254a | ||
|
20c30a060b | ||
|
ca334a5502 |
11
.github/workflows/docker-image.yml
vendored
11
.github/workflows/docker-image.yml
vendored
@@ -32,10 +32,10 @@ jobs:
|
||||
git describe --tags > VERSION
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
@@ -62,8 +62,9 @@ jobs:
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
platforms: linux/amd64 # TODO disable arm64 for now, because it cause error
|
||||
platforms: ${{ contains(github.ref, 'alpha') && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
TARGETARCH=${{ startsWith(matrix.platform, 'linux/arm64') && 'arm64' || 'amd64' }}
|
1
.github/workflows/linux-release.yml
vendored
1
.github/workflows/linux-release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- '!*-alpha*'
|
||||
- '!*-preview*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
name:
|
||||
|
1
.github/workflows/macos-release.yml
vendored
1
.github/workflows/macos-release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- '!*-alpha*'
|
||||
- '!*-preview*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
name:
|
||||
|
1
.github/workflows/windows-release.yml
vendored
1
.github/workflows/windows-release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
- '!*-alpha*'
|
||||
- '!*-preview*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
name:
|
||||
|
29
Dockerfile
29
Dockerfile
@@ -9,23 +9,23 @@ RUN npm install --prefix /web/default & \
|
||||
npm install --prefix /web/air & \
|
||||
wait
|
||||
|
||||
RUN DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat /web/default/VERSION) npm run build --prefix /web/default & \
|
||||
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat /web/berry/VERSION) npm run build --prefix /web/berry & \
|
||||
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat /web/air/VERSION) npm run build --prefix /web/air & \
|
||||
RUN DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat ./VERSION) npm run build --prefix /web/default & \
|
||||
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat ./VERSION) npm run build --prefix /web/berry & \
|
||||
DISABLE_ESLINT_PLUGIN='true' REACT_APP_VERSION=$(cat ./VERSION) npm run build --prefix /web/air & \
|
||||
wait
|
||||
|
||||
FROM golang AS builder2
|
||||
FROM golang:alpine AS builder2
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
sqlite3 libsqlite3-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
musl-dev \
|
||||
sqlite-dev \
|
||||
build-base
|
||||
|
||||
ENV GO111MODULE=on \
|
||||
CGO_ENABLED=1 \
|
||||
GOOS=linux \
|
||||
CGO_CFLAGS="-I/usr/include" \
|
||||
CGO_LDFLAGS="-L/usr/lib"
|
||||
GOARCH=$TARGETARCH
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
@@ -35,14 +35,11 @@ RUN go mod download
|
||||
COPY . .
|
||||
COPY --from=builder /web/build ./web/build
|
||||
|
||||
RUN go build -trimpath -ldflags "-s -w -X 'github.com/songquanpeng/one-api/common.Version=$(cat VERSION)'" -o one-api
|
||||
RUN go build -trimpath -ldflags "-s -w -X 'github.com/songquanpeng/one-api/common.Version=$(cat VERSION)' -linkmode external -extldflags '-static'" -o one-api
|
||||
|
||||
# Final runtime image
|
||||
FROM ubuntu:22.04
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates tzdata bash \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
COPY --from=builder2 /build/one-api /
|
||||
|
||||
|
@@ -315,6 +315,7 @@ If the channel ID is not provided, load balancing will be used to distribute the
|
||||
* [FastGPT](https://github.com/labring/FastGPT): Knowledge question answering system based on the LLM
|
||||
* [VChart](https://github.com/VisActor/VChart): More than just a cross-platform charting library, but also an expressive data storyteller.
|
||||
* [VMind](https://github.com/VisActor/VMind): Not just automatic, but also fantastic. Open-source solution for intelligent visualization.
|
||||
* * [CherryStudio](https://github.com/CherryHQ/cherry-studio): A cross-platform AI client that integrates multiple service providers and supports local knowledge base management.
|
||||
|
||||
## Note
|
||||
This project is an open-source project. Please use it in compliance with OpenAI's [Terms of Use](https://openai.com/policies/terms-of-use) and **applicable laws and regulations**. It must not be used for illegal purposes.
|
||||
|
@@ -287,8 +287,8 @@ graph LR
|
||||
+ インターフェイスアドレスと API Key が正しいか再確認してください。
|
||||
|
||||
## 関連プロジェクト
|
||||
[FastGPT](https://github.com/labring/FastGPT): LLM に基づく知識質問応答システム
|
||||
|
||||
* [FastGPT](https://github.com/labring/FastGPT): LLM に基づく知識質問応答システム
|
||||
* [CherryStudio](https://github.com/CherryHQ/cherry-studio): マルチプラットフォーム対応のAIクライアント。複数のサービスプロバイダーを統合管理し、ローカル知識ベースをサポートします。
|
||||
## 注
|
||||
本プロジェクトはオープンソースプロジェクトです。OpenAI の[利用規約](https://openai.com/policies/terms-of-use)および**適用される法令**を遵守してご利用ください。違法な目的での利用はご遠慮ください。
|
||||
|
||||
|
@@ -57,10 +57,10 @@ _✨ 通过标准的 OpenAI API 格式访问所有的大模型,开箱即用
|
||||
> 根据[《生成式人工智能服务管理暂行办法》](http://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm)的要求,请勿对中国地区公众提供一切未经备案的生成式人工智能服务。
|
||||
|
||||
> [!NOTE]
|
||||
> 稳定版镜像地址:[justsong/one-api](https://hub.docker.com/repository/docker/justsong/one-api)
|
||||
> 稳定版 / 预览版镜像地址:[justsong/one-api](https://hub.docker.com/repository/docker/justsong/one-api)
|
||||
> 或者 [ghcr.io/songquanpeng/one-api](https://github.com/songquanpeng/one-api/pkgs/container/one-api)
|
||||
>
|
||||
> 预览版镜像地址:[justsong/one-api-alpha](https://hub.docker.com/repository/docker/justsong/one-api-alpha)
|
||||
> alpha 版镜像地址:[justsong/one-api-alpha](https://hub.docker.com/repository/docker/justsong/one-api-alpha)
|
||||
> 或者 [ghcr.io/songquanpeng/one-api-alpha](https://github.com/songquanpeng/one-api/pkgs/container/one-api-alpha)
|
||||
|
||||
> [!WARNING]
|
||||
@@ -93,7 +93,7 @@ _✨ 通过标准的 OpenAI API 格式访问所有的大模型,开箱即用
|
||||
+ [x] [DeepL](https://www.deepl.com/)
|
||||
+ [x] [together.ai](https://www.together.ai/)
|
||||
+ [x] [novita.ai](https://www.novita.ai/)
|
||||
+ [x] [硅基流动 SiliconCloud](https://siliconflow.cn/siliconcloud)
|
||||
+ [x] [硅基流动 SiliconCloud](https://cloud.siliconflow.cn/i/rKXmRobW)
|
||||
+ [x] [xAI](https://x.ai/)
|
||||
2. 支持配置镜像以及众多[第三方代理服务](https://iamazing.cn/page/openai-api-third-party-services)。
|
||||
3. 支持通过**负载均衡**的方式访问多个渠道。
|
||||
@@ -469,6 +469,7 @@ https://openai.justsong.cn
|
||||
* [ChatGPT Next Web](https://github.com/Yidadaa/ChatGPT-Next-Web): 一键拥有你自己的跨平台 ChatGPT 应用
|
||||
* [VChart](https://github.com/VisActor/VChart): 不只是开箱即用的多端图表库,更是生动灵活的数据故事讲述者。
|
||||
* [VMind](https://github.com/VisActor/VMind): 不仅自动,还很智能。开源智能可视化解决方案。
|
||||
* [CherryStudio](https://github.com/CherryHQ/cherry-studio): 全平台支持的AI客户端, 多服务商集成管理、本地知识库支持。
|
||||
|
||||
## 注意
|
||||
|
||||
|
@@ -126,10 +126,10 @@ var ValidThemes = map[string]bool{
|
||||
// All duration's unit is seconds
|
||||
// Shouldn't larger then RateLimitKeyExpirationDuration
|
||||
var (
|
||||
GlobalApiRateLimitNum = env.Int("GLOBAL_API_RATE_LIMIT", 240)
|
||||
GlobalApiRateLimitNum = env.Int("GLOBAL_API_RATE_LIMIT", 480)
|
||||
GlobalApiRateLimitDuration int64 = 3 * 60
|
||||
|
||||
GlobalWebRateLimitNum = env.Int("GLOBAL_WEB_RATE_LIMIT", 120)
|
||||
GlobalWebRateLimitNum = env.Int("GLOBAL_WEB_RATE_LIMIT", 240)
|
||||
GlobalWebRateLimitDuration int64 = 3 * 60
|
||||
|
||||
UploadRateLimitNum = 10
|
||||
|
@@ -3,10 +3,11 @@ package common
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/songquanpeng/one-api/common/ctxkey"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/songquanpeng/one-api/common/ctxkey"
|
||||
)
|
||||
|
||||
func GetRequestBody(c *gin.Context) ([]byte, error) {
|
||||
@@ -31,7 +32,6 @@ func UnmarshalBodyReusable(c *gin.Context, v any) error {
|
||||
contentType := c.Request.Header.Get("Content-Type")
|
||||
if strings.HasPrefix(contentType, "application/json") {
|
||||
err = json.Unmarshal(requestBody, &v)
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
|
||||
} else {
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
|
||||
err = c.ShouldBind(&v)
|
||||
@@ -40,6 +40,7 @@ func UnmarshalBodyReusable(c *gin.Context, v any) error {
|
||||
return err
|
||||
}
|
||||
// Reset request body
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -93,6 +93,9 @@ func Error(ctx context.Context, msg string) {
|
||||
}
|
||||
|
||||
func Debugf(ctx context.Context, format string, a ...any) {
|
||||
if !config.DebugEnabled {
|
||||
return
|
||||
}
|
||||
logHelper(ctx, loggerDEBUG, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,8 @@ func ShouldDisableChannel(err *model.Error, statusCode int) bool {
|
||||
strings.Contains(lowerMessage, "balance") ||
|
||||
strings.Contains(lowerMessage, "permission denied") ||
|
||||
strings.Contains(lowerMessage, "organization has been restricted") || // groq
|
||||
strings.Contains(lowerMessage, "api key not valid") || // gemini
|
||||
strings.Contains(lowerMessage, "api key expired") || // gemini
|
||||
strings.Contains(lowerMessage, "已欠费") {
|
||||
return true
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ var ModelList = []string{
|
||||
"claude-instant-1.2", "claude-2.0", "claude-2.1",
|
||||
"claude-3-haiku-20240307",
|
||||
"claude-3-5-haiku-20241022",
|
||||
"claude-3-5-haiku-latest",
|
||||
"claude-3-sonnet-20240229",
|
||||
"claude-3-opus-20240229",
|
||||
"claude-3-5-sonnet-20240620",
|
||||
|
@@ -2,5 +2,5 @@ package deepseek
|
||||
|
||||
var ModelList = []string{
|
||||
"deepseek-chat",
|
||||
"deepseek-coder",
|
||||
"deepseek-reasoner",
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/songquanpeng/one-api/common/helper"
|
||||
channelhelper "github.com/songquanpeng/one-api/relay/adaptor"
|
||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||
@@ -29,6 +30,8 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
|
||||
"gemini-2.0-flash-thinking-exp",
|
||||
"gemini-2.0-flash-thinking-exp-01-21":
|
||||
defaultVersion = "v1beta"
|
||||
default:
|
||||
defaultVersion = "v1beta"
|
||||
}
|
||||
|
||||
version := helper.AssignOrDefault(meta.Config.APIVersion, defaultVersion)
|
||||
|
@@ -3,7 +3,6 @@ package groq
|
||||
// https://console.groq.com/docs/models
|
||||
|
||||
var ModelList = []string{
|
||||
"gemma-7b-it",
|
||||
"gemma2-9b-it",
|
||||
"llama-3.1-70b-versatile",
|
||||
"llama-3.1-8b-instant",
|
||||
@@ -11,7 +10,6 @@ var ModelList = []string{
|
||||
"llama-3.2-11b-vision-preview",
|
||||
"llama-3.2-1b-preview",
|
||||
"llama-3.2-3b-preview",
|
||||
"llama-3.2-11b-vision-preview",
|
||||
"llama-3.2-90b-text-preview",
|
||||
"llama-3.2-90b-vision-preview",
|
||||
"llama-guard-3-8b",
|
||||
@@ -24,4 +22,6 @@ var ModelList = []string{
|
||||
"distil-whisper-large-v3-en",
|
||||
"whisper-large-v3",
|
||||
"whisper-large-v3-turbo",
|
||||
"deepseek-r1-distill-llama-70b-specdec",
|
||||
"deepseek-r1-distill-llama-70b",
|
||||
}
|
||||
|
@@ -1,7 +1,14 @@
|
||||
package zhipu
|
||||
|
||||
// https://open.bigmodel.cn/pricing
|
||||
|
||||
var ModelList = []string{
|
||||
"chatglm_turbo", "chatglm_pro", "chatglm_std", "chatglm_lite",
|
||||
"glm-4", "glm-4v", "glm-3-turbo", "embedding-2",
|
||||
"cogview-3",
|
||||
"glm-zero-preview", "glm-4-plus", "glm-4-0520", "glm-4-airx",
|
||||
"glm-4-air", "glm-4-long", "glm-4-flashx", "glm-4-flash",
|
||||
"glm-4", "glm-3-turbo",
|
||||
"glm-4v-plus", "glm-4v", "glm-4v-flash",
|
||||
"cogview-3-plus", "cogview-3", "cogview-3-flash",
|
||||
"cogviewx", "cogviewx-flash",
|
||||
"charglm-4", "emohaa", "codegeex-4",
|
||||
"embedding-2", "embedding-3",
|
||||
}
|
||||
|
@@ -3,8 +3,10 @@ package ratio
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/songquanpeng/one-api/common/logger"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var groupRatioLock sync.RWMutex
|
||||
var GroupRatio = map[string]float64{
|
||||
"default": 1,
|
||||
"vip": 1,
|
||||
@@ -20,11 +22,15 @@ func GroupRatio2JSONString() string {
|
||||
}
|
||||
|
||||
func UpdateGroupRatioByJSONString(jsonStr string) error {
|
||||
groupRatioLock.Lock()
|
||||
defer groupRatioLock.Unlock()
|
||||
GroupRatio = make(map[string]float64)
|
||||
return json.Unmarshal([]byte(jsonStr), &GroupRatio)
|
||||
}
|
||||
|
||||
func GetGroupRatio(name string) float64 {
|
||||
groupRatioLock.RLock()
|
||||
defer groupRatioLock.RUnlock()
|
||||
ratio, ok := GroupRatio[name]
|
||||
if !ok {
|
||||
logger.SysError("group ratio not found: " + name)
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/songquanpeng/one-api/common/logger"
|
||||
)
|
||||
@@ -15,6 +16,8 @@ const (
|
||||
RMB = USD / USD2RMB
|
||||
)
|
||||
|
||||
var modelRatioLock sync.RWMutex
|
||||
|
||||
// ModelRatio
|
||||
// https://platform.openai.com/docs/models/model-endpoint-compatibility
|
||||
// https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Blfmc9dlf
|
||||
@@ -82,15 +85,17 @@ var ModelRatio = map[string]float64{
|
||||
"text-moderation-latest": 0.1,
|
||||
"dall-e-2": 0.02 * USD, // $0.016 - $0.020 / image
|
||||
"dall-e-3": 0.04 * USD, // $0.040 - $0.120 / image
|
||||
// https://www.anthropic.com/api#pricing
|
||||
// https://docs.anthropic.com/en/docs/about-claude/models
|
||||
"claude-instant-1.2": 0.8 / 1000 * USD,
|
||||
"claude-2.0": 8.0 / 1000 * USD,
|
||||
"claude-2.1": 8.0 / 1000 * USD,
|
||||
"claude-3-haiku-20240307": 0.25 / 1000 * USD,
|
||||
"claude-3-5-haiku-20241022": 1.0 / 1000 * USD,
|
||||
"claude-3-5-haiku-latest": 1.0 / 1000 * USD,
|
||||
"claude-3-sonnet-20240229": 3.0 / 1000 * USD,
|
||||
"claude-3-5-sonnet-20240620": 3.0 / 1000 * USD,
|
||||
"claude-3-5-sonnet-20241022": 3.0 / 1000 * USD,
|
||||
"claude-3-5-sonnet-latest": 3.0 / 1000 * USD,
|
||||
"claude-3-opus-20240229": 15.0 / 1000 * USD,
|
||||
// https://cloud.baidu.com/doc/WENXINWORKSHOP/s/hlrk4akp7
|
||||
"ERNIE-4.0-8K": 0.120 * RMB,
|
||||
@@ -121,15 +126,29 @@ var ModelRatio = map[string]float64{
|
||||
"gemini-2.0-flash-thinking-exp-01-21": 1,
|
||||
"aqa": 1,
|
||||
// https://open.bigmodel.cn/pricing
|
||||
"glm-4": 0.1 * RMB,
|
||||
"glm-4v": 0.1 * RMB,
|
||||
"glm-3-turbo": 0.005 * RMB,
|
||||
"embedding-2": 0.0005 * RMB,
|
||||
"chatglm_turbo": 0.3572, // ¥0.005 / 1k tokens
|
||||
"chatglm_pro": 0.7143, // ¥0.01 / 1k tokens
|
||||
"chatglm_std": 0.3572, // ¥0.005 / 1k tokens
|
||||
"chatglm_lite": 0.1429, // ¥0.002 / 1k tokens
|
||||
"cogview-3": 0.25 * RMB,
|
||||
"glm-zero-preview": 0.01 * RMB,
|
||||
"glm-4-plus": 0.05 * RMB,
|
||||
"glm-4-0520": 0.1 * RMB,
|
||||
"glm-4-airx": 0.01 * RMB,
|
||||
"glm-4-air": 0.0005 * RMB,
|
||||
"glm-4-long": 0.001 * RMB,
|
||||
"glm-4-flashx": 0.0001 * RMB,
|
||||
"glm-4-flash": 0,
|
||||
"glm-4": 0.1 * RMB, // deprecated model, available until 2025/06
|
||||
"glm-3-turbo": 0.001 * RMB, // deprecated model, available until 2025/06
|
||||
"glm-4v-plus": 0.004 * RMB,
|
||||
"glm-4v": 0.05 * RMB,
|
||||
"glm-4v-flash": 0,
|
||||
"cogview-3-plus": 0.06 * RMB,
|
||||
"cogview-3": 0.1 * RMB,
|
||||
"cogview-3-flash": 0,
|
||||
"cogviewx": 0.5 * RMB,
|
||||
"cogviewx-flash": 0,
|
||||
"charglm-4": 0.001 * RMB,
|
||||
"emohaa": 0.015 * RMB,
|
||||
"codegeex-4": 0.0001 * RMB,
|
||||
"embedding-2": 0.0005 * RMB,
|
||||
"embedding-3": 0.0005 * RMB,
|
||||
// https://help.aliyun.com/zh/dashscope/developer-reference/tongyi-thousand-questions-metering-and-billing
|
||||
"qwen-turbo": 1.4286, // ¥0.02 / 1k tokens
|
||||
"qwen-turbo-latest": 1.4286,
|
||||
@@ -216,9 +235,19 @@ var ModelRatio = map[string]float64{
|
||||
"embedding-bert-512-v1": 0.0715, // ¥0.001 / 1k tokens
|
||||
"embedding_s1_v1": 0.0715, // ¥0.001 / 1k tokens
|
||||
"semantic_similarity_s1_v1": 0.0715, // ¥0.001 / 1k tokens
|
||||
"hunyuan": 7.143, // ¥0.1 / 1k tokens // https://cloud.tencent.com/document/product/1729/97731#e0e6be58-60c8-469f-bdeb-6c264ce3b4d0
|
||||
"ChatStd": 0.01 * RMB,
|
||||
"ChatPro": 0.1 * RMB,
|
||||
// https://cloud.tencent.com/document/product/1729/97731#e0e6be58-60c8-469f-bdeb-6c264ce3b4d0
|
||||
"hunyuan-turbo": 0.015 * RMB,
|
||||
"hunyuan-large": 0.004 * RMB,
|
||||
"hunyuan-large-longcontext": 0.006 * RMB,
|
||||
"hunyuan-standard": 0.0008 * RMB,
|
||||
"hunyuan-standard-256K": 0.0005 * RMB,
|
||||
"hunyuan-translation-lite": 0.005 * RMB,
|
||||
"hunyuan-role": 0.004 * RMB,
|
||||
"hunyuan-functioncall": 0.004 * RMB,
|
||||
"hunyuan-code": 0.004 * RMB,
|
||||
"hunyuan-turbo-vision": 0.08 * RMB,
|
||||
"hunyuan-vision": 0.018 * RMB,
|
||||
"hunyuan-embedding": 0.0007 * RMB,
|
||||
// https://platform.moonshot.cn/pricing
|
||||
"moonshot-v1-8k": 0.012 * RMB,
|
||||
"moonshot-v1-32k": 0.024 * RMB,
|
||||
@@ -391,11 +420,15 @@ func ModelRatio2JSONString() string {
|
||||
}
|
||||
|
||||
func UpdateModelRatioByJSONString(jsonStr string) error {
|
||||
modelRatioLock.Lock()
|
||||
defer modelRatioLock.Unlock()
|
||||
ModelRatio = make(map[string]float64)
|
||||
return json.Unmarshal([]byte(jsonStr), &ModelRatio)
|
||||
}
|
||||
|
||||
func GetModelRatio(name string, channelType int) float64 {
|
||||
modelRatioLock.RLock()
|
||||
defer modelRatioLock.RUnlock()
|
||||
if strings.HasPrefix(name, "qwen-") && strings.HasSuffix(name, "-internet") {
|
||||
name = strings.TrimSuffix(name, "-internet")
|
||||
}
|
||||
|
@@ -1,11 +1,12 @@
|
||||
package model
|
||||
|
||||
type Message struct {
|
||||
Role string `json:"role,omitempty"`
|
||||
Content any `json:"content,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
ToolCalls []Tool `json:"tool_calls,omitempty"`
|
||||
ToolCallId string `json:"tool_call_id,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
Content any `json:"content,omitempty"`
|
||||
ReasoningContent any `json:"reasoning_content,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
ToolCalls []Tool `json:"tool_calls,omitempty"`
|
||||
ToolCallId string `json:"tool_call_id,omitempty"`
|
||||
}
|
||||
|
||||
func (m Message) IsStringContent() bool {
|
||||
|
@@ -7,7 +7,7 @@ export const CHANNEL_OPTIONS = [
|
||||
{ key: 24, text: 'Google Gemini', value: 24, color: 'orange' },
|
||||
{ key: 28, text: 'Mistral AI', value: 28, color: 'orange' },
|
||||
{ key: 41, text: 'Novita', value: 41, color: 'purple' },
|
||||
{ key: 40, text: '字节跳动豆包', value: 40, color: 'blue' },
|
||||
{key: 40, text: '火山引擎', value: 40, color: 'blue'},
|
||||
{ key: 15, text: '百度文心千帆', value: 15, color: 'blue' },
|
||||
{ key: 17, text: '阿里通义千问', value: 17, color: 'orange' },
|
||||
{ key: 18, text: '讯飞星火认知', value: 18, color: 'blue' },
|
||||
|
@@ -49,7 +49,7 @@ export const CHANNEL_OPTIONS = {
|
||||
},
|
||||
40: {
|
||||
key: 40,
|
||||
text: '字节跳动豆包',
|
||||
text: '火山引擎',
|
||||
value: 40,
|
||||
color: 'primary'
|
||||
},
|
||||
|
@@ -7,7 +7,6 @@
|
||||
"history": "^5.3.0",
|
||||
"i18next": "^24.2.2",
|
||||
"i18next-browser-languagedetector": "^8.0.2",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"marked": "^4.1.1",
|
||||
"moment": "^2.30.1",
|
||||
"react": "^18.2.0",
|
||||
|
@@ -328,6 +328,7 @@ const LogsTable = () => {
|
||||
<Form.Input
|
||||
fluid
|
||||
label={t('log.table.token_name')}
|
||||
size={'small'}
|
||||
width={3}
|
||||
value={token_name}
|
||||
placeholder={t('log.table.token_name_placeholder')}
|
||||
@@ -337,6 +338,7 @@ const LogsTable = () => {
|
||||
<Form.Input
|
||||
fluid
|
||||
label={t('log.table.model_name')}
|
||||
size={'small'}
|
||||
width={3}
|
||||
value={model_name}
|
||||
placeholder={t('log.table.model_name_placeholder')}
|
||||
@@ -346,6 +348,7 @@ const LogsTable = () => {
|
||||
<Form.Input
|
||||
fluid
|
||||
label={t('log.table.start_time')}
|
||||
size={'small'}
|
||||
width={4}
|
||||
value={start_timestamp}
|
||||
type='datetime-local'
|
||||
@@ -355,6 +358,7 @@ const LogsTable = () => {
|
||||
<Form.Input
|
||||
fluid
|
||||
label={t('log.table.end_time')}
|
||||
size={'small'}
|
||||
width={4}
|
||||
value={end_timestamp}
|
||||
type='datetime-local'
|
||||
@@ -364,6 +368,7 @@ const LogsTable = () => {
|
||||
<Form.Button
|
||||
fluid
|
||||
label={t('log.buttons.query')}
|
||||
size={'small'}
|
||||
width={2}
|
||||
onClick={refresh}
|
||||
>
|
||||
@@ -376,6 +381,7 @@ const LogsTable = () => {
|
||||
<Form.Input
|
||||
fluid
|
||||
label={t('log.table.channel_id')}
|
||||
size={'small'}
|
||||
width={3}
|
||||
value={channel}
|
||||
placeholder={t('log.table.channel_id_placeholder')}
|
||||
@@ -385,6 +391,7 @@ const LogsTable = () => {
|
||||
<Form.Input
|
||||
fluid
|
||||
label={t('log.table.username')}
|
||||
size={'small'}
|
||||
width={3}
|
||||
value={username}
|
||||
placeholder={t('log.table.username_placeholder')}
|
||||
|
@@ -415,9 +415,9 @@ const TokensTable = () => {
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<div>
|
||||
<Button.Group color='green' size={'mini'}>
|
||||
<Button.Group color='green' size={'tiny'}>
|
||||
<Button
|
||||
size={'mini'}
|
||||
size={'tiny'}
|
||||
positive
|
||||
onClick={async () => await onCopy('', token.key)}
|
||||
>
|
||||
@@ -430,9 +430,9 @@ const TokensTable = () => {
|
||||
trigger={<></>}
|
||||
/>
|
||||
</Button.Group>{' '}
|
||||
<Button.Group color='blue' size={'mini'}>
|
||||
<Button.Group color='olive' size={'tiny'}>
|
||||
<Button
|
||||
size={'mini'}
|
||||
size={'tiny'}
|
||||
positive
|
||||
onClick={() => onOpenLink('', token.key)}
|
||||
>
|
||||
@@ -456,7 +456,7 @@ const TokensTable = () => {
|
||||
hoverable
|
||||
>
|
||||
<Button
|
||||
size={'mini'}
|
||||
size={'tiny'}
|
||||
negative
|
||||
onClick={() => {
|
||||
manageToken(token.id, 'delete', idx);
|
||||
@@ -466,7 +466,7 @@ const TokensTable = () => {
|
||||
</Button>
|
||||
</Popup>
|
||||
<Button
|
||||
size={'mini'}
|
||||
size={'tiny'}
|
||||
onClick={() => {
|
||||
manageToken(
|
||||
token.id,
|
||||
@@ -480,7 +480,7 @@ const TokensTable = () => {
|
||||
: t('token.buttons.enable')}
|
||||
</Button>
|
||||
<Button
|
||||
size={'mini'}
|
||||
size={'tiny'}
|
||||
as={Link}
|
||||
to={'/token/edit/' + token.id}
|
||||
>
|
||||
|
@@ -7,7 +7,7 @@ export const CHANNEL_OPTIONS = [
|
||||
{ key: 24, text: 'Google Gemini', value: 24, color: 'orange' },
|
||||
{ key: 28, text: 'Mistral AI', value: 28, color: 'orange' },
|
||||
{ key: 41, text: 'Novita', value: 41, color: 'purple' },
|
||||
{ key: 40, text: '字节跳动豆包', value: 40, color: 'blue' },
|
||||
{key: 40, text: '火山引擎', value: 40, color: 'blue'},
|
||||
{ key: 15, text: '百度文心千帆', value: 15, color: 'blue' },
|
||||
{ key: 17, text: '阿里通义千问', value: 17, color: 'orange' },
|
||||
{ key: 18, text: '讯飞星火认知', value: 18, color: 'blue' },
|
||||
|
@@ -1,7 +1,7 @@
|
||||
export const toastConstants = {
|
||||
SUCCESS_TIMEOUT: 1500,
|
||||
INFO_TIMEOUT: 3000,
|
||||
ERROR_TIMEOUT: 5000,
|
||||
SUCCESS_TIMEOUT: 5000,
|
||||
INFO_TIMEOUT: 8000,
|
||||
ERROR_TIMEOUT: 10000,
|
||||
WARNING_TIMEOUT: 10000,
|
||||
NOTICE_TIMEOUT: 20000
|
||||
NOTICE_TIMEOUT: 20000,
|
||||
};
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import Backend from 'i18next-http-backend';
|
||||
import {initReactI18next} from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import zhTranslation from './locales/zh/translation.json';
|
||||
import enTranslation from './locales/en/translation.json';
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
@@ -15,9 +15,14 @@ i18n
|
||||
escapeValue: false,
|
||||
},
|
||||
|
||||
backend: {
|
||||
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
||||
},
|
||||
resources: {
|
||||
zh: {
|
||||
translation: zhTranslation
|
||||
},
|
||||
en: {
|
||||
translation: enTranslation
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
@@ -62,7 +62,8 @@
|
||||
"not_tested": "Not Tested",
|
||||
"priority_tip": "Channel selection priority, higher is preferred",
|
||||
"select_test_model": "Please select test model",
|
||||
"click_to_update": "Click to update"
|
||||
"click_to_update": "Click to update",
|
||||
"balance_not_supported": "-"
|
||||
},
|
||||
"buttons": {
|
||||
"test": "Test",
|
||||
@@ -85,7 +86,8 @@
|
||||
"test_all_started": "Channel testing started successfully, please refresh page to see results.",
|
||||
"delete_disabled_success": "Deleted all disabled channels, total: {{count}}",
|
||||
"balance_update_success": "Channel {{name}} balance updated successfully!",
|
||||
"all_balance_updated": "All enabled channel balances have been updated!"
|
||||
"all_balance_updated": "All enabled channel balances have been updated!",
|
||||
"operation_success": "Operation completed successfully!"
|
||||
},
|
||||
"edit": {
|
||||
"title_edit": "Update Channel Information",
|
||||
@@ -165,7 +167,7 @@
|
||||
"expired_time": "Expiry Time",
|
||||
"actions": "Actions",
|
||||
"no_name": "None",
|
||||
"never_expire": "Never Expires",
|
||||
"never_expire": "never",
|
||||
"unlimited": "Unlimited",
|
||||
"status_enabled": "Enabled",
|
||||
"status_disabled": "Disabled",
|
@@ -62,7 +62,8 @@
|
||||
"not_tested": "未测试",
|
||||
"priority_tip": "渠道选择优先级,越高越优先",
|
||||
"select_test_model": "请选择测试模型",
|
||||
"click_to_update": "点击更新"
|
||||
"click_to_update": "点击更新",
|
||||
"balance_not_supported": "-"
|
||||
},
|
||||
"buttons": {
|
||||
"test": "测试",
|
||||
@@ -85,7 +86,8 @@
|
||||
"test_all_started": "已成功开始测试渠道,请刷新页面查看结果。",
|
||||
"delete_disabled_success": "已删除所有禁用渠道,共计 {{count}} 个",
|
||||
"balance_update_success": "渠道 {{name}} 余额更新成功!",
|
||||
"all_balance_updated": "已更新完毕所有已启用渠道余额!"
|
||||
"all_balance_updated": "已更新完毕所有已启用渠道余额!",
|
||||
"operation_success": "操作成功完成!"
|
||||
},
|
||||
"edit": {
|
||||
"title_edit": "更新渠道信息",
|
||||
@@ -133,7 +135,7 @@
|
||||
"coze_notice": "对于 Coze 而言,模型名称即 Bot ID,你可以添加一个前缀 `bot-`,例如:`bot-123456`。",
|
||||
"douban_notice": "对于豆包而言,需要手动去",
|
||||
"douban_notice_link": "模型推理页面",
|
||||
"douban_notice_2": "创建推理接入点,以接入点名称作为模型名称,例如:`ep-20240608051426-tkxvl`。",
|
||||
"douban_notice_2": "创建推理接入点,以接入点名称作为模型名称,例如:`ep-20240608051426-tkxvl`。你可以结合模型重定向功能将其转换为常规的模型名称,例如:doubao-lite-4k -> ep-20240608051426-tkxvl(前者作为 JSON 的 key,后者作为 value)。注意,doubao-lite-4k 和 ep-20240608051426-tkxvl 都需要通过自定义模型的方式填入到本渠道的模型列表中。",
|
||||
"aws_region_placeholder": "region,例如:us-west-2",
|
||||
"aws_ak_placeholder": "AWS IAM Access Key",
|
||||
"aws_sk_placeholder": "AWS IAM Secret Key",
|
@@ -54,10 +54,16 @@ const About = () => {
|
||||
style={{ width: '100%', height: '100vh', border: 'none' }}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{ fontSize: 'larger' }}
|
||||
dangerouslySetInnerHTML={{ __html: about }}
|
||||
></div>
|
||||
<div className='dashboard-container'>
|
||||
<Card fluid className='chart-card'>
|
||||
<Card.Content>
|
||||
<div
|
||||
style={{ fontSize: 'larger' }}
|
||||
dangerouslySetInnerHTML={{ __html: about }}
|
||||
></div>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
@@ -207,6 +207,9 @@ const EditChannel = () => {
|
||||
return;
|
||||
}
|
||||
let localInputs = { ...inputs };
|
||||
if (localInputs.key === 'undefined|undefined|undefined') {
|
||||
localInputs.key = ''; // prevent potential bug
|
||||
}
|
||||
if (localInputs.base_url && localInputs.base_url.endsWith('/')) {
|
||||
localInputs.base_url = localInputs.base_url.slice(
|
||||
0,
|
||||
@@ -622,6 +625,21 @@ const EditChannel = () => {
|
||||
/>
|
||||
</Form.Field>
|
||||
))}
|
||||
{inputs.type === 37 && (
|
||||
<Form.Field>
|
||||
<Form.Input
|
||||
label='Account ID'
|
||||
name='user_id'
|
||||
required
|
||||
placeholder={
|
||||
'请输入 Account ID,例如:d8d7c61dbc334c32d3ced580e4bf42b4'
|
||||
}
|
||||
onChange={handleConfigChange}
|
||||
value={config.user_id}
|
||||
autoComplete=''
|
||||
/>
|
||||
</Form.Field>
|
||||
)}
|
||||
{inputs.type !== 33 && !isEdit && (
|
||||
<Form.Checkbox
|
||||
checked={batch}
|
||||
|
@@ -36,7 +36,7 @@
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04) !important;
|
||||
border: none !important;
|
||||
border-radius: 16px !important;
|
||||
padding-top: 8px!important;
|
||||
padding: 8px!important;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
@@ -91,15 +91,15 @@
|
||||
}
|
||||
|
||||
.settings-tab .item {
|
||||
color: #2B3674 !important;
|
||||
color: #000 !important;
|
||||
font-weight: 500 !important;
|
||||
padding: 0.8rem 1.2rem !important;
|
||||
}
|
||||
|
||||
.settings-tab .active.item {
|
||||
color: #4318FF !important;
|
||||
color: #000 !important;
|
||||
font-weight: 600 !important;
|
||||
border-color: #4318FF !important;
|
||||
border-color: #000 !important;
|
||||
}
|
||||
|
||||
.ui.tab.segment {
|
||||
|
@@ -1,19 +1,17 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Card, Grid, Header, Segment, Statistic } from 'semantic-ui-react';
|
||||
import { API, showError } from '../../helpers';
|
||||
import moment from 'moment';
|
||||
import { Card, Grid } from 'semantic-ui-react';
|
||||
import {
|
||||
LineChart,
|
||||
Bar,
|
||||
BarChart,
|
||||
CartesianGrid,
|
||||
Legend,
|
||||
Line,
|
||||
LineChart,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
BarChart,
|
||||
Bar,
|
||||
Legend,
|
||||
} from 'recharts';
|
||||
import axios from 'axios';
|
||||
import './Dashboard.css';
|
||||
@@ -244,7 +242,7 @@ const Dashboard = () => {
|
||||
<Card.Content>
|
||||
<Card.Header>
|
||||
{t('dashboard.charts.requests.title')}
|
||||
<span className='stat-value'>{summaryData.todayRequests}</span>
|
||||
{/* <span className='stat-value'>{summaryData.todayRequests}</span> */}
|
||||
</Card.Header>
|
||||
<div className='chart-container'>
|
||||
<ResponsiveContainer
|
||||
@@ -273,7 +271,9 @@ const Dashboard = () => {
|
||||
t('dashboard.charts.requests.tooltip'),
|
||||
]}
|
||||
labelFormatter={(label) =>
|
||||
`${t('dashboard.tooltip.date')}: ${formatDate(label)}`
|
||||
`${t(
|
||||
'dashboard.statistics.tooltip.date'
|
||||
)}: ${formatDate(label)}`
|
||||
}
|
||||
/>
|
||||
<Line
|
||||
@@ -296,9 +296,9 @@ const Dashboard = () => {
|
||||
<Card.Content>
|
||||
<Card.Header>
|
||||
{t('dashboard.charts.quota.title')}
|
||||
<span className='stat-value'>
|
||||
{/* <span className='stat-value'>
|
||||
${summaryData.todayQuota.toFixed(3)}
|
||||
</span>
|
||||
</span> */}
|
||||
</Card.Header>
|
||||
<div className='chart-container'>
|
||||
<ResponsiveContainer
|
||||
@@ -323,11 +323,13 @@ const Dashboard = () => {
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
||||
}}
|
||||
formatter={(value) => [
|
||||
value,
|
||||
value.toFixed(6),
|
||||
t('dashboard.charts.quota.tooltip'),
|
||||
]}
|
||||
labelFormatter={(label) =>
|
||||
`${t('dashboard.tooltip.date')}: ${formatDate(label)}`
|
||||
`${t(
|
||||
'dashboard.statistics.tooltip.date'
|
||||
)}: ${formatDate(label)}`
|
||||
}
|
||||
/>
|
||||
<Line
|
||||
@@ -350,7 +352,7 @@ const Dashboard = () => {
|
||||
<Card.Content>
|
||||
<Card.Header>
|
||||
{t('dashboard.charts.tokens.title')}
|
||||
<span className='stat-value'>{summaryData.todayTokens}</span>
|
||||
{/* <span className='stat-value'>{summaryData.todayTokens}</span> */}
|
||||
</Card.Header>
|
||||
<div className='chart-container'>
|
||||
<ResponsiveContainer
|
||||
@@ -379,7 +381,9 @@ const Dashboard = () => {
|
||||
t('dashboard.charts.tokens.tooltip'),
|
||||
]}
|
||||
labelFormatter={(label) =>
|
||||
`${t('dashboard.tooltip.date')}: ${formatDate(label)}`
|
||||
`${t(
|
||||
'dashboard.statistics.tooltip.date'
|
||||
)}: ${formatDate(label)}`
|
||||
}
|
||||
/>
|
||||
<Line
|
||||
@@ -424,7 +428,9 @@ const Dashboard = () => {
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
|
||||
}}
|
||||
labelFormatter={(label) =>
|
||||
`${t('dashboard.tooltip.date')}: ${formatDate(label)}`
|
||||
`${t('dashboard.statistics.tooltip.date')}: ${formatDate(
|
||||
label
|
||||
)}`
|
||||
}
|
||||
/>
|
||||
<Legend
|
||||
|
Reference in New Issue
Block a user