Compare commits

..

1 Commits

Author SHA1 Message Date
Laisky.Cai
d4703dfd97 feat: add Proxy channel type and relay mode
Add the Proxy channel type and relay mode to support proxying requests to custom upstream services.
2024-07-21 13:42:13 +00:00
25 changed files with 332 additions and 404 deletions

View File

@@ -1,4 +1,4 @@
name: Publish Docker image (English) name: Publish Docker image (amd64, English)
on: on:
push: push:
@@ -34,13 +34,6 @@ 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:
@@ -58,7 +51,6 @@ 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 }}

View File

@@ -0,0 +1,61 @@
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 }}

View File

@@ -1,9 +1,10 @@
name: Publish Docker image name: Publish Docker image (arm64)
on: on:
push: push:
tags: tags:
- 'v*.*.*' - 'v*.*.*'
- '!*-alpha*'
workflow_dispatch: workflow_dispatch:
inputs: inputs:
name: name:

View File

@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM node:16 AS builder FROM node:16 as builder
WORKDIR /web WORKDIR /web
COPY ./VERSION . COPY ./VERSION .

View File

@@ -89,7 +89,6 @@ _✨ 通过标准的 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 模式**,可以通过流式传输实现打字机效果。
@@ -252,9 +251,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
根据[文档](https://qchatgpt.rockchin.top)完成部署后,在 `data/provider.json`设置`requester.openai-chat-completions.base-url`为 One API 实例地址,并填写 API Key 到 `keys.openai` 组中,设置 `model` 为要使用的模型名称。 根据文档完成部署后,在`config.py`设置配置项`openai_config`的`reverse_proxy`为 One API 后端地址,设置`api_key`为 One API 生成的key并在配置项`completion_api_params`的`model`参数设置为 One API 支持的模型名称。
运行期间可以通过`!model`命令查看、切换可用模型。 可安装 [Switcher 插件](https://github.com/RockChinQ/Switcher)在运行时切换所使用的模型。
### 部署到第三方平台 ### 部署到第三方平台
<details> <details>

View File

@@ -1,7 +1,6 @@
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",

View File

@@ -7,12 +7,8 @@ import (
) )
func GetRequestURL(meta *meta.Meta) (string, error) { func GetRequestURL(meta *meta.Meta) (string, error) {
switch meta.Mode { if meta.Mode == relaymode.ChatCompletions {
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)
} }

View File

@@ -3,5 +3,6 @@ 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", "gemini-1.5-flash", "gemini-1.5-pro", "text-embedding-004", "aqa", "gemini-pro", "gemini-1.0-pro-001", "gemini-1.5-pro",
"gemini-pro-vision", "gemini-1.0-pro-vision-001", "embedding-001", "text-embedding-004",
} }

View File

@@ -4,14 +4,9 @@ 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",
} }

View File

@@ -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/embed", meta.BaseURL) fullRequestURL = fmt.Sprintf("%s/api/embeddings", meta.BaseURL)
} }
return fullRequestURL, nil return fullRequestURL, nil
} }

View File

@@ -31,8 +31,6 @@ 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,
} }
@@ -120,10 +118,8 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
common.SetEventStreamHeaders(c) common.SetEventStreamHeaders(c)
for scanner.Scan() { for scanner.Scan() {
data := scanner.Text() data := strings.TrimPrefix(scanner.Text(), "}")
if strings.HasPrefix(data, "}") { data = data + "}"
data = strings.TrimPrefix(data, "}") + "}"
}
var ollamaResponse ChatResponse var ollamaResponse ChatResponse
err := json.Unmarshal([]byte(data), &ollamaResponse) err := json.Unmarshal([]byte(data), &ollamaResponse)
@@ -162,14 +158,7 @@ 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,
Input: request.ParseInput(), Prompt: strings.Join(request.ParseInput(), " "),
Options: &Options{
Seed: int(request.Seed),
Temperature: request.Temperature,
TopP: request.TopP,
FrequencyPenalty: request.FrequencyPenalty,
PresencePenalty: request.PresencePenalty,
},
} }
} }
@@ -212,17 +201,15 @@ 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: response.Model, Model: "text-embedding-v1",
Usage: model.Usage{TotalTokens: 0}, Usage: model.Usage{TotalTokens: 0},
} }
for i, embedding := range response.Embeddings {
openAIEmbeddingResponse.Data = append(openAIEmbeddingResponse.Data, openai.EmbeddingResponseItem{ openAIEmbeddingResponse.Data = append(openAIEmbeddingResponse.Data, openai.EmbeddingResponseItem{
Object: `embedding`, Object: `embedding`,
Index: i, Index: 0,
Embedding: embedding, Embedding: response.Embedding,
}) })
}
return &openAIEmbeddingResponse return &openAIEmbeddingResponse
} }

View File

@@ -7,8 +7,6 @@ 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 {
@@ -40,14 +38,10 @@ type ChatResponse struct {
type EmbeddingRequest struct { type EmbeddingRequest struct {
Model string `json:"model"` Model string `json:"model"`
Input []string `json:"input"` Prompt string `json:"prompt"`
// 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"`
Model string `json:"model"` Embedding []float64 `json:"embedding,omitempty"`
Embeddings [][]float64 `json:"embeddings"`
} }

View File

@@ -13,7 +13,6 @@ 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"
) )
@@ -31,7 +30,6 @@ 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) {
@@ -62,8 +60,6 @@ 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
} }

View File

@@ -8,7 +8,6 @@ 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",

View File

