Merge branch 'songquanpeng:main' into base
This commit is contained in:
commit
52ac1fe7d8
@ -60,7 +60,7 @@ _✨ Access all LLM through the standard OpenAI API format, easy to deploy & use
|
|||||||
1. Support for multiple large models:
|
1. Support for multiple large models:
|
||||||
+ [x] [OpenAI ChatGPT Series Models](https://platform.openai.com/docs/guides/gpt/chat-completions-api) (Supports [Azure OpenAI API](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference))
|
+ [x] [OpenAI ChatGPT Series Models](https://platform.openai.com/docs/guides/gpt/chat-completions-api) (Supports [Azure OpenAI API](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference))
|
||||||
+ [x] [Anthropic Claude Series Models](https://anthropic.com)
|
+ [x] [Anthropic Claude Series Models](https://anthropic.com)
|
||||||
+ [x] [Google PaLM2 Series Models](https://developers.generativeai.google)
|
+ [x] [Google PaLM2 and Gemini Series Models](https://developers.generativeai.google)
|
||||||
+ [x] [Baidu Wenxin Yiyuan Series Models](https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html)
|
+ [x] [Baidu Wenxin Yiyuan Series Models](https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html)
|
||||||
+ [x] [Alibaba Tongyi Qianwen Series Models](https://help.aliyun.com/document_detail/2400395.html)
|
+ [x] [Alibaba Tongyi Qianwen Series Models](https://help.aliyun.com/document_detail/2400395.html)
|
||||||
+ [x] [Zhipu ChatGLM Series Models](https://bigmodel.cn)
|
+ [x] [Zhipu ChatGLM Series Models](https://bigmodel.cn)
|
||||||
|
@ -60,7 +60,7 @@ _✨ 標準的な OpenAI API フォーマットを通じてすべての LLM に
|
|||||||
1. 複数の大型モデルをサポート:
|
1. 複数の大型モデルをサポート:
|
||||||
+ [x] [OpenAI ChatGPT シリーズモデル](https://platform.openai.com/docs/guides/gpt/chat-completions-api) ([Azure OpenAI API](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference) をサポート)
|
+ [x] [OpenAI ChatGPT シリーズモデル](https://platform.openai.com/docs/guides/gpt/chat-completions-api) ([Azure OpenAI API](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference) をサポート)
|
||||||
+ [x] [Anthropic Claude シリーズモデル](https://anthropic.com)
|
+ [x] [Anthropic Claude シリーズモデル](https://anthropic.com)
|
||||||
+ [x] [Google PaLM2 シリーズモデル](https://developers.generativeai.google)
|
+ [x] [Google PaLM2/Gemini シリーズモデル](https://developers.generativeai.google)
|
||||||
+ [x] [Baidu Wenxin Yiyuan シリーズモデル](https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html)
|
+ [x] [Baidu Wenxin Yiyuan シリーズモデル](https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html)
|
||||||
+ [x] [Alibaba Tongyi Qianwen シリーズモデル](https://help.aliyun.com/document_detail/2400395.html)
|
+ [x] [Alibaba Tongyi Qianwen シリーズモデル](https://help.aliyun.com/document_detail/2400395.html)
|
||||||
+ [x] [Zhipu ChatGLM シリーズモデル](https://bigmodel.cn)
|
+ [x] [Zhipu ChatGLM シリーズモデル](https://bigmodel.cn)
|
||||||
|
10
README.md
10
README.md
@ -66,20 +66,14 @@ _✨ 通过标准的 OpenAI API 格式访问所有的大模型,开箱即用
|
|||||||
1. 支持多种大模型:
|
1. 支持多种大模型:
|
||||||
+ [x] [OpenAI ChatGPT 系列模型](https://platform.openai.com/docs/guides/gpt/chat-completions-api)(支持 [Azure OpenAI API](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference))
|
+ [x] [OpenAI ChatGPT 系列模型](https://platform.openai.com/docs/guides/gpt/chat-completions-api)(支持 [Azure OpenAI API](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference))
|
||||||
+ [x] [Anthropic Claude 系列模型](https://anthropic.com)
|
+ [x] [Anthropic Claude 系列模型](https://anthropic.com)
|
||||||
+ [x] [Google PaLM2 系列模型](https://developers.generativeai.google)
|
+ [x] [Google PaLM2/Gemini 系列模型](https://developers.generativeai.google)
|
||||||
+ [x] [百度文心一言系列模型](https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html)
|
+ [x] [百度文心一言系列模型](https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html)
|
||||||
+ [x] [阿里通义千问系列模型](https://help.aliyun.com/document_detail/2400395.html)
|
+ [x] [阿里通义千问系列模型](https://help.aliyun.com/document_detail/2400395.html)
|
||||||
+ [x] [讯飞星火认知大模型](https://www.xfyun.cn/doc/spark/Web.html)
|
+ [x] [讯飞星火认知大模型](https://www.xfyun.cn/doc/spark/Web.html)
|
||||||
+ [x] [智谱 ChatGLM 系列模型](https://bigmodel.cn)
|
+ [x] [智谱 ChatGLM 系列模型](https://bigmodel.cn)
|
||||||
+ [x] [360 智脑](https://ai.360.cn)
|
+ [x] [360 智脑](https://ai.360.cn)
|
||||||
+ [x] [腾讯混元大模型](https://cloud.tencent.com/document/product/1729)
|
+ [x] [腾讯混元大模型](https://cloud.tencent.com/document/product/1729)
|
||||||
2. 支持配置镜像以及众多第三方代理服务:
|
2. 支持配置镜像以及众多[第三方代理服务](https://iamazing.cn/page/openai-api-third-party-services)。
|
||||||
+ [x] [OpenAI-SB](https://openai-sb.com)
|
|
||||||
+ [x] [CloseAI](https://referer.shadowai.xyz/r/2412)
|
|
||||||
+ [x] [API2D](https://api2d.com/r/197971)
|
|
||||||
+ [x] [OhMyGPT](https://aigptx.top?aff=uFpUl2Kf)
|
|
||||||
+ [x] [AI Proxy](https://aiproxy.io/?i=OneAPI) (邀请码:`OneAPI`)
|
|
||||||
+ [x] 自定义渠道:例如各种未收录的第三方代理服务
|
|
||||||
3. 支持通过**负载均衡**的方式访问多个渠道。
|
3. 支持通过**负载均衡**的方式访问多个渠道。
|
||||||
4. 支持 **stream 模式**,可以通过流式传输实现打字机效果。
|
4. 支持 **stream 模式**,可以通过流式传输实现打字机效果。
|
||||||
5. 支持**多机部署**,[详见此处](#多机部署)。
|
5. 支持**多机部署**,[详见此处](#多机部署)。
|
||||||
|
@ -187,6 +187,7 @@ const (
|
|||||||
ChannelTypeAIProxyLibrary = 21
|
ChannelTypeAIProxyLibrary = 21
|
||||||
ChannelTypeFastGPT = 22
|
ChannelTypeFastGPT = 22
|
||||||
ChannelTypeTencent = 23
|
ChannelTypeTencent = 23
|
||||||
|
ChannelTypeGemini = 24
|
||||||
)
|
)
|
||||||
|
|
||||||
var ChannelBaseURLs = []string{
|
var ChannelBaseURLs = []string{
|
||||||
@ -214,4 +215,5 @@ var ChannelBaseURLs = []string{
|
|||||||
"https://api.aiproxy.io", // 21
|
"https://api.aiproxy.io", // 21
|
||||||
"https://fastgpt.run/api/openapi", // 22
|
"https://fastgpt.run/api/openapi", // 22
|
||||||
"https://hunyuan.cloud.tencent.com", //23
|
"https://hunyuan.cloud.tencent.com", //23
|
||||||
|
"", //24
|
||||||
}
|
}
|
||||||
|
@ -83,12 +83,15 @@ var ModelRatio = map[string]float64{
|
|||||||
"ERNIE-Bot-4": 8.572, // ¥0.12 / 1k tokens
|
"ERNIE-Bot-4": 8.572, // ¥0.12 / 1k tokens
|
||||||
"Embedding-V1": 0.1429, // ¥0.002 / 1k tokens
|
"Embedding-V1": 0.1429, // ¥0.002 / 1k tokens
|
||||||
"PaLM-2": 1,
|
"PaLM-2": 1,
|
||||||
|
"gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
|
||||||
"chatglm_turbo": 0.3572, // ¥0.005 / 1k tokens
|
"chatglm_turbo": 0.3572, // ¥0.005 / 1k tokens
|
||||||
"chatglm_pro": 0.7143, // ¥0.01 / 1k tokens
|
"chatglm_pro": 0.7143, // ¥0.01 / 1k tokens
|
||||||
"chatglm_std": 0.3572, // ¥0.005 / 1k tokens
|
"chatglm_std": 0.3572, // ¥0.005 / 1k tokens
|
||||||
"chatglm_lite": 0.1429, // ¥0.002 / 1k tokens
|
"chatglm_lite": 0.1429, // ¥0.002 / 1k tokens
|
||||||
"qwen-turbo": 0.8572, // ¥0.012 / 1k tokens
|
"qwen-turbo": 0.5715, // ¥0.008 / 1k tokens // https://help.aliyun.com/zh/dashscope/developer-reference/tongyi-thousand-questions-metering-and-billing
|
||||||
"qwen-plus": 10, // ¥0.14 / 1k tokens
|
"qwen-plus": 1.4286, // ¥0.02 / 1k tokens
|
||||||
|
"qwen-max": 1.4286, // ¥0.02 / 1k tokens
|
||||||
|
"qwen-max-longcontext": 1.4286, // ¥0.02 / 1k tokens
|
||||||
"text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens
|
"text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens
|
||||||
"SparkDesk": 1.2858, // ¥0.018 / 1k tokens
|
"SparkDesk": 1.2858, // ¥0.018 / 1k tokens
|
||||||
"360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens
|
"360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens
|
||||||
|
@ -20,6 +20,8 @@ func testChannel(channel *model.Channel, request ChatRequest) (err error, openai
|
|||||||
switch channel.Type {
|
switch channel.Type {
|
||||||
case common.ChannelTypePaLM:
|
case common.ChannelTypePaLM:
|
||||||
fallthrough
|
fallthrough
|
||||||
|
case common.ChannelTypeGemini:
|
||||||
|
fallthrough
|
||||||
case common.ChannelTypeAnthropic:
|
case common.ChannelTypeAnthropic:
|
||||||
fallthrough
|
fallthrough
|
||||||
case common.ChannelTypeBaidu:
|
case common.ChannelTypeBaidu:
|
||||||
|
@ -423,6 +423,15 @@ func init() {
|
|||||||
Root: "PaLM-2",
|
Root: "PaLM-2",
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Id: "gemini-pro",
|
||||||
|
Object: "model",
|
||||||
|
Created: 1677649963,
|
||||||
|
OwnedBy: "google",
|
||||||
|
Permission: permission,
|
||||||
|
Root: "gemini-pro",
|
||||||
|
Parent: nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Id: "chatglm_turbo",
|
Id: "chatglm_turbo",
|
||||||
Object: "model",
|
Object: "model",
|
||||||
@ -477,6 +486,24 @@ func init() {
|
|||||||
Root: "qwen-plus",
|
Root: "qwen-plus",
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Id: "qwen-max",
|
||||||
|
Object: "model",
|
||||||
|
Created: 1677649963,
|
||||||
|
OwnedBy: "ali",
|
||||||
|
Permission: permission,
|
||||||
|
Root: "qwen-max",
|
||||||
|
Parent: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Id: "qwen-max-longcontext",
|
||||||
|
Object: "model",
|
||||||
|
Created: 1677649963,
|
||||||
|
OwnedBy: "ali",
|
||||||
|
Permission: permission,
|
||||||
|
Root: "qwen-max-longcontext",
|
||||||
|
Parent: nil,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Id: "text-embedding-v1",
|
Id: "text-embedding-v1",
|
||||||
Object: "model",
|
Object: "model",
|
||||||
|
@ -13,13 +13,13 @@ import (
|
|||||||
// https://help.aliyun.com/document_detail/613695.html?spm=a2c4g.2399480.0.0.1adb778fAdzP9w#341800c0f8w0r
|
// https://help.aliyun.com/document_detail/613695.html?spm=a2c4g.2399480.0.0.1adb778fAdzP9w#341800c0f8w0r
|
||||||
|
|
||||||
type AliMessage struct {
|
type AliMessage struct {
|
||||||
User string `json:"user"`
|
Content string `json:"content"`
|
||||||
Bot string `json:"bot"`
|
Role string `json:"role"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AliInput struct {
|
type AliInput struct {
|
||||||
Prompt string `json:"prompt"`
|
//Prompt string `json:"prompt"`
|
||||||
History []AliMessage `json:"history"`
|
Messages []AliMessage `json:"messages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AliParameters struct {
|
type AliParameters struct {
|
||||||
@ -83,32 +83,17 @@ type AliChatResponse struct {
|
|||||||
|
|
||||||
func requestOpenAI2Ali(request GeneralOpenAIRequest) *AliChatRequest {
|
func requestOpenAI2Ali(request GeneralOpenAIRequest) *AliChatRequest {
|
||||||
messages := make([]AliMessage, 0, len(request.Messages))
|
messages := make([]AliMessage, 0, len(request.Messages))
|
||||||
prompt := ""
|
|
||||||
for i := 0; i < len(request.Messages); i++ {
|
for i := 0; i < len(request.Messages); i++ {
|
||||||
message := request.Messages[i]
|
message := request.Messages[i]
|
||||||
if message.Role == "system" {
|
messages = append(messages, AliMessage{
|
||||||
messages = append(messages, AliMessage{
|
Content: message.StringContent(),
|
||||||
User: message.StringContent(),
|
Role: strings.ToLower(message.Role),
|
||||||
Bot: "Okay",
|
})
|
||||||
})
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
if i == len(request.Messages)-1 {
|
|
||||||
prompt = message.StringContent()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
messages = append(messages, AliMessage{
|
|
||||||
User: message.StringContent(),
|
|
||||||
Bot: request.Messages[i+1].StringContent(),
|
|
||||||
})
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return &AliChatRequest{
|
return &AliChatRequest{
|
||||||
Model: request.Model,
|
Model: request.Model,
|
||||||
Input: AliInput{
|
Input: AliInput{
|
||||||
Prompt: prompt,
|
Messages: messages,
|
||||||
History: messages,
|
|
||||||
},
|
},
|
||||||
//Parameters: AliParameters{ // ChatGPT's parameters are not compatible with Ali's
|
//Parameters: AliParameters{ // ChatGPT's parameters are not compatible with Ali's
|
||||||
// TopP: request.TopP,
|
// TopP: request.TopP,
|
||||||
|
305
controller/relay-gemini.go
Normal file
305
controller/relay-gemini.go
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"one-api/common"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GeminiChatRequest struct {
|
||||||
|
Contents []GeminiChatContent `json:"contents"`
|
||||||
|
SafetySettings []GeminiChatSafetySettings `json:"safety_settings,omitempty"`
|
||||||
|
GenerationConfig GeminiChatGenerationConfig `json:"generation_config,omitempty"`
|
||||||
|
Tools []GeminiChatTools `json:"tools,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiInlineData struct {
|
||||||
|
MimeType string `json:"mimeType"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiPart struct {
|
||||||
|
Text string `json:"text,omitempty"`
|
||||||
|
InlineData *GeminiInlineData `json:"inlineData,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiChatContent struct {
|
||||||
|
Role string `json:"role,omitempty"`
|
||||||
|
Parts []GeminiPart `json:"parts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiChatSafetySettings struct {
|
||||||
|
Category string `json:"category"`
|
||||||
|
Threshold string `json:"threshold"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiChatTools struct {
|
||||||
|
FunctionDeclarations any `json:"functionDeclarations,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiChatGenerationConfig struct {
|
||||||
|
Temperature float64 `json:"temperature,omitempty"`
|
||||||
|
TopP float64 `json:"topP,omitempty"`
|
||||||
|
TopK float64 `json:"topK,omitempty"`
|
||||||
|
MaxOutputTokens int `json:"maxOutputTokens,omitempty"`
|
||||||
|
CandidateCount int `json:"candidateCount,omitempty"`
|
||||||
|
StopSequences []string `json:"stopSequences,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting safety to the lowest possible values since Gemini is already powerless enough
|
||||||
|
func requestOpenAI2Gemini(textRequest GeneralOpenAIRequest) *GeminiChatRequest {
|
||||||
|
geminiRequest := GeminiChatRequest{
|
||||||
|
Contents: make([]GeminiChatContent, 0, len(textRequest.Messages)),
|
||||||
|
//SafetySettings: []GeminiChatSafetySettings{
|
||||||
|
// {
|
||||||
|
// Category: "HARM_CATEGORY_HARASSMENT",
|
||||||
|
// Threshold: "BLOCK_ONLY_HIGH",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Category: "HARM_CATEGORY_HATE_SPEECH",
|
||||||
|
// Threshold: "BLOCK_ONLY_HIGH",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
||||||
|
// Threshold: "BLOCK_ONLY_HIGH",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// Category: "HARM_CATEGORY_DANGEROUS_CONTENT",
|
||||||
|
// Threshold: "BLOCK_ONLY_HIGH",
|
||||||
|
// },
|
||||||
|
//},
|
||||||
|
GenerationConfig: GeminiChatGenerationConfig{
|
||||||
|
Temperature: textRequest.Temperature,
|
||||||
|
TopP: textRequest.TopP,
|
||||||
|
MaxOutputTokens: textRequest.MaxTokens,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if textRequest.Functions != nil {
|
||||||
|
geminiRequest.Tools = []GeminiChatTools{
|
||||||
|
{
|
||||||
|
FunctionDeclarations: textRequest.Functions,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shouldAddDummyModelMessage := false
|
||||||
|
for _, message := range textRequest.Messages {
|
||||||
|
content := GeminiChatContent{
|
||||||
|
Role: message.Role,
|
||||||
|
Parts: []GeminiPart{
|
||||||
|
{
|
||||||
|
Text: message.StringContent(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// there's no assistant role in gemini and API shall vomit if Role is not user or model
|
||||||
|
if content.Role == "assistant" {
|
||||||
|
content.Role = "model"
|
||||||
|
}
|
||||||
|
// Converting system prompt to prompt from user for the same reason
|
||||||
|
if content.Role == "system" {
|
||||||
|
content.Role = "user"
|
||||||
|
shouldAddDummyModelMessage = true
|
||||||
|
}
|
||||||
|
geminiRequest.Contents = append(geminiRequest.Contents, content)
|
||||||
|
|
||||||
|
// If a system message is the last message, we need to add a dummy model message to make gemini happy
|
||||||
|
if shouldAddDummyModelMessage {
|
||||||
|
geminiRequest.Contents = append(geminiRequest.Contents, GeminiChatContent{
|
||||||
|
Role: "model",
|
||||||
|
Parts: []GeminiPart{
|
||||||
|
{
|
||||||
|
Text: "Okay",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
shouldAddDummyModelMessage = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &geminiRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiChatResponse struct {
|
||||||
|
Candidates []GeminiChatCandidate `json:"candidates"`
|
||||||
|
PromptFeedback GeminiChatPromptFeedback `json:"promptFeedback"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GeminiChatResponse) GetResponseText() string {
|
||||||
|
if g == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(g.Candidates) > 0 && len(g.Candidates[0].Content.Parts) > 0 {
|
||||||
|
return g.Candidates[0].Content.Parts[0].Text
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiChatCandidate struct {
|
||||||
|
Content GeminiChatContent `json:"content"`
|
||||||
|
FinishReason string `json:"finishReason"`
|
||||||
|
Index int64 `json:"index"`
|
||||||
|
SafetyRatings []GeminiChatSafetyRating `json:"safetyRatings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiChatSafetyRating struct {
|
||||||
|
Category string `json:"category"`
|
||||||
|
Probability string `json:"probability"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeminiChatPromptFeedback struct {
|
||||||
|
SafetyRatings []GeminiChatSafetyRating `json:"safetyRatings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseGeminiChat2OpenAI(response *GeminiChatResponse) *OpenAITextResponse {
|
||||||
|
fullTextResponse := OpenAITextResponse{
|
||||||
|
Id: fmt.Sprintf("chatcmpl-%s", common.GetUUID()),
|
||||||
|
Object: "chat.completion",
|
||||||
|
Created: common.GetTimestamp(),
|
||||||
|
Choices: make([]OpenAITextResponseChoice, 0, len(response.Candidates)),
|
||||||
|
}
|
||||||
|
for i, candidate := range response.Candidates {
|
||||||
|
choice := OpenAITextResponseChoice{
|
||||||
|
Index: i,
|
||||||
|
Message: Message{
|
||||||
|
Role: "assistant",
|
||||||
|
Content: "",
|
||||||
|
},
|
||||||
|
FinishReason: stopFinishReason,
|
||||||
|
}
|
||||||
|
if len(candidate.Content.Parts) > 0 {
|
||||||
|
choice.Message.Content = candidate.Content.Parts[0].Text
|
||||||
|
}
|
||||||
|
fullTextResponse.Choices = append(fullTextResponse.Choices, choice)
|
||||||
|
}
|
||||||
|
return &fullTextResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
func streamResponseGeminiChat2OpenAI(geminiResponse *GeminiChatResponse) *ChatCompletionsStreamResponse {
|
||||||
|
var choice ChatCompletionsStreamResponseChoice
|
||||||
|
choice.Delta.Content = geminiResponse.GetResponseText()
|
||||||
|
choice.FinishReason = &stopFinishReason
|
||||||
|
var response ChatCompletionsStreamResponse
|
||||||
|
response.Object = "chat.completion.chunk"
|
||||||
|
response.Model = "gemini"
|
||||||
|
response.Choices = []ChatCompletionsStreamResponseChoice{choice}
|
||||||
|
return &response
|
||||||
|
}
|
||||||
|
|
||||||
|
func geminiChatStreamHandler(c *gin.Context, resp *http.Response) (*OpenAIErrorWithStatusCode, string) {
|
||||||
|
responseText := ""
|
||||||
|
dataChan := make(chan string)
|
||||||
|
stopChan := make(chan bool)
|
||||||
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
|
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
|
if atEOF && len(data) == 0 {
|
||||||
|
return 0, nil, nil
|
||||||
|
}
|
||||||
|
if i := strings.Index(string(data), "\n"); i >= 0 {
|
||||||
|
return i + 1, data[0:i], nil
|
||||||
|
}
|
||||||
|
if atEOF {
|
||||||
|
return len(data), data, nil
|
||||||
|
}
|
||||||
|
return 0, nil, nil
|
||||||
|
})
|
||||||
|
go func() {
|
||||||
|
for scanner.Scan() {
|
||||||
|
data := scanner.Text()
|
||||||
|
data = strings.TrimSpace(data)
|
||||||
|
if !strings.HasPrefix(data, "\"text\": \"") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
data = strings.TrimPrefix(data, "\"text\": \"")
|
||||||
|
data = strings.TrimSuffix(data, "\"")
|
||||||
|
dataChan <- data
|
||||||
|
}
|
||||||
|
stopChan <- true
|
||||||
|
}()
|
||||||
|
setEventStreamHeaders(c)
|
||||||
|
c.Stream(func(w io.Writer) bool {
|
||||||
|
select {
|
||||||
|
case data := <-dataChan:
|
||||||
|
// this is used to prevent annoying \ related format bug
|
||||||
|
data = fmt.Sprintf("{\"content\": \"%s\"}", data)
|
||||||
|
type dummyStruct struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
var dummy dummyStruct
|
||||||
|
err := json.Unmarshal([]byte(data), &dummy)
|
||||||
|
responseText += dummy.Content
|
||||||
|
var choice ChatCompletionsStreamResponseChoice
|
||||||
|
choice.Delta.Content = dummy.Content
|
||||||
|
response := ChatCompletionsStreamResponse{
|
||||||
|
Id: fmt.Sprintf("chatcmpl-%s", common.GetUUID()),
|
||||||
|
Object: "chat.completion.chunk",
|
||||||
|
Created: common.GetTimestamp(),
|
||||||
|
Model: "gemini-pro",
|
||||||
|
Choices: []ChatCompletionsStreamResponseChoice{choice},
|
||||||
|
}
|
||||||
|
jsonResponse, err := json.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
common.SysError("error marshalling stream response: " + err.Error())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
||||||
|
return true
|
||||||
|
case <-stopChan:
|
||||||
|
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
err := resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return errorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), ""
|
||||||
|
}
|
||||||
|
return nil, responseText
|
||||||
|
}
|
||||||
|
|
||||||
|
func geminiChatHandler(c *gin.Context, resp *http.Response, promptTokens int, model string) (*OpenAIErrorWithStatusCode, *Usage) {
|
||||||
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return errorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
|
err = resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return errorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
|
var geminiResponse GeminiChatResponse
|
||||||
|
err = json.Unmarshal(responseBody, &geminiResponse)
|
||||||
|
if err != nil {
|
||||||
|
return errorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
|
if len(geminiResponse.Candidates) == 0 {
|
||||||
|
return &OpenAIErrorWithStatusCode{
|
||||||
|
OpenAIError: OpenAIError{
|
||||||
|
Message: "No candidates returned",
|
||||||
|
Type: "server_error",
|
||||||
|
Param: "",
|
||||||
|
Code: 500,
|
||||||
|
},
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
fullTextResponse := responseGeminiChat2OpenAI(&geminiResponse)
|
||||||
|
completionTokens := countTokenText(geminiResponse.GetResponseText(), model)
|
||||||
|
usage := Usage{
|
||||||
|
PromptTokens: promptTokens,
|
||||||
|
CompletionTokens: completionTokens,
|
||||||
|
TotalTokens: promptTokens + completionTokens,
|
||||||
|
}
|
||||||
|
fullTextResponse.Usage = usage
|
||||||
|
jsonResponse, err := json.Marshal(fullTextResponse)
|
||||||
|
if err != nil {
|
||||||
|
return errorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
|
c.Writer.Header().Set("Content-Type", "application/json")
|
||||||
|
c.Writer.WriteHeader(resp.StatusCode)
|
||||||
|
_, err = c.Writer.Write(jsonResponse)
|
||||||
|
return nil, &usage
|
||||||
|
}
|
@ -19,7 +19,6 @@ func isWithinRange(element string, value int) bool {
|
|||||||
if _, ok := common.DalleGenerationImageAmounts[element]; !ok {
|
if _, ok := common.DalleGenerationImageAmounts[element]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
min := common.DalleGenerationImageAmounts[element][0]
|
min := common.DalleGenerationImageAmounts[element][0]
|
||||||
max := common.DalleGenerationImageAmounts[element][1]
|
max := common.DalleGenerationImageAmounts[element][1]
|
||||||
|
|
||||||
@ -42,6 +41,10 @@ func relayImageHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode
|
|||||||
return errorWrapper(err, "bind_request_body_failed", http.StatusBadRequest)
|
return errorWrapper(err, "bind_request_body_failed", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if imageRequest.N == 0 {
|
||||||
|
imageRequest.N = 1
|
||||||
|
}
|
||||||
|
|
||||||
// Size validation
|
// Size validation
|
||||||
if imageRequest.Size != "" {
|
if imageRequest.Size != "" {
|
||||||
imageSize = imageRequest.Size
|
imageSize = imageRequest.Size
|
||||||
@ -79,7 +82,10 @@ func relayImageHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode
|
|||||||
|
|
||||||
// Number of generated images validation
|
// Number of generated images validation
|
||||||
if isWithinRange(imageModel, imageRequest.N) == false {
|
if isWithinRange(imageModel, imageRequest.N) == false {
|
||||||
return errorWrapper(errors.New("invalid value of n"), "n_not_within_range", http.StatusBadRequest)
|
// channel not azure
|
||||||
|
if channelType != common.ChannelTypeAzure {
|
||||||
|
return errorWrapper(errors.New("invalid value of n"), "n_not_within_range", http.StatusBadRequest)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// map model name
|
// map model name
|
||||||
@ -102,7 +108,7 @@ func relayImageHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode
|
|||||||
baseURL = c.GetString("base_url")
|
baseURL = c.GetString("base_url")
|
||||||
}
|
}
|
||||||
fullRequestURL := getFullRequestURL(baseURL, requestURL, channelType)
|
fullRequestURL := getFullRequestURL(baseURL, requestURL, channelType)
|
||||||
if channelType == common.ChannelTypeAzure && relayMode == RelayModeImagesGenerations {
|
if channelType == common.ChannelTypeAzure {
|
||||||
// https://learn.microsoft.com/en-us/azure/ai-services/openai/dall-e-quickstart?tabs=dalle3%2Ccommand-line&pivots=rest-api
|
// https://learn.microsoft.com/en-us/azure/ai-services/openai/dall-e-quickstart?tabs=dalle3%2Ccommand-line&pivots=rest-api
|
||||||
apiVersion := GetAPIVersion(c)
|
apiVersion := GetAPIVersion(c)
|
||||||
// https://{resource_name}.openai.azure.com/openai/deployments/dall-e-3/images/generations?api-version=2023-06-01-preview
|
// https://{resource_name}.openai.azure.com/openai/deployments/dall-e-3/images/generations?api-version=2023-06-01-preview
|
||||||
|
@ -27,6 +27,7 @@ const (
|
|||||||
APITypeXunfei
|
APITypeXunfei
|
||||||
APITypeAIProxyLibrary
|
APITypeAIProxyLibrary
|
||||||
APITypeTencent
|
APITypeTencent
|
||||||
|
APITypeGemini
|
||||||
)
|
)
|
||||||
|
|
||||||
var httpClient *http.Client
|
var httpClient *http.Client
|
||||||
@ -118,6 +119,8 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
apiType = APITypeAIProxyLibrary
|
apiType = APITypeAIProxyLibrary
|
||||||
case common.ChannelTypeTencent:
|
case common.ChannelTypeTencent:
|
||||||
apiType = APITypeTencent
|
apiType = APITypeTencent
|
||||||
|
case common.ChannelTypeGemini:
|
||||||
|
apiType = APITypeGemini
|
||||||
}
|
}
|
||||||
baseURL := common.ChannelBaseURLs[channelType]
|
baseURL := common.ChannelBaseURLs[channelType]
|
||||||
requestURL := c.Request.URL.String()
|
requestURL := c.Request.URL.String()
|
||||||
@ -177,6 +180,23 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
apiKey := c.Request.Header.Get("Authorization")
|
apiKey := c.Request.Header.Get("Authorization")
|
||||||
apiKey = strings.TrimPrefix(apiKey, "Bearer ")
|
apiKey = strings.TrimPrefix(apiKey, "Bearer ")
|
||||||
fullRequestURL += "?key=" + apiKey
|
fullRequestURL += "?key=" + apiKey
|
||||||
|
case APITypeGemini:
|
||||||
|
requestBaseURL := "https://generativelanguage.googleapis.com"
|
||||||
|
if baseURL != "" {
|
||||||
|
requestBaseURL = baseURL
|
||||||
|
}
|
||||||
|
version := "v1"
|
||||||
|
if c.GetString("api_version") != "" {
|
||||||
|
version = c.GetString("api_version")
|
||||||
|
}
|
||||||
|
action := "generateContent"
|
||||||
|
if textRequest.Stream {
|
||||||
|
action = "streamGenerateContent"
|
||||||
|
}
|
||||||
|
fullRequestURL = fmt.Sprintf("%s/%s/models/%s:%s", requestBaseURL, version, textRequest.Model, action)
|
||||||
|
apiKey := c.Request.Header.Get("Authorization")
|
||||||
|
apiKey = strings.TrimPrefix(apiKey, "Bearer ")
|
||||||
|
fullRequestURL += "?key=" + apiKey
|
||||||
case APITypeZhipu:
|
case APITypeZhipu:
|
||||||
method := "invoke"
|
method := "invoke"
|
||||||
if textRequest.Stream {
|
if textRequest.Stream {
|
||||||
@ -274,6 +294,13 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
return errorWrapper(err, "marshal_text_request_failed", http.StatusInternalServerError)
|
return errorWrapper(err, "marshal_text_request_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
requestBody = bytes.NewBuffer(jsonStr)
|
requestBody = bytes.NewBuffer(jsonStr)
|
||||||
|
case APITypeGemini:
|
||||||
|
geminiChatRequest := requestOpenAI2Gemini(textRequest)
|
||||||
|
jsonStr, err := json.Marshal(geminiChatRequest)
|
||||||
|
if err != nil {
|
||||||
|
return errorWrapper(err, "marshal_text_request_failed", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
requestBody = bytes.NewBuffer(jsonStr)
|
||||||
case APITypeZhipu:
|
case APITypeZhipu:
|
||||||
zhipuRequest := requestOpenAI2Zhipu(textRequest)
|
zhipuRequest := requestOpenAI2Zhipu(textRequest)
|
||||||
jsonStr, err := json.Marshal(zhipuRequest)
|
jsonStr, err := json.Marshal(zhipuRequest)
|
||||||
@ -367,6 +394,8 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
req.Header.Set("Authorization", apiKey)
|
req.Header.Set("Authorization", apiKey)
|
||||||
case APITypePaLM:
|
case APITypePaLM:
|
||||||
// do not set Authorization header
|
// do not set Authorization header
|
||||||
|
case APITypeGemini:
|
||||||
|
// do not set Authorization header
|
||||||
default:
|
default:
|
||||||
req.Header.Set("Authorization", "Bearer "+apiKey)
|
req.Header.Set("Authorization", "Bearer "+apiKey)
|
||||||
}
|
}
|
||||||
@ -527,6 +556,25 @@ func relayTextHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
case APITypeGemini:
|
||||||
|
if textRequest.Stream {
|
||||||
|
err, responseText := geminiChatStreamHandler(c, resp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
textResponse.Usage.PromptTokens = promptTokens
|
||||||
|
textResponse.Usage.CompletionTokens = countTokenText(responseText, textRequest.Model)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
err, usage := geminiChatHandler(c, resp, promptTokens, textRequest.Model)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if usage != nil {
|
||||||
|
textResponse.Usage = *usage
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
case APITypeZhipu:
|
case APITypeZhipu:
|
||||||
if isStream {
|
if isStream {
|
||||||
err, usage := zhipuStreamHandler(c, resp)
|
err, usage := zhipuStreamHandler(c, resp)
|
||||||
|
@ -263,11 +263,52 @@ func setEventStreamHeaders(c *gin.Context) {
|
|||||||
c.Writer.Header().Set("X-Accel-Buffering", "no")
|
c.Writer.Header().Set("X-Accel-Buffering", "no")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GeneralErrorResponse struct {
|
||||||
|
Error OpenAIError `json:"error"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Err string `json:"err"`
|
||||||
|
ErrorMsg string `json:"error_msg"`
|
||||||
|
Header struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"header"`
|
||||||
|
Response struct {
|
||||||
|
Error struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"error"`
|
||||||
|
} `json:"response"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e GeneralErrorResponse) ToMessage() string {
|
||||||
|
if e.Error.Message != "" {
|
||||||
|
return e.Error.Message
|
||||||
|
}
|
||||||
|
if e.Message != "" {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
if e.Msg != "" {
|
||||||
|
return e.Msg
|
||||||
|
}
|
||||||
|
if e.Err != "" {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
if e.ErrorMsg != "" {
|
||||||
|
return e.ErrorMsg
|
||||||
|
}
|
||||||
|
if e.Header.Message != "" {
|
||||||
|
return e.Header.Message
|
||||||
|
}
|
||||||
|
if e.Response.Error.Message != "" {
|
||||||
|
return e.Response.Error.Message
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func relayErrorHandler(resp *http.Response) (openAIErrorWithStatusCode *OpenAIErrorWithStatusCode) {
|
func relayErrorHandler(resp *http.Response) (openAIErrorWithStatusCode *OpenAIErrorWithStatusCode) {
|
||||||
openAIErrorWithStatusCode = &OpenAIErrorWithStatusCode{
|
openAIErrorWithStatusCode = &OpenAIErrorWithStatusCode{
|
||||||
StatusCode: resp.StatusCode,
|
StatusCode: resp.StatusCode,
|
||||||
OpenAIError: OpenAIError{
|
OpenAIError: OpenAIError{
|
||||||
Message: fmt.Sprintf("bad response status code %d", resp.StatusCode),
|
Message: "",
|
||||||
Type: "upstream_error",
|
Type: "upstream_error",
|
||||||
Code: "bad_response_status_code",
|
Code: "bad_response_status_code",
|
||||||
Param: strconv.Itoa(resp.StatusCode),
|
Param: strconv.Itoa(resp.StatusCode),
|
||||||
@ -281,12 +322,20 @@ func relayErrorHandler(resp *http.Response) (openAIErrorWithStatusCode *OpenAIEr
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var textResponse TextResponse
|
var errResponse GeneralErrorResponse
|
||||||
err = json.Unmarshal(responseBody, &textResponse)
|
err = json.Unmarshal(responseBody, &errResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
openAIErrorWithStatusCode.OpenAIError = textResponse.Error
|
if errResponse.Error.Message != "" {
|
||||||
|
// OpenAI format error, so we override the default one
|
||||||
|
openAIErrorWithStatusCode.OpenAIError = errResponse.Error
|
||||||
|
} else {
|
||||||
|
openAIErrorWithStatusCode.OpenAIError.Message = errResponse.ToMessage()
|
||||||
|
}
|
||||||
|
if openAIErrorWithStatusCode.OpenAIError.Message == "" {
|
||||||
|
openAIErrorWithStatusCode.OpenAIError.Message = fmt.Sprintf("bad response status code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +230,13 @@ func xunfeiHandler(c *gin.Context, textRequest GeneralOpenAIRequest, appId strin
|
|||||||
case stop = <-stopChan:
|
case stop = <-stopChan:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
||||||
|
xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{
|
||||||
|
{
|
||||||
|
Content: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
xunfeiResponse.Payload.Choices.Text[0].Content = content
|
xunfeiResponse.Payload.Choices.Text[0].Content = content
|
||||||
|
|
||||||
response := responseXunfei2OpenAI(&xunfeiResponse)
|
response := responseXunfei2OpenAI(&xunfeiResponse)
|
||||||
|
@ -236,7 +236,7 @@ type ChatCompletionsStreamResponseChoice struct {
|
|||||||
Delta struct {
|
Delta struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
} `json:"delta"`
|
} `json:"delta"`
|
||||||
FinishReason *string `json:"finish_reason"`
|
FinishReason *string `json:"finish_reason,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatCompletionsStreamResponse struct {
|
type ChatCompletionsStreamResponse struct {
|
||||||
|
@ -87,6 +87,8 @@ func Distribute() func(c *gin.Context) {
|
|||||||
c.Set("api_version", channel.Other)
|
c.Set("api_version", channel.Other)
|
||||||
case common.ChannelTypeXunfei:
|
case common.ChannelTypeXunfei:
|
||||||
c.Set("api_version", channel.Other)
|
c.Set("api_version", channel.Other)
|
||||||
|
case common.ChannelTypeGemini:
|
||||||
|
c.Set("api_version", channel.Other)
|
||||||
case common.ChannelTypeAIProxyLibrary:
|
case common.ChannelTypeAIProxyLibrary:
|
||||||
c.Set("library_id", channel.Other)
|
c.Set("library_id", channel.Other)
|
||||||
case common.ChannelTypeAli:
|
case common.ChannelTypeAli:
|
||||||
|
@ -3,6 +3,7 @@ export const CHANNEL_OPTIONS = [
|
|||||||
{ key: 14, text: 'Anthropic Claude', value: 14, color: 'black' },
|
{ key: 14, text: 'Anthropic Claude', value: 14, color: 'black' },
|
||||||
{ key: 3, text: 'Azure OpenAI', value: 3, color: 'olive' },
|
{ key: 3, text: 'Azure OpenAI', value: 3, color: 'olive' },
|
||||||
{ key: 11, text: 'Google PaLM2', value: 11, color: 'orange' },
|
{ key: 11, text: 'Google PaLM2', value: 11, color: 'orange' },
|
||||||
|
{ key: 24, text: 'Google Gemini', value: 24, color: 'orange' },
|
||||||
{ key: 15, text: '百度文心千帆', value: 15, color: 'blue' },
|
{ key: 15, text: '百度文心千帆', value: 15, color: 'blue' },
|
||||||
{ key: 17, text: '阿里通义千问', value: 17, color: 'orange' },
|
{ key: 17, text: '阿里通义千问', value: 17, color: 'orange' },
|
||||||
{ key: 18, text: '讯飞星火认知', value: 18, color: 'blue' },
|
{ key: 18, text: '讯飞星火认知', value: 18, color: 'blue' },
|
||||||
|
@ -69,7 +69,7 @@ const EditChannel = () => {
|
|||||||
localModels = ['ERNIE-Bot', 'ERNIE-Bot-turbo', 'ERNIE-Bot-4', 'Embedding-V1'];
|
localModels = ['ERNIE-Bot', 'ERNIE-Bot-turbo', 'ERNIE-Bot-4', 'Embedding-V1'];
|
||||||
break;
|
break;
|
||||||
case 17:
|
case 17:
|
||||||
localModels = ['qwen-turbo', 'qwen-plus', 'text-embedding-v1'];
|
localModels = ['qwen-turbo', 'qwen-plus', 'qwen-max', 'qwen-max-longcontext', 'text-embedding-v1'];
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
localModels = ['chatglm_turbo', 'chatglm_pro', 'chatglm_std', 'chatglm_lite'];
|
localModels = ['chatglm_turbo', 'chatglm_pro', 'chatglm_std', 'chatglm_lite'];
|
||||||
@ -83,6 +83,9 @@ const EditChannel = () => {
|
|||||||
case 23:
|
case 23:
|
||||||
localModels = ['hunyuan'];
|
localModels = ['hunyuan'];
|
||||||
break;
|
break;
|
||||||
|
case 24:
|
||||||
|
localModels = ['gemini-pro'];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
setInputs((inputs) => ({ ...inputs, models: localModels }));
|
setInputs((inputs) => ({ ...inputs, models: localModels }));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user