mirror of
https://github.com/songquanpeng/one-api.git
synced 2026-02-05 21:54:24 +08:00
Compare commits
20 Commits
patch/prox
...
ebcbe8afd2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebcbe8afd2 | ||
|
|
cba82404ae | ||
|
|
c9ac670ba1 | ||
|
|
15f815c23c | ||
|
|
89b63ca96f | ||
|
|
8cc54489b9 | ||
|
|
58bf60805e | ||
|
|
6714cf96d6 | ||
|
|
4a362caae6 | ||
|
|
f9774698e9 | ||
|
|
2af6f6a166 | ||
|
|
04bb3ef392 | ||
|
|
b4bfa418a8 | ||
|
|
e7e99e558a | ||
|
|
402fcf7f79 | ||
|
|
36039e329e | ||
|
|
c936198ac8 | ||
|
|
296ab013b8 | ||
|
|
5f03c856b4 | ||
|
|
39383e5532 |
61
.github/workflows/docker-image-amd64.yml
vendored
61
.github/workflows/docker-image-amd64.yml
vendored
@@ -1,61 +0,0 @@
|
|||||||
name: Publish Docker image (amd64)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
name:
|
|
||||||
description: 'reason'
|
|
||||||
required: false
|
|
||||||
jobs:
|
|
||||||
push_to_registries:
|
|
||||||
name: Push Docker image to multiple registries
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
packages: write
|
|
||||||
contents: read
|
|
||||||
steps:
|
|
||||||
- name: Check out the repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Check repository URL
|
|
||||||
run: |
|
|
||||||
REPO_URL=$(git config --get remote.origin.url)
|
|
||||||
if [[ $REPO_URL == *"pro" ]]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Save version info
|
|
||||||
run: |
|
|
||||||
git describe --tags > VERSION
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v4
|
|
||||||
with:
|
|
||||||
images: |
|
|
||||||
justsong/one-api
|
|
||||||
ghcr.io/${{ github.repository }}
|
|
||||||
|
|
||||||
- name: Build and push Docker images
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Publish Docker image (amd64, English)
|
name: Publish Docker image (English)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -34,6 +34,13 @@ jobs:
|
|||||||
- name: Translate
|
- name: Translate
|
||||||
run: |
|
run: |
|
||||||
python ./i18n/translate.py --repository_path . --json_file_path ./i18n/en.json
|
python ./i18n/translate.py --repository_path . --json_file_path ./i18n/en.json
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
@@ -51,6 +58,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
name: Publish Docker image (arm64)
|
name: Publish Docker image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*.*.*'
|
- 'v*.*.*'
|
||||||
- '!*-alpha*'
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
name:
|
name:
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:16 as builder
|
FROM --platform=$BUILDPLATFORM node:16 AS builder
|
||||||
|
|
||||||
WORKDIR /web
|
WORKDIR /web
|
||||||
COPY ./VERSION .
|
COPY ./VERSION .
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ _✨ 通过标准的 OpenAI API 格式访问所有的大模型,开箱即用
|
|||||||
+ [x] [DeepL](https://www.deepl.com/)
|
+ [x] [DeepL](https://www.deepl.com/)
|
||||||
+ [x] [together.ai](https://www.together.ai/)
|
+ [x] [together.ai](https://www.together.ai/)
|
||||||
+ [x] [novita.ai](https://www.novita.ai/)
|
+ [x] [novita.ai](https://www.novita.ai/)
|
||||||
|
+ [x] [硅基流动 SiliconCloud](https://siliconflow.cn/siliconcloud)
|
||||||
2. 支持配置镜像以及众多[第三方代理服务](https://iamazing.cn/page/openai-api-third-party-services)。
|
2. 支持配置镜像以及众多[第三方代理服务](https://iamazing.cn/page/openai-api-third-party-services)。
|
||||||
3. 支持通过**负载均衡**的方式访问多个渠道。
|
3. 支持通过**负载均衡**的方式访问多个渠道。
|
||||||
4. 支持 **stream 模式**,可以通过流式传输实现打字机效果。
|
4. 支持 **stream 模式**,可以通过流式传输实现打字机效果。
|
||||||
@@ -251,9 +252,9 @@ docker run --name chatgpt-web -d -p 3002:3002 -e OPENAI_API_BASE_URL=https://ope
|
|||||||
#### QChatGPT - QQ机器人
|
#### QChatGPT - QQ机器人
|
||||||
项目主页:https://github.com/RockChinQ/QChatGPT
|
项目主页:https://github.com/RockChinQ/QChatGPT
|
||||||
|
|
||||||
根据文档完成部署后,在`config.py`设置配置项`openai_config`的`reverse_proxy`为 One API 后端地址,设置`api_key`为 One API 生成的key,并在配置项`completion_api_params`的`model`参数设置为 One API 支持的模型名称。
|
根据[文档](https://qchatgpt.rockchin.top)完成部署后,在 `data/provider.json`设置`requester.openai-chat-completions.base-url`为 One API 实例地址,并填写 API Key 到 `keys.openai` 组中,设置 `model` 为要使用的模型名称。
|
||||||
|
|
||||||
可安装 [Switcher 插件](https://github.com/RockChinQ/Switcher)在运行时切换所使用的模型。
|
运行期间可以通过`!model`命令查看、切换可用模型。
|
||||||
|
|
||||||
### 部署到第三方平台
|
### 部署到第三方平台
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
13
model/log.go
13
model/log.go
@@ -3,6 +3,7 @@ package model
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
@@ -152,7 +153,11 @@ func SearchUserLogs(userId int, keyword string) (logs []*Log, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SumUsedQuota(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, channel int) (quota int64) {
|
func SumUsedQuota(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string, channel int) (quota int64) {
|
||||||
tx := LOG_DB.Table("logs").Select("ifnull(sum(quota),0)")
|
ifnull := "ifnull"
|
||||||
|
if common.UsingPostgreSQL {
|
||||||
|
ifnull = "COALESCE"
|
||||||
|
}
|
||||||
|
tx := LOG_DB.Table("logs").Select(fmt.Sprintf("%s(sum(quota),0)", ifnull))
|
||||||
if username != "" {
|
if username != "" {
|
||||||
tx = tx.Where("username = ?", username)
|
tx = tx.Where("username = ?", username)
|
||||||
}
|
}
|
||||||
@@ -176,7 +181,11 @@ 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) {
|
func SumUsedToken(logType int, startTimestamp int64, endTimestamp int64, modelName string, username string, tokenName string) (token int) {
|
||||||
tx := LOG_DB.Table("logs").Select("ifnull(sum(prompt_tokens),0) + ifnull(sum(completion_tokens),0)")
|
ifnull := "ifnull"
|
||||||
|
if common.UsingPostgreSQL {
|
||||||
|
ifnull = "COALESCE"
|
||||||
|
}
|
||||||
|
tx := LOG_DB.Table("logs").Select(fmt.Sprintf("%s(sum(prompt_tokens),0) + %s(sum(completion_tokens),0)", ifnull, ifnull))
|
||||||
if username != "" {
|
if username != "" {
|
||||||
tx = tx.Where("username = ?", username)
|
tx = tx.Where("username = ?", username)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ type Token struct {
|
|||||||
RemainQuota int64 `json:"remain_quota" gorm:"bigint;default:0"`
|
RemainQuota int64 `json:"remain_quota" gorm:"bigint;default:0"`
|
||||||
UnlimitedQuota bool `json:"unlimited_quota" gorm:"default:false"`
|
UnlimitedQuota bool `json:"unlimited_quota" gorm:"default:false"`
|
||||||
UsedQuota int64 `json:"used_quota" gorm:"bigint;default:0"` // used quota
|
UsedQuota int64 `json:"used_quota" gorm:"bigint;default:0"` // used quota
|
||||||
Models *string `json:"models" gorm:"default:''"` // allowed models
|
Models *string `json:"models" gorm:"type:text"` // allowed models
|
||||||
Subnet *string `json:"subnet" gorm:"default:''"` // allowed subnet
|
Subnet *string `json:"subnet" gorm:"default:''"` // allowed subnet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package monitor
|
package monitor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ShouldDisableChannel(err *model.Error, statusCode int) bool {
|
func ShouldDisableChannel(err *model.Error, statusCode int) bool {
|
||||||
@@ -18,31 +19,23 @@ func ShouldDisableChannel(err *model.Error, statusCode int) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
switch err.Type {
|
switch err.Type {
|
||||||
case "insufficient_quota":
|
case "insufficient_quota", "authentication_error", "permission_error", "forbidden":
|
||||||
return true
|
|
||||||
// https://docs.anthropic.com/claude/reference/errors
|
|
||||||
case "authentication_error":
|
|
||||||
return true
|
|
||||||
case "permission_error":
|
|
||||||
return true
|
|
||||||
case "forbidden":
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if err.Code == "invalid_api_key" || err.Code == "account_deactivated" {
|
if err.Code == "invalid_api_key" || err.Code == "account_deactivated" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(err.Message, "Your credit balance is too low") { // anthropic
|
|
||||||
return true
|
lowerMessage := strings.ToLower(err.Message)
|
||||||
} else if strings.HasPrefix(err.Message, "This organization has been disabled.") {
|
if strings.Contains(lowerMessage, "your access was terminated") ||
|
||||||
return true
|
strings.Contains(lowerMessage, "violation of our policies") ||
|
||||||
}
|
strings.Contains(lowerMessage, "your credit balance is too low") ||
|
||||||
//if strings.Contains(err.Message, "quota") {
|
strings.Contains(lowerMessage, "organization has been disabled") ||
|
||||||
// return true
|
strings.Contains(lowerMessage, "credit") ||
|
||||||
//}
|
strings.Contains(lowerMessage, "balance") ||
|
||||||
if strings.Contains(err.Message, "credit") {
|
strings.Contains(lowerMessage, "permission denied") ||
|
||||||
return true
|
strings.Contains(lowerMessage, "organization has been restricted") || // groq
|
||||||
}
|
strings.Contains(lowerMessage, "已欠费") {
|
||||||
if strings.Contains(err.Message, "balance") {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package ali
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/songquanpeng/one-api/common/ctxkey"
|
||||||
"github.com/songquanpeng/one-api/common/render"
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -59,7 +60,7 @@ func ConvertRequest(request model.GeneralOpenAIRequest) *ChatRequest {
|
|||||||
|
|
||||||
func ConvertEmbeddingRequest(request model.GeneralOpenAIRequest) *EmbeddingRequest {
|
func ConvertEmbeddingRequest(request model.GeneralOpenAIRequest) *EmbeddingRequest {
|
||||||
return &EmbeddingRequest{
|
return &EmbeddingRequest{
|
||||||
Model: "text-embedding-v1",
|
Model: request.Model,
|
||||||
Input: struct {
|
Input: struct {
|
||||||
Texts []string `json:"texts"`
|
Texts []string `json:"texts"`
|
||||||
}{
|
}{
|
||||||
@@ -102,8 +103,9 @@ func EmbeddingHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStat
|
|||||||
StatusCode: resp.StatusCode,
|
StatusCode: resp.StatusCode,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
requestModel := c.GetString(ctxkey.RequestModel)
|
||||||
fullTextResponse := embeddingResponseAli2OpenAI(&aliResponse)
|
fullTextResponse := embeddingResponseAli2OpenAI(&aliResponse)
|
||||||
|
fullTextResponse.Model = requestModel
|
||||||
jsonResponse, err := json.Marshal(fullTextResponse)
|
jsonResponse, err := json.Marshal(fullTextResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), nil
|
return openai.ErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cloudflare
|
package cloudflare
|
||||||
|
|
||||||
var ModelList = []string{
|
var ModelList = []string{
|
||||||
|
"@cf/meta/llama-3.1-8b-instruct",
|
||||||
"@cf/meta/llama-2-7b-chat-fp16",
|
"@cf/meta/llama-2-7b-chat-fp16",
|
||||||
"@cf/meta/llama-2-7b-chat-int8",
|
"@cf/meta/llama-2-7b-chat-int8",
|
||||||
"@cf/mistral/mistral-7b-instruct-v0.1",
|
"@cf/mistral/mistral-7b-instruct-v0.1",
|
||||||
|
|||||||
@@ -7,8 +7,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetRequestURL(meta *meta.Meta) (string, error) {
|
func GetRequestURL(meta *meta.Meta) (string, error) {
|
||||||
if meta.Mode == relaymode.ChatCompletions {
|
switch meta.Mode {
|
||||||
|
case relaymode.ChatCompletions:
|
||||||
return fmt.Sprintf("%s/api/v3/chat/completions", meta.BaseURL), nil
|
return fmt.Sprintf("%s/api/v3/chat/completions", meta.BaseURL), nil
|
||||||
|
case relaymode.Embeddings:
|
||||||
|
return fmt.Sprintf("%s/api/v3/embeddings", meta.BaseURL), nil
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("unsupported relay mode %d for doubao", meta.Mode)
|
return "", fmt.Errorf("unsupported relay mode %d for doubao", meta.Mode)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,5 @@ package gemini
|
|||||||
// https://ai.google.dev/models/gemini
|
// https://ai.google.dev/models/gemini
|
||||||
|
|
||||||
var ModelList = []string{
|
var ModelList = []string{
|
||||||
"gemini-pro", "gemini-1.0-pro-001", "gemini-1.5-pro",
|
"gemini-pro", "gemini-1.0-pro", "gemini-1.5-flash", "gemini-1.5-pro", "text-embedding-004", "aqa",
|
||||||
"gemini-pro-vision", "gemini-1.0-pro-vision-001", "embedding-001", "text-embedding-004",
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,14 @@ package groq
|
|||||||
|
|
||||||
var ModelList = []string{
|
var ModelList = []string{
|
||||||
"gemma-7b-it",
|
"gemma-7b-it",
|
||||||
"llama2-7b-2048",
|
|
||||||
"llama2-70b-4096",
|
|
||||||
"mixtral-8x7b-32768",
|
"mixtral-8x7b-32768",
|
||||||
"llama3-8b-8192",
|
"llama3-8b-8192",
|
||||||
"llama3-70b-8192",
|
"llama3-70b-8192",
|
||||||
|
"gemma2-9b-it",
|
||||||
|
"llama-3.1-405b-reasoning",
|
||||||
|
"llama-3.1-70b-versatile",
|
||||||
|
"llama-3.1-8b-instant",
|
||||||
|
"llama3-groq-70b-8192-tool-use-preview",
|
||||||
|
"llama3-groq-8b-8192-tool-use-preview",
|
||||||
|
"whisper-large-v3",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
|
|||||||
// https://github.com/ollama/ollama/blob/main/docs/api.md
|
// https://github.com/ollama/ollama/blob/main/docs/api.md
|
||||||
fullRequestURL := fmt.Sprintf("%s/api/chat", meta.BaseURL)
|
fullRequestURL := fmt.Sprintf("%s/api/chat", meta.BaseURL)
|
||||||
if meta.Mode == relaymode.Embeddings {
|
if meta.Mode == relaymode.Embeddings {
|
||||||
fullRequestURL = fmt.Sprintf("%s/api/embeddings", meta.BaseURL)
|
fullRequestURL = fmt.Sprintf("%s/api/embed", meta.BaseURL)
|
||||||
}
|
}
|
||||||
return fullRequestURL, nil
|
return fullRequestURL, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ func ConvertRequest(request model.GeneralOpenAIRequest) *ChatRequest {
|
|||||||
TopP: request.TopP,
|
TopP: request.TopP,
|
||||||
FrequencyPenalty: request.FrequencyPenalty,
|
FrequencyPenalty: request.FrequencyPenalty,
|
||||||
PresencePenalty: request.PresencePenalty,
|
PresencePenalty: request.PresencePenalty,
|
||||||
|
NumPredict: request.MaxTokens,
|
||||||
|
NumCtx: request.NumCtx,
|
||||||
},
|
},
|
||||||
Stream: request.Stream,
|
Stream: request.Stream,
|
||||||
}
|
}
|
||||||
@@ -118,8 +120,10 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
|
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
data := strings.TrimPrefix(scanner.Text(), "}")
|
data := scanner.Text()
|
||||||
data = data + "}"
|
if strings.HasPrefix(data, "}") {
|
||||||
|
data = strings.TrimPrefix(data, "}") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
var ollamaResponse ChatResponse
|
var ollamaResponse ChatResponse
|
||||||
err := json.Unmarshal([]byte(data), &ollamaResponse)
|
err := json.Unmarshal([]byte(data), &ollamaResponse)
|
||||||
@@ -157,8 +161,15 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
|
|
||||||
func ConvertEmbeddingRequest(request model.GeneralOpenAIRequest) *EmbeddingRequest {
|
func ConvertEmbeddingRequest(request model.GeneralOpenAIRequest) *EmbeddingRequest {
|
||||||
return &EmbeddingRequest{
|
return &EmbeddingRequest{
|
||||||
Model: request.Model,
|
Model: request.Model,
|
||||||
Prompt: strings.Join(request.ParseInput(), " "),
|
Input: request.ParseInput(),
|
||||||
|
Options: &Options{
|
||||||
|
Seed: int(request.Seed),
|
||||||
|
Temperature: request.Temperature,
|
||||||
|
TopP: request.TopP,
|
||||||
|
FrequencyPenalty: request.FrequencyPenalty,
|
||||||
|
PresencePenalty: request.PresencePenalty,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,15 +212,17 @@ func embeddingResponseOllama2OpenAI(response *EmbeddingResponse) *openai.Embeddi
|
|||||||
openAIEmbeddingResponse := openai.EmbeddingResponse{
|
openAIEmbeddingResponse := openai.EmbeddingResponse{
|
||||||
Object: "list",
|
Object: "list",
|
||||||
Data: make([]openai.EmbeddingResponseItem, 0, 1),
|
Data: make([]openai.EmbeddingResponseItem, 0, 1),
|
||||||
Model: "text-embedding-v1",
|
Model: response.Model,
|
||||||
Usage: model.Usage{TotalTokens: 0},
|
Usage: model.Usage{TotalTokens: 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
openAIEmbeddingResponse.Data = append(openAIEmbeddingResponse.Data, openai.EmbeddingResponseItem{
|
for i, embedding := range response.Embeddings {
|
||||||
Object: `embedding`,
|
openAIEmbeddingResponse.Data = append(openAIEmbeddingResponse.Data, openai.EmbeddingResponseItem{
|
||||||
Index: 0,
|
Object: `embedding`,
|
||||||
Embedding: response.Embedding,
|
Index: i,
|
||||||
})
|
Embedding: embedding,
|
||||||
|
})
|
||||||
|
}
|
||||||
return &openAIEmbeddingResponse
|
return &openAIEmbeddingResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ type Options struct {
|
|||||||
TopP float64 `json:"top_p,omitempty"`
|
TopP float64 `json:"top_p,omitempty"`
|
||||||
FrequencyPenalty float64 `json:"frequency_penalty,omitempty"`
|
FrequencyPenalty float64 `json:"frequency_penalty,omitempty"`
|
||||||
PresencePenalty float64 `json:"presence_penalty,omitempty"`
|
PresencePenalty float64 `json:"presence_penalty,omitempty"`
|
||||||
|
NumPredict int `json:"num_predict,omitempty"`
|
||||||
|
NumCtx int `json:"num_ctx,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
@@ -37,11 +39,15 @@ type ChatResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EmbeddingRequest struct {
|
type EmbeddingRequest struct {
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
Prompt string `json:"prompt"`
|
Input []string `json:"input"`
|
||||||
|
// Truncate bool `json:"truncate,omitempty"`
|
||||||
|
Options *Options `json:"options,omitempty"`
|
||||||
|
// KeepAlive string `json:"keep_alive,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmbeddingResponse struct {
|
type EmbeddingResponse struct {
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
Embedding []float64 `json:"embedding,omitempty"`
|
Model string `json:"model"`
|
||||||
|
Embeddings [][]float64 `json:"embeddings"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/adaptor/novita"
|
"github.com/songquanpeng/one-api/relay/adaptor/novita"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/stepfun"
|
"github.com/songquanpeng/one-api/relay/adaptor/stepfun"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/togetherai"
|
"github.com/songquanpeng/one-api/relay/adaptor/togetherai"
|
||||||
|
"github.com/songquanpeng/one-api/relay/adaptor/siliconflow"
|
||||||
"github.com/songquanpeng/one-api/relay/channeltype"
|
"github.com/songquanpeng/one-api/relay/channeltype"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ var CompatibleChannels = []int{
|
|||||||
channeltype.DeepSeek,
|
channeltype.DeepSeek,
|
||||||
channeltype.TogetherAI,
|
channeltype.TogetherAI,
|
||||||
channeltype.Novita,
|
channeltype.Novita,
|
||||||
|
channeltype.SiliconFlow,
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCompatibleChannelMeta(channelType int) (string, []string) {
|
func GetCompatibleChannelMeta(channelType int) (string, []string) {
|
||||||
@@ -60,6 +62,8 @@ func GetCompatibleChannelMeta(channelType int) (string, []string) {
|
|||||||
return "doubao", doubao.ModelList
|
return "doubao", doubao.ModelList
|
||||||
case channeltype.Novita:
|
case channeltype.Novita:
|
||||||
return "novita", novita.ModelList
|
return "novita", novita.ModelList
|
||||||
|
case channeltype.SiliconFlow:
|
||||||
|
return "siliconflow", siliconflow.ModelList
|
||||||
default:
|
default:
|
||||||
return "openai", ModelList
|
return "openai", ModelList
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ var ModelList = []string{
|
|||||||
"gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613",
|
"gpt-4-32k", "gpt-4-32k-0314", "gpt-4-32k-0613",
|
||||||
"gpt-4-turbo-preview", "gpt-4-turbo", "gpt-4-turbo-2024-04-09",
|
"gpt-4-turbo-preview", "gpt-4-turbo", "gpt-4-turbo-2024-04-09",
|
||||||
"gpt-4o", "gpt-4o-2024-05-13",
|
"gpt-4o", "gpt-4o-2024-05-13",
|
||||||
|
"gpt-4o-mini", "gpt-4o-mini-2024-07-18",
|
||||||
"gpt-4-vision-preview",
|
"gpt-4-vision-preview",
|
||||||
"text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large",
|
"text-embedding-ada-002", "text-embedding-3-small", "text-embedding-3-large",
|
||||||
"text-curie-001", "text-babbage-001", "text-ada-001", "text-davinci-002", "text-davinci-003",
|
"text-curie-001", "text-babbage-001", "text-ada-001", "text-davinci-002", "text-davinci-003",
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ func CountTokenMessages(messages []model.Message, model string) int {
|
|||||||
if imageUrl["detail"] != nil {
|
if imageUrl["detail"] != nil {
|
||||||
detail = imageUrl["detail"].(string)
|
detail = imageUrl["detail"].(string)
|
||||||
}
|
}
|
||||||
imageTokens, err := countImageTokens(url, detail)
|
imageTokens, err := countImageTokens(url, detail, model)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.SysError("error counting image tokens: " + err.Error())
|
logger.SysError("error counting image tokens: " + err.Error())
|
||||||
} else {
|
} else {
|
||||||
@@ -134,11 +134,15 @@ const (
|
|||||||
lowDetailCost = 85
|
lowDetailCost = 85
|
||||||
highDetailCostPerTile = 170
|
highDetailCostPerTile = 170
|
||||||
additionalCost = 85
|
additionalCost = 85
|
||||||
|
// gpt-4o-mini cost higher than other model
|
||||||
|
gpt4oMiniLowDetailCost = 2833
|
||||||
|
gpt4oMiniHighDetailCost = 5667
|
||||||
|
gpt4oMiniAdditionalCost = 2833
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://platform.openai.com/docs/guides/vision/calculating-costs
|
// https://platform.openai.com/docs/guides/vision/calculating-costs
|
||||||
// https://github.com/openai/openai-cookbook/blob/05e3f9be4c7a2ae7ecf029a7c32065b024730ebe/examples/How_to_count_tokens_with_tiktoken.ipynb
|
// https://github.com/openai/openai-cookbook/blob/05e3f9be4c7a2ae7ecf029a7c32065b024730ebe/examples/How_to_count_tokens_with_tiktoken.ipynb
|
||||||
func countImageTokens(url string, detail string) (_ int, err error) {
|
func countImageTokens(url string, detail string, model string) (_ int, err error) {
|
||||||
var fetchSize = true
|
var fetchSize = true
|
||||||
var width, height int
|
var width, height int
|
||||||
// Reference: https://platform.openai.com/docs/guides/vision/low-or-high-fidelity-image-understanding
|
// Reference: https://platform.openai.com/docs/guides/vision/low-or-high-fidelity-image-understanding
|
||||||
@@ -172,6 +176,9 @@ func countImageTokens(url string, detail string) (_ int, err error) {
|
|||||||
}
|
}
|
||||||
switch detail {
|
switch detail {
|
||||||
case "low":
|
case "low":
|
||||||
|
if strings.HasPrefix(model, "gpt-4o-mini") {
|
||||||
|
return gpt4oMiniLowDetailCost, nil
|
||||||
|
}
|
||||||
return lowDetailCost, nil
|
return lowDetailCost, nil
|
||||||
case "high":
|
case "high":
|
||||||
if fetchSize {
|
if fetchSize {
|
||||||
@@ -191,6 +198,9 @@ func countImageTokens(url string, detail string) (_ int, err error) {
|
|||||||
height = int(float64(height) * ratio)
|
height = int(float64(height) * ratio)
|
||||||
}
|
}
|
||||||
numSquares := int(math.Ceil(float64(width)/512) * math.Ceil(float64(height)/512))
|
numSquares := int(math.Ceil(float64(width)/512) * math.Ceil(float64(height)/512))
|
||||||
|
if strings.HasPrefix(model, "gpt-4o-mini") {
|
||||||
|
return numSquares*gpt4oMiniHighDetailCost + gpt4oMiniAdditionalCost, nil
|
||||||
|
}
|
||||||
result := numSquares*highDetailCostPerTile + additionalCost
|
result := numSquares*highDetailCostPerTile + additionalCost
|
||||||
return result, nil
|
return result, nil
|
||||||
default:
|
default:
|
||||||
|
|||||||
36
relay/adaptor/siliconflow/constants.go
Normal file
36
relay/adaptor/siliconflow/constants.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package siliconflow
|
||||||
|
|
||||||
|
// https://docs.siliconflow.cn/docs/getting-started
|
||||||
|
|
||||||
|
var ModelList = []string{
|
||||||
|
"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",
|
||||||
|
"THUDM/glm-4-9b-chat",
|
||||||
|
"Qwen/Qwen2-72B-Instruct",
|
||||||
|
"Qwen/Qwen2-7B-Instruct",
|
||||||
|
"Qwen/Qwen2-57B-A14B-Instruct",
|
||||||
|
"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",
|
||||||
|
}
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
package stepfun
|
package stepfun
|
||||||
|
|
||||||
var ModelList = []string{
|
var ModelList = []string{
|
||||||
|
"step-1-8k",
|
||||||
"step-1-32k",
|
"step-1-32k",
|
||||||
|
"step-1-128k",
|
||||||
|
"step-1-256k",
|
||||||
|
"step-1-flash",
|
||||||
|
"step-2-16k",
|
||||||
|
"step-1v-8k",
|
||||||
"step-1v-32k",
|
"step-1v-32k",
|
||||||
"step-1-200k",
|
"step-1x-medium",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,14 @@ var ImageSizeRatios = map[string]map[string]float64{
|
|||||||
"720x1280": 1,
|
"720x1280": 1,
|
||||||
"1280x720": 1,
|
"1280x720": 1,
|
||||||
},
|
},
|
||||||
|
"step-1x-medium": {
|
||||||
|
"256x256": 1,
|
||||||
|
"512x512": 1,
|
||||||
|
"768x768": 1,
|
||||||
|
"1024x1024": 1,
|
||||||
|
"1280x800": 1,
|
||||||
|
"800x1280": 1,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var ImageGenerationAmounts = map[string][2]int{
|
var ImageGenerationAmounts = map[string][2]int{
|
||||||
@@ -39,6 +47,7 @@ var ImageGenerationAmounts = map[string][2]int{
|
|||||||
"ali-stable-diffusion-v1.5": {1, 4}, // Ali
|
"ali-stable-diffusion-v1.5": {1, 4}, // Ali
|
||||||
"wanx-v1": {1, 4}, // Ali
|
"wanx-v1": {1, 4}, // Ali
|
||||||
"cogview-3": {1, 1},
|
"cogview-3": {1, 1},
|
||||||
|
"step-1x-medium": {1, 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
var ImagePromptLengthLimitations = map[string]int{
|
var ImagePromptLengthLimitations = map[string]int{
|
||||||
@@ -48,6 +57,7 @@ var ImagePromptLengthLimitations = map[string]int{
|
|||||||
"ali-stable-diffusion-v1.5": 4000,
|
"ali-stable-diffusion-v1.5": 4000,
|
||||||
"wanx-v1": 4000,
|
"wanx-v1": 4000,
|
||||||
"cogview-3": 833,
|
"cogview-3": 833,
|
||||||
|
"step-1x-medium": 4000,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ImageOriginModelName = map[string]string{
|
var ImageOriginModelName = map[string]string{
|
||||||
|
|||||||
@@ -28,15 +28,17 @@ var ModelRatio = map[string]float64{
|
|||||||
"gpt-4-32k": 30,
|
"gpt-4-32k": 30,
|
||||||
"gpt-4-32k-0314": 30,
|
"gpt-4-32k-0314": 30,
|
||||||
"gpt-4-32k-0613": 30,
|
"gpt-4-32k-0613": 30,
|
||||||
"gpt-4-1106-preview": 5, // $0.01 / 1K tokens
|
"gpt-4-1106-preview": 5, // $0.01 / 1K tokens
|
||||||
"gpt-4-0125-preview": 5, // $0.01 / 1K tokens
|
"gpt-4-0125-preview": 5, // $0.01 / 1K tokens
|
||||||
"gpt-4-turbo-preview": 5, // $0.01 / 1K tokens
|
"gpt-4-turbo-preview": 5, // $0.01 / 1K tokens
|
||||||
"gpt-4-turbo": 5, // $0.01 / 1K tokens
|
"gpt-4-turbo": 5, // $0.01 / 1K tokens
|
||||||
"gpt-4-turbo-2024-04-09": 5, // $0.01 / 1K tokens
|
"gpt-4-turbo-2024-04-09": 5, // $0.01 / 1K tokens
|
||||||
"gpt-4o": 2.5, // $0.005 / 1K tokens
|
"gpt-4o": 2.5, // $0.005 / 1K tokens
|
||||||
"gpt-4o-2024-05-13": 2.5, // $0.005 / 1K tokens
|
"gpt-4o-2024-05-13": 2.5, // $0.005 / 1K tokens
|
||||||
"gpt-4-vision-preview": 5, // $0.01 / 1K tokens
|
"gpt-4o-mini": 0.075, // $0.00015 / 1K tokens
|
||||||
"gpt-3.5-turbo": 0.25, // $0.0005 / 1K tokens
|
"gpt-4o-mini-2024-07-18": 0.075, // $0.00015 / 1K tokens
|
||||||
|
"gpt-4-vision-preview": 5, // $0.01 / 1K tokens
|
||||||
|
"gpt-3.5-turbo": 0.25, // $0.0005 / 1K tokens
|
||||||
"gpt-3.5-turbo-0301": 0.75,
|
"gpt-3.5-turbo-0301": 0.75,
|
||||||
"gpt-3.5-turbo-0613": 0.75,
|
"gpt-3.5-turbo-0613": 0.75,
|
||||||
"gpt-3.5-turbo-16k": 1.5, // $0.003 / 1K tokens
|
"gpt-3.5-turbo-16k": 1.5, // $0.003 / 1K tokens
|
||||||
@@ -96,12 +98,11 @@ var ModelRatio = map[string]float64{
|
|||||||
"bge-large-en": 0.002 * RMB,
|
"bge-large-en": 0.002 * RMB,
|
||||||
"tao-8k": 0.002 * RMB,
|
"tao-8k": 0.002 * RMB,
|
||||||
// https://ai.google.dev/pricing
|
// https://ai.google.dev/pricing
|
||||||
"PaLM-2": 1,
|
"gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
|
||||||
"gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
|
"gemini-1.0-pro": 1,
|
||||||
"gemini-pro-vision": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
|
"gemini-1.5-flash": 1,
|
||||||
"gemini-1.0-pro-vision-001": 1,
|
"gemini-1.5-pro": 1,
|
||||||
"gemini-1.0-pro-001": 1,
|
"aqa": 1,
|
||||||
"gemini-1.5-pro": 1,
|
|
||||||
// https://open.bigmodel.cn/pricing
|
// https://open.bigmodel.cn/pricing
|
||||||
"glm-4": 0.1 * RMB,
|
"glm-4": 0.1 * RMB,
|
||||||
"glm-4v": 0.1 * RMB,
|
"glm-4v": 0.1 * RMB,
|
||||||
@@ -156,20 +157,29 @@ var ModelRatio = map[string]float64{
|
|||||||
"mistral-large-latest": 8.0 / 1000 * USD,
|
"mistral-large-latest": 8.0 / 1000 * USD,
|
||||||
"mistral-embed": 0.1 / 1000 * USD,
|
"mistral-embed": 0.1 / 1000 * USD,
|
||||||
// https://wow.groq.com/#:~:text=inquiries%C2%A0here.-,Model,-Current%20Speed
|
// https://wow.groq.com/#:~:text=inquiries%C2%A0here.-,Model,-Current%20Speed
|
||||||
"llama3-70b-8192": 0.59 / 1000 * USD,
|
"gemma-7b-it": 0.07 / 1000000 * USD,
|
||||||
"mixtral-8x7b-32768": 0.27 / 1000 * USD,
|
"mixtral-8x7b-32768": 0.24 / 1000000 * USD,
|
||||||
"llama3-8b-8192": 0.05 / 1000 * USD,
|
"llama3-8b-8192": 0.05 / 1000000 * USD,
|
||||||
"gemma-7b-it": 0.1 / 1000 * USD,
|
"llama3-70b-8192": 0.59 / 1000000 * USD,
|
||||||
"llama2-70b-4096": 0.64 / 1000 * USD,
|
"gemma2-9b-it": 0.20 / 1000000 * USD,
|
||||||
"llama2-7b-2048": 0.1 / 1000 * USD,
|
"llama-3.1-405b-reasoning": 0.89 / 1000000 * USD,
|
||||||
|
"llama-3.1-70b-versatile": 0.59 / 1000000 * USD,
|
||||||
|
"llama-3.1-8b-instant": 0.05 / 1000000 * USD,
|
||||||
|
"llama3-groq-70b-8192-tool-use-preview": 0.89 / 1000000 * USD,
|
||||||
|
"llama3-groq-8b-8192-tool-use-preview": 0.19 / 1000000 * USD,
|
||||||
// https://platform.lingyiwanwu.com/docs#-计费单元
|
// https://platform.lingyiwanwu.com/docs#-计费单元
|
||||||
"yi-34b-chat-0205": 2.5 / 1000 * RMB,
|
"yi-34b-chat-0205": 2.5 / 1000 * RMB,
|
||||||
"yi-34b-chat-200k": 12.0 / 1000 * RMB,
|
"yi-34b-chat-200k": 12.0 / 1000 * RMB,
|
||||||
"yi-vl-plus": 6.0 / 1000 * RMB,
|
"yi-vl-plus": 6.0 / 1000 * RMB,
|
||||||
// stepfun todo
|
// https://platform.stepfun.com/docs/pricing/details
|
||||||
"step-1v-32k": 0.024 * RMB,
|
"step-1-8k": 0.005 / 1000 * RMB,
|
||||||
"step-1-32k": 0.024 * RMB,
|
"step-1-32k": 0.015 / 1000 * RMB,
|
||||||
"step-1-200k": 0.15 * RMB,
|
"step-1-128k": 0.040 / 1000 * RMB,
|
||||||
|
"step-1-256k": 0.095 / 1000 * RMB,
|
||||||
|
"step-1-flash": 0.001 / 1000 * RMB,
|
||||||
|
"step-2-16k": 0.038 / 1000 * RMB,
|
||||||
|
"step-1v-8k": 0.005 / 1000 * RMB,
|
||||||
|
"step-1v-32k": 0.015 / 1000 * RMB,
|
||||||
// aws llama3 https://aws.amazon.com/cn/bedrock/pricing/
|
// aws llama3 https://aws.amazon.com/cn/bedrock/pricing/
|
||||||
"llama3-8b-8192(33)": 0.0003 / 0.002, // $0.0003 / 1K tokens
|
"llama3-8b-8192(33)": 0.0003 / 0.002, // $0.0003 / 1K tokens
|
||||||
"llama3-70b-8192(33)": 0.00265 / 0.002, // $0.00265 / 1K tokens
|
"llama3-70b-8192(33)": 0.00265 / 0.002, // $0.00265 / 1K tokens
|
||||||
@@ -195,8 +205,10 @@ var CompletionRatio = map[string]float64{
|
|||||||
"llama3-70b-8192(33)": 0.0035 / 0.00265,
|
"llama3-70b-8192(33)": 0.0035 / 0.00265,
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultModelRatio map[string]float64
|
var (
|
||||||
var DefaultCompletionRatio map[string]float64
|
DefaultModelRatio map[string]float64
|
||||||
|
DefaultCompletionRatio map[string]float64
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
DefaultModelRatio = make(map[string]float64)
|
DefaultModelRatio = make(map[string]float64)
|
||||||
@@ -308,6 +320,9 @@ func GetCompletionRatio(name string, channelType int) float64 {
|
|||||||
return 4.0 / 3.0
|
return 4.0 / 3.0
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(name, "gpt-4") {
|
if strings.HasPrefix(name, "gpt-4") {
|
||||||
|
if strings.HasPrefix(name, "gpt-4o-mini") {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
if strings.HasPrefix(name, "gpt-4-turbo") ||
|
if strings.HasPrefix(name, "gpt-4-turbo") ||
|
||||||
strings.HasPrefix(name, "gpt-4o") ||
|
strings.HasPrefix(name, "gpt-4o") ||
|
||||||
strings.HasSuffix(name, "preview") {
|
strings.HasSuffix(name, "preview") {
|
||||||
|
|||||||
@@ -45,5 +45,6 @@ const (
|
|||||||
Novita
|
Novita
|
||||||
VertextAI
|
VertextAI
|
||||||
Proxy
|
Proxy
|
||||||
|
SiliconFlow
|
||||||
Dummy
|
Dummy
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ var ChannelBaseURLs = []string{
|
|||||||
"https://api.novita.ai/v3/openai", // 41
|
"https://api.novita.ai/v3/openai", // 41
|
||||||
"", // 42
|
"", // 42
|
||||||
"", // 43
|
"", // 43
|
||||||
|
"https://api.siliconflow.cn", // 44
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/relay"
|
"github.com/songquanpeng/one-api/relay"
|
||||||
|
"github.com/songquanpeng/one-api/relay/adaptor"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/apitype"
|
"github.com/songquanpeng/one-api/relay/apitype"
|
||||||
"github.com/songquanpeng/one-api/relay/billing"
|
"github.com/songquanpeng/one-api/relay/billing"
|
||||||
@@ -31,9 +32,8 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
meta.IsStream = textRequest.Stream
|
meta.IsStream = textRequest.Stream
|
||||||
|
|
||||||
// map model name
|
// map model name
|
||||||
var isModelMapped bool
|
|
||||||
meta.OriginModelName = textRequest.Model
|
meta.OriginModelName = textRequest.Model
|
||||||
textRequest.Model, isModelMapped = getMappedModelName(textRequest.Model, meta.ModelMapping)
|
textRequest.Model, _ = getMappedModelName(textRequest.Model, meta.ModelMapping)
|
||||||
meta.ActualModelName = textRequest.Model
|
meta.ActualModelName = textRequest.Model
|
||||||
// get model ratio & group ratio
|
// get model ratio & group ratio
|
||||||
modelRatio := billingratio.GetModelRatio(textRequest.Model, meta.ChannelType)
|
modelRatio := billingratio.GetModelRatio(textRequest.Model, meta.ChannelType)
|
||||||
@@ -55,30 +55,9 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
adaptor.Init(meta)
|
adaptor.Init(meta)
|
||||||
|
|
||||||
// get request body
|
// get request body
|
||||||
var requestBody io.Reader
|
requestBody, err := getRequestBody(c, meta, textRequest, adaptor)
|
||||||
if meta.APIType == apitype.OpenAI {
|
if err != nil {
|
||||||
// no need to convert request for openai
|
return openai.ErrorWrapper(err, "convert_request_failed", http.StatusInternalServerError)
|
||||||
shouldResetRequestBody := isModelMapped || meta.ChannelType == channeltype.Baichuan // frequency_penalty 0 is not acceptable for baichuan
|
|
||||||
if shouldResetRequestBody {
|
|
||||||
jsonStr, err := json.Marshal(textRequest)
|
|
||||||
if err != nil {
|
|
||||||
return openai.ErrorWrapper(err, "json_marshal_failed", http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
requestBody = bytes.NewBuffer(jsonStr)
|
|
||||||
} else {
|
|
||||||
requestBody = c.Request.Body
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
convertedRequest, err := adaptor.ConvertRequest(c, meta.Mode, textRequest)
|
|
||||||
if err != nil {
|
|
||||||
return openai.ErrorWrapper(err, "convert_request_failed", http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
jsonData, err := json.Marshal(convertedRequest)
|
|
||||||
if err != nil {
|
|
||||||
return openai.ErrorWrapper(err, "json_marshal_failed", http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
logger.Debugf(ctx, "converted request: \n%s", string(jsonData))
|
|
||||||
requestBody = bytes.NewBuffer(jsonData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// do request
|
// do request
|
||||||
@@ -103,3 +82,26 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
go postConsumeQuota(ctx, usage, meta, textRequest, ratio, preConsumedQuota, modelRatio, groupRatio)
|
go postConsumeQuota(ctx, usage, meta, textRequest, ratio, preConsumedQuota, modelRatio, groupRatio)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getRequestBody(c *gin.Context, meta *meta.Meta, textRequest *model.GeneralOpenAIRequest, adaptor adaptor.Adaptor) (io.Reader, error) {
|
||||||
|
if meta.APIType == apitype.OpenAI && meta.OriginModelName == meta.ActualModelName && meta.ChannelType != channeltype.Baichuan {
|
||||||
|
// no need to convert request for openai
|
||||||
|
return c.Request.Body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get request body
|
||||||
|
var requestBody io.Reader
|
||||||
|
convertedRequest, err := adaptor.ConvertRequest(c, meta.Mode, textRequest)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debugf(c.Request.Context(), "converted request failed: %s\n", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
jsonData, err := json.Marshal(convertedRequest)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debugf(c.Request.Context(), "converted request json_marshal_failed: %s\n", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logger.Debugf(c.Request.Context(), "converted request: \n%s", string(jsonData))
|
||||||
|
requestBody = bytes.NewBuffer(jsonData)
|
||||||
|
return requestBody, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type ResponseFormat struct {
|
type ResponseFormat struct {
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
|
JsonSchema *JSONSchema `json:"json_schema,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JSONSchema struct {
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Schema map[string]interface{} `json:"schema,omitempty"`
|
||||||
|
Strict *bool `json:"strict,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GeneralOpenAIRequest struct {
|
type GeneralOpenAIRequest struct {
|
||||||
@@ -29,6 +37,7 @@ type GeneralOpenAIRequest struct {
|
|||||||
Dimensions int `json:"dimensions,omitempty"`
|
Dimensions int `json:"dimensions,omitempty"`
|
||||||
Instruction string `json:"instruction,omitempty"`
|
Instruction string `json:"instruction,omitempty"`
|
||||||
Size string `json:"size,omitempty"`
|
Size string `json:"size,omitempty"`
|
||||||
|
NumCtx int `json:"num_ctx,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r GeneralOpenAIRequest) ParseInput() []string {
|
func (r GeneralOpenAIRequest) ParseInput() []string {
|
||||||
|
|||||||
@@ -11,12 +11,14 @@ import EditToken from '../pages/Token/EditToken';
|
|||||||
const COPY_OPTIONS = [
|
const COPY_OPTIONS = [
|
||||||
{ key: 'next', text: 'ChatGPT Next Web', value: 'next' },
|
{ key: 'next', text: 'ChatGPT Next Web', value: 'next' },
|
||||||
{ key: 'ama', text: 'ChatGPT Web & Midjourney', value: 'ama' },
|
{ key: 'ama', text: 'ChatGPT Web & Midjourney', value: 'ama' },
|
||||||
{ key: 'opencat', text: 'OpenCat', value: 'opencat' }
|
{ key: 'opencat', text: 'OpenCat', value: 'opencat' },
|
||||||
|
{ key: 'lobechat', text: 'LobeChat', value: 'lobechat' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const OPEN_LINK_OPTIONS = [
|
const OPEN_LINK_OPTIONS = [
|
||||||
{ key: 'ama', text: 'ChatGPT Web & Midjourney', value: 'ama' },
|
{ key: 'ama', text: 'ChatGPT Web & Midjourney', value: 'ama' },
|
||||||
{ key: 'opencat', text: 'OpenCat', value: 'opencat' }
|
{ key: 'opencat', text: 'OpenCat', value: 'opencat' },
|
||||||
|
{ key: 'lobechat', text: 'LobeChat', value: 'lobechat' }
|
||||||
];
|
];
|
||||||
|
|
||||||
function renderTimestamp(timestamp) {
|
function renderTimestamp(timestamp) {
|
||||||
@@ -60,7 +62,12 @@ const TokensTable = () => {
|
|||||||
onOpenLink('next-mj');
|
onOpenLink('next-mj');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ node: 'item', key: 'opencat', name: 'OpenCat', value: 'opencat' }
|
{ node: 'item', key: 'opencat', name: 'OpenCat', value: 'opencat' },
|
||||||
|
{
|
||||||
|
node: 'item', key: 'lobechat', name: 'LobeChat', onClick: () => {
|
||||||
|
onOpenLink('lobechat');
|
||||||
|
}
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
@@ -177,6 +184,11 @@ const TokensTable = () => {
|
|||||||
node: 'item', key: 'opencat', name: 'OpenCat', onClick: () => {
|
node: 'item', key: 'opencat', name: 'OpenCat', onClick: () => {
|
||||||
onOpenLink('opencat', record.key);
|
onOpenLink('opencat', record.key);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node: 'item', key: 'lobechat', name: 'LobeChat', onClick: () => {
|
||||||
|
onOpenLink('lobechat');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -382,6 +394,9 @@ const TokensTable = () => {
|
|||||||
case 'next-mj':
|
case 'next-mj':
|
||||||
url = mjLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
url = mjLink + `/#/?settings={"key":"sk-${key}","url":"${serverAddress}"}`;
|
||||||
break;
|
break;
|
||||||
|
case 'lobechat':
|
||||||
|
url = chatLink + `/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${serverAddress}"/v1"}}}`;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (!chatLink) {
|
if (!chatLink) {
|
||||||
showError('管理员未设置聊天链接');
|
showError('管理员未设置聊天链接');
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export const CHANNEL_OPTIONS = [
|
|||||||
{ key: 39, text: 'together.ai', value: 39, color: 'blue' },
|
{ key: 39, text: 'together.ai', value: 39, color: 'blue' },
|
||||||
{ key: 42, text: 'VertexAI', value: 42, color: 'blue' },
|
{ key: 42, text: 'VertexAI', value: 42, color: 'blue' },
|
||||||
{ key: 43, text: 'Proxy', value: 43, color: 'blue' },
|
{ key: 43, text: 'Proxy', value: 43, color: 'blue' },
|
||||||
|
{ key: 44, text: 'SiliconFlow', value: 44, color: 'blue' },
|
||||||
{ key: 8, text: '自定义渠道', value: 8, color: 'pink' },
|
{ key: 8, text: '自定义渠道', value: 8, color: 'pink' },
|
||||||
{ key: 22, text: '知识库:FastGPT', value: 22, color: 'blue' },
|
{ key: 22, text: '知识库:FastGPT', value: 22, color: 'blue' },
|
||||||
{ key: 21, text: '知识库:AI Proxy', value: 21, color: 'purple' },
|
{ key: 21, text: '知识库:AI Proxy', value: 21, color: 'purple' },
|
||||||
|
|||||||
@@ -173,6 +173,12 @@ export const CHANNEL_OPTIONS = {
|
|||||||
value: 43,
|
value: 43,
|
||||||
color: 'primary'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
|
44: {
|
||||||
|
key: 44,
|
||||||
|
text: 'SiliconFlow',
|
||||||
|
value: 44,
|
||||||
|
color: 'primary'
|
||||||
|
},
|
||||||
41: {
|
41: {
|
||||||
key: 41,
|
key: 41,
|
||||||
text: 'Novita',
|
text: 'Novita',
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ const COPY_OPTIONS = [
|
|||||||
encode: false
|
encode: false
|
||||||
},
|
},
|
||||||
{ key: 'ama', text: 'BotGem', url: 'ama://set-api-key?server={serverAddress}&key=sk-{key}', encode: true },
|
{ key: 'ama', text: 'BotGem', url: 'ama://set-api-key?server={serverAddress}&key=sk-{key}', encode: true },
|
||||||
{ key: 'opencat', text: 'OpenCat', url: 'opencat://team/join?domain={serverAddress}&token=sk-{key}', encode: true }
|
{ key: 'opencat', text: 'OpenCat', url: 'opencat://team/join?domain={serverAddress}&token=sk-{key}', encode: true },
|
||||||
|
{ key: 'lobechat', text: 'LobeChat', url: 'https://lobehub.com/?settings={"keyVaults":{"openai":{"apiKey":"user-key","baseURL":"https://your-proxy.com/v1"}}}', encode: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
function replacePlaceholders(text, key, serverAddress) {
|
function replacePlaceholders(text, key, serverAddress) {
|
||||||
|
|||||||
@@ -10,12 +10,14 @@ const COPY_OPTIONS = [
|
|||||||
{ key: 'next', text: 'ChatGPT Next Web', value: 'next' },
|
{ key: 'next', text: 'ChatGPT Next Web', value: 'next' },
|
||||||
{ key: 'ama', text: 'BotGem', value: 'ama' },
|
{ key: 'ama', text: 'BotGem', value: 'ama' },
|
||||||
{ key: 'opencat', text: 'OpenCat', value: 'opencat' },
|
{ key: 'opencat', text: 'OpenCat', value: 'opencat' },
|
||||||
|
{ key: 'lobechat', text: 'LobeChat', value: 'lobechat' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const OPEN_LINK_OPTIONS = [
|
const OPEN_LINK_OPTIONS = [
|
||||||
{ key: 'next', text: 'ChatGPT Next Web', value: 'next' },
|
{ key: 'next', text: 'ChatGPT Next Web', value: 'next' },
|
||||||
{ key: 'ama', text: 'BotGem', value: 'ama' },
|
{ key: 'ama', text: 'BotGem', value: 'ama' },
|
||||||
{ key: 'opencat', text: 'OpenCat', value: 'opencat' },
|
{ key: 'opencat', text: 'OpenCat', value: 'opencat' },
|
||||||
|
{ key: 'lobechat', text: 'LobeChat', value: 'lobechat' },
|
||||||
];
|
];
|
||||||
|
|
||||||
function renderTimestamp(timestamp) {
|
function renderTimestamp(timestamp) {
|
||||||
@@ -114,6 +116,9 @@ const TokensTable = () => {
|
|||||||
case 'next':
|
case 'next':
|
||||||
url = nextUrl;
|
url = nextUrl;
|
||||||
break;
|
break;
|
||||||
|
case 'lobechat':
|
||||||
|
url = nextLink + `/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${serverAddress}"/v1"}}}`;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
url = `sk-${key}`;
|
url = `sk-${key}`;
|
||||||
}
|
}
|
||||||
@@ -153,7 +158,11 @@ const TokensTable = () => {
|
|||||||
case 'opencat':
|
case 'opencat':
|
||||||
url = `opencat://team/join?domain=${encodedServerAddress}&token=sk-${key}`;
|
url = `opencat://team/join?domain=${encodedServerAddress}&token=sk-${key}`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'lobechat':
|
||||||
|
url = chatLink + `/?settings={"keyVaults":{"openai":{"apiKey":"sk-${key}","baseURL":"${serverAddress}"/v1"}}}`;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
url = defaultUrl;
|
url = defaultUrl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export const CHANNEL_OPTIONS = [
|
|||||||
{ key: 39, text: 'together.ai', value: 39, color: 'blue' },
|
{ key: 39, text: 'together.ai', value: 39, color: 'blue' },
|
||||||
{ key: 42, text: 'VertexAI', value: 42, color: 'blue' },
|
{ key: 42, text: 'VertexAI', value: 42, color: 'blue' },
|
||||||
{ key: 43, text: 'Proxy', value: 43, color: 'blue' },
|
{ key: 43, text: 'Proxy', value: 43, color: 'blue' },
|
||||||
|
{ key: 44, text: 'SiliconFlow', value: 44, color: 'blue' },
|
||||||
{ key: 8, text: '自定义渠道', value: 8, color: 'pink' },
|
{ key: 8, text: '自定义渠道', value: 8, color: 'pink' },
|
||||||
{ key: 22, text: '知识库:FastGPT', value: 22, color: 'blue' },
|
{ key: 22, text: '知识库:FastGPT', value: 22, color: 'blue' },
|
||||||
{ key: 21, text: '知识库:AI Proxy', value: 21, color: 'purple' },
|
{ key: 21, text: '知识库:AI Proxy', value: 21, color: 'purple' },
|
||||||
|
|||||||
Reference in New Issue
Block a user