@@ -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, model) imageTokens, err := countImageTokens(url, detail)
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,15 +134,11 @@ 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, model string) (_ int, err error) { func countImageTokens(url string, detail 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
@@ -176,9 +172,6 @@ func countImageTokens(url string, detail string, model 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 {
@@ -198,9 +191,6 @@ func countImageTokens(url string, detail string, model 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:

View File

@@ -1,36 +0,0 @@
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",
}

View File

@@ -35,8 +35,6 @@ var ModelRatio = map[string]float64{
"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-4o-mini": 0.075, // $0.00015 / 1K tokens
"gpt-4o-mini-2024-07-18": 0.075, // $0.00015 / 1K tokens
"gpt-4-vision-preview": 5, // $0.01 / 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": 0.25, // $0.0005 / 1K tokens
"gpt-3.5-turbo-0301": 0.75, "gpt-3.5-turbo-0301": 0.75,
@@ -98,11 +96,12 @@ 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.0-pro-001": 1,
"gemini-1.5-pro": 1, "gemini-1.5-pro": 1,
"aqa": 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,
@@ -157,16 +156,12 @@ 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
"gemma-7b-it": 0.07 / 1000000 * USD, "llama3-70b-8192": 0.59 / 1000 * USD,
"mixtral-8x7b-32768": 0.24 / 1000000 * USD, "mixtral-8x7b-32768": 0.27 / 1000 * USD,
"llama3-8b-8192": 0.05 / 1000000 * USD, "llama3-8b-8192": 0.05 / 1000 * USD,
"llama3-70b-8192": 0.59 / 1000000 * USD, "gemma-7b-it": 0.1 / 1000 * USD,
"gemma2-9b-it": 0.20 / 1000000 * USD, "llama2-70b-4096": 0.64 / 1000 * USD,
"llama-3.1-405b-reasoning": 0.89 / 1000000 * USD, "llama2-7b-2048": 0.1 / 1000 * 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,
@@ -313,9 +308,6 @@ 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") {

View File

@@ -45,6 +45,5 @@ const (
Novita Novita
VertextAI VertextAI
Proxy Proxy
SiliconFlow
Dummy Dummy
) )

View File

@@ -45,7 +45,6 @@ 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() {

View File

@@ -10,7 +10,6 @@ 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"
@@ -32,8 +31,9 @@ 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, _ = getMappedModelName(textRequest.Model, meta.ModelMapping) textRequest.Model, isModelMapped = 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,10 +55,31 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
adaptor.Init(meta) adaptor.Init(meta)
// get request body // get request body
requestBody, err := getRequestBody(c, meta, textRequest, adaptor) var requestBody io.Reader
if meta.APIType == apitype.OpenAI {
// no need to convert request for openai
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 { if err != nil {
return openai.ErrorWrapper(err, "convert_request_failed", http.StatusInternalServerError) 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
resp, err := adaptor.DoRequest(c, meta, requestBody) resp, err := adaptor.DoRequest(c, meta, requestBody)
@@ -82,26 +103,3 @@ 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
}

View File

@@ -29,7 +29,6 @@ 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 {

View File

@@ -29,7 +29,6 @@ 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' },

View File

@@ -173,12 +173,6 @@ 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',

View File

@@ -3,13 +3,13 @@ import { useSelector } from 'react-redux';
import useRegister from 'hooks/useRegister'; import useRegister from 'hooks/useRegister';
import Turnstile from 'react-turnstile'; import Turnstile from 'react-turnstile';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
// import { useSelector } from 'react-redux';
// material-ui // material-ui
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import { import {
Box, Box,
Button, Button,
CircularProgress,
FormControl, FormControl,
FormHelperText, FormHelperText,
Grid, Grid,
@@ -50,9 +50,6 @@ const RegisterForm = ({ ...others }) => {
const [strength, setStrength] = useState(0); const [strength, setStrength] = useState(0);
const [level, setLevel] = useState(); const [level, setLevel] = useState();
const [timer, setTimer] = useState(0);
const [loading, setLoading] = useState(false);
const handleClickShowPassword = () => { const handleClickShowPassword = () => {
setShowPassword(!showPassword); setShowPassword(!showPassword);
}; };
@@ -77,17 +74,11 @@ const RegisterForm = ({ ...others }) => {
return; return;
} }
setLoading(true); // Start loading
const { success, message } = await sendVerificationCode(email, turnstileToken); const { success, message } = await sendVerificationCode(email, turnstileToken);
setLoading(false); // Stop loading
if (!success) { if (!success) {
showError(message); showError(message);
return; return;
} }
setTimer(60); // Start the 60-second timer
}; };
useEffect(() => { useEffect(() => {
@@ -103,17 +94,6 @@ const RegisterForm = ({ ...others }) => {
} }
}, [siteInfo]); }, [siteInfo]);
useEffect(() => {
let interval;
if (timer > 0) {
interval = setInterval(() => {
setTimer((prevTimer) => prevTimer - 1);
}, 1000);
}
return () => clearInterval(interval);
}, [timer]);
return ( return (
<> <>
<Formik <Formik
@@ -260,13 +240,8 @@ const RegisterForm = ({ ...others }) => {
onChange={handleChange} onChange={handleChange}
endAdornment={ endAdornment={
<InputAdornment position="end"> <InputAdornment position="end">
<Button <Button variant="contained" color="primary" onClick={() => handleSendCode(values.email)}>
variant="contained" 发送验证码
color="primary"
onClick={() => handleSendCode(values.email)}
disabled={timer > 0 || loading}
>
{loading ? <CircularProgress size={24} /> : timer > 0 ? `${timer}s` : '发送验证码'}
</Button> </Button>
</InputAdornment> </InputAdornment>
} }

View File

@@ -29,7 +29,6 @@ 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' },