mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-12 03:13:41 +08:00
Compare commits
11 Commits
v0.6.11-pr
...
v0.6.11-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aed090dd55 | ||
|
|
696265774e | ||
|
|
974729426d | ||
|
|
57c1367ec8 | ||
|
|
44233d5c04 | ||
|
|
bf45a955c3 | ||
|
|
20435fcbfc | ||
|
|
6e7a1c2323 | ||
|
|
dd65b997dd | ||
|
|
0b6d03d6c6 | ||
|
|
4375246e24 |
4
.github/workflows/docker-image.yml
vendored
4
.github/workflows/docker-image.yml
vendored
@@ -62,9 +62,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: ${{ contains(github.ref, 'alpha') && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
|
platforms: ${{ contains(github.ref, 'alpha') && 'linux/amd64' || 'linux/amd64' }}
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
build-args: |
|
|
||||||
TARGETARCH=${{ startsWith(matrix.platform, 'linux/arm64') && 'arm64' || 'amd64' }}
|
|
||||||
@@ -24,8 +24,7 @@ RUN apk add --no-cache \
|
|||||||
|
|
||||||
ENV GO111MODULE=on \
|
ENV GO111MODULE=on \
|
||||||
CGO_ENABLED=1 \
|
CGO_ENABLED=1 \
|
||||||
GOOS=linux \
|
GOOS=linux
|
||||||
GOARCH=$TARGETARCH
|
|
||||||
|
|
||||||
WORKDIR /build
|
WORKDIR /build
|
||||||
|
|
||||||
|
|||||||
13
common/utils/array.go
Normal file
13
common/utils/array.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
func DeDuplication(slice []string) []string {
|
||||||
|
m := make(map[string]bool)
|
||||||
|
for _, v := range slice {
|
||||||
|
m[v] = true
|
||||||
|
}
|
||||||
|
result := make([]string, 0, len(m))
|
||||||
|
for v := range m {
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -2,10 +2,13 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/songquanpeng/one-api/common"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"github.com/songquanpeng/one-api/common"
|
||||||
|
"github.com/songquanpeng/one-api/common/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Ability struct {
|
type Ability struct {
|
||||||
@@ -49,6 +52,7 @@ func GetRandomSatisfiedChannel(group string, model string, ignoreFirstPriority b
|
|||||||
|
|
||||||
func (channel *Channel) AddAbilities() error {
|
func (channel *Channel) AddAbilities() error {
|
||||||
models_ := strings.Split(channel.Models, ",")
|
models_ := strings.Split(channel.Models, ",")
|
||||||
|
models_ = utils.DeDuplication(models_)
|
||||||
groups_ := strings.Split(channel.Group, ",")
|
groups_ := strings.Split(channel.Group, ",")
|
||||||
abilities := make([]Ability, 0, len(models_))
|
abilities := make([]Ability, 0, len(models_))
|
||||||
for _, model := range models_ {
|
for _, model := range models_ {
|
||||||
|
|||||||
30
relay/adaptor/baiduv2/constants.go
Normal file
30
relay/adaptor/baiduv2/constants.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package baiduv2
|
||||||
|
|
||||||
|
// https://console.bce.baidu.com/support/?_=1692863460488×tamp=1739074632076#/api?product=QIANFAN&project=%E5%8D%83%E5%B8%86ModelBuilder&parent=%E5%AF%B9%E8%AF%9DChat%20V2&api=v2%2Fchat%2Fcompletions&method=post
|
||||||
|
// https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Fm2vrveyu#%E6%94%AF%E6%8C%81%E6%A8%A1%E5%9E%8B%E5%88%97%E8%A1%A8
|
||||||
|
|
||||||
|
var ModelList = []string{
|
||||||
|
"ernie-4.0-8k-latest",
|
||||||
|
"ernie-4.0-8k-preview",
|
||||||
|
"ernie-4.0-8k",
|
||||||
|
"ernie-4.0-turbo-8k-latest",
|
||||||
|
"ernie-4.0-turbo-8k-preview",
|
||||||
|
"ernie-4.0-turbo-8k",
|
||||||
|
"ernie-4.0-turbo-128k",
|
||||||
|
"ernie-3.5-8k-preview",
|
||||||
|
"ernie-3.5-8k",
|
||||||
|
"ernie-3.5-128k",
|
||||||
|
"ernie-speed-8k",
|
||||||
|
"ernie-speed-128k",
|
||||||
|
"ernie-speed-pro-128k",
|
||||||
|
"ernie-lite-8k",
|
||||||
|
"ernie-lite-pro-128k",
|
||||||
|
"ernie-tiny-8k",
|
||||||
|
"ernie-char-8k",
|
||||||
|
"ernie-char-fiction-8k",
|
||||||
|
"ernie-novel-8k",
|
||||||
|
"deepseek-v3",
|
||||||
|
"deepseek-r1",
|
||||||
|
"deepseek-r1-distill-qwen-32b",
|
||||||
|
"deepseek-r1-distill-qwen-14b",
|
||||||
|
}
|
||||||
17
relay/adaptor/baiduv2/main.go
Normal file
17
relay/adaptor/baiduv2/main.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package baiduv2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/songquanpeng/one-api/relay/meta"
|
||||||
|
"github.com/songquanpeng/one-api/relay/relaymode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetRequestURL(meta *meta.Meta) (string, error) {
|
||||||
|
switch meta.Mode {
|
||||||
|
case relaymode.ChatCompletions:
|
||||||
|
return fmt.Sprintf("%s/v2/chat/completions", meta.BaseURL), nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("unsupported relay mode %d for baidu v2", meta.Mode)
|
||||||
|
}
|
||||||
@@ -8,4 +8,6 @@ var ModelList = []string{
|
|||||||
"abab6-chat",
|
"abab6-chat",
|
||||||
"abab5.5-chat",
|
"abab5.5-chat",
|
||||||
"abab5.5s-chat",
|
"abab5.5s-chat",
|
||||||
|
"MiniMax-VL-01",
|
||||||
|
"MiniMax-Text-01",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor"
|
"github.com/songquanpeng/one-api/relay/adaptor"
|
||||||
|
"github.com/songquanpeng/one-api/relay/adaptor/baiduv2"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/doubao"
|
"github.com/songquanpeng/one-api/relay/adaptor/doubao"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/minimax"
|
"github.com/songquanpeng/one-api/relay/adaptor/minimax"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/novita"
|
"github.com/songquanpeng/one-api/relay/adaptor/novita"
|
||||||
@@ -52,6 +54,8 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
|
|||||||
return doubao.GetRequestURL(meta)
|
return doubao.GetRequestURL(meta)
|
||||||
case channeltype.Novita:
|
case channeltype.Novita:
|
||||||
return novita.GetRequestURL(meta)
|
return novita.GetRequestURL(meta)
|
||||||
|
case channeltype.BaiduV2:
|
||||||
|
return baiduv2.GetRequestURL(meta)
|
||||||
default:
|
default:
|
||||||
return GetFullRequestURL(meta.BaseURL, meta.RequestURLPath, meta.ChannelType), nil
|
return GetFullRequestURL(meta.BaseURL, meta.RequestURLPath, meta.ChannelType), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package openai
|
|||||||
import (
|
import (
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/ai360"
|
"github.com/songquanpeng/one-api/relay/adaptor/ai360"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/baichuan"
|
"github.com/songquanpeng/one-api/relay/adaptor/baichuan"
|
||||||
|
"github.com/songquanpeng/one-api/relay/adaptor/baiduv2"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/deepseek"
|
"github.com/songquanpeng/one-api/relay/adaptor/deepseek"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/doubao"
|
"github.com/songquanpeng/one-api/relay/adaptor/doubao"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/groq"
|
"github.com/songquanpeng/one-api/relay/adaptor/groq"
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
"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/xai"
|
"github.com/songquanpeng/one-api/relay/adaptor/xai"
|
||||||
|
"github.com/songquanpeng/one-api/relay/adaptor/xunfeiv2"
|
||||||
"github.com/songquanpeng/one-api/relay/channeltype"
|
"github.com/songquanpeng/one-api/relay/channeltype"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,6 +36,8 @@ var CompatibleChannels = []int{
|
|||||||
channeltype.Novita,
|
channeltype.Novita,
|
||||||
channeltype.SiliconFlow,
|
channeltype.SiliconFlow,
|
||||||
channeltype.XAI,
|
channeltype.XAI,
|
||||||
|
channeltype.BaiduV2,
|
||||||
|
channeltype.XunfeiV2,
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCompatibleChannelMeta(channelType int) (string, []string) {
|
func GetCompatibleChannelMeta(channelType int) (string, []string) {
|
||||||
@@ -68,6 +72,10 @@ func GetCompatibleChannelMeta(channelType int) (string, []string) {
|
|||||||
return "siliconflow", siliconflow.ModelList
|
return "siliconflow", siliconflow.ModelList
|
||||||
case channeltype.XAI:
|
case channeltype.XAI:
|
||||||
return "xai", xai.ModelList
|
return "xai", xai.ModelList
|
||||||
|
case channeltype.BaiduV2:
|
||||||
|
return "baiduv2", baiduv2.ModelList
|
||||||
|
case channeltype.XunfeiV2:
|
||||||
|
return "xunfeiv2", xunfeiv2.ModelList
|
||||||
default:
|
default:
|
||||||
return "openai", ModelList
|
return "openai", ModelList
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package xunfei
|
package xunfei
|
||||||
|
|
||||||
var ModelList = []string{
|
var ModelList = []string{
|
||||||
"SparkDesk",
|
"Spark-Lite",
|
||||||
"SparkDesk-v1.1",
|
"Spark-Pro",
|
||||||
"SparkDesk-v2.1",
|
"Spark-Pro-128K",
|
||||||
"SparkDesk-v3.1",
|
"Spark-Max",
|
||||||
"SparkDesk-v3.1-128K",
|
"Spark-Max-32K",
|
||||||
"SparkDesk-v3.5",
|
"Spark-4.0-Ultra",
|
||||||
"SparkDesk-v3.5-32K",
|
|
||||||
"SparkDesk-v4.0",
|
|
||||||
}
|
}
|
||||||
|
|||||||
97
relay/adaptor/xunfei/domain.go
Normal file
97
relay/adaptor/xunfei/domain.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package xunfei
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
|
||||||
|
|
||||||
|
//Spark4.0 Ultra 请求地址,对应的domain参数为4.0Ultra:
|
||||||
|
//
|
||||||
|
//wss://spark-api.xf-yun.com/v4.0/chat
|
||||||
|
//Spark Max-32K请求地址,对应的domain参数为max-32k
|
||||||
|
//
|
||||||
|
//wss://spark-api.xf-yun.com/chat/max-32k
|
||||||
|
//Spark Max请求地址,对应的domain参数为generalv3.5
|
||||||
|
//
|
||||||
|
//wss://spark-api.xf-yun.com/v3.5/chat
|
||||||
|
//Spark Pro-128K请求地址,对应的domain参数为pro-128k:
|
||||||
|
//
|
||||||
|
// wss://spark-api.xf-yun.com/chat/pro-128k
|
||||||
|
//Spark Pro请求地址,对应的domain参数为generalv3:
|
||||||
|
//
|
||||||
|
//wss://spark-api.xf-yun.com/v3.1/chat
|
||||||
|
//Spark Lite请求地址,对应的domain参数为lite:
|
||||||
|
//
|
||||||
|
//wss://spark-api.xf-yun.com/v1.1/chat
|
||||||
|
|
||||||
|
// Lite、Pro、Pro-128K、Max、Max-32K和4.0 Ultra
|
||||||
|
|
||||||
|
func parseAPIVersionByModelName(modelName string) string {
|
||||||
|
apiVersion := modelName2APIVersion(modelName)
|
||||||
|
if apiVersion != "" {
|
||||||
|
return apiVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
index := strings.IndexAny(modelName, "-")
|
||||||
|
if index != -1 {
|
||||||
|
return modelName[index+1:]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func modelName2APIVersion(modelName string) string {
|
||||||
|
switch modelName {
|
||||||
|
case "Spark-Lite":
|
||||||
|
return "v1.1"
|
||||||
|
case "Spark-Pro":
|
||||||
|
return "v3.1"
|
||||||
|
case "Spark-Pro-128K":
|
||||||
|
return "v3.1-128K"
|
||||||
|
case "Spark-Max":
|
||||||
|
return "v3.5"
|
||||||
|
case "Spark-Max-32K":
|
||||||
|
return "v3.5-32K"
|
||||||
|
case "Spark-4.0-Ultra":
|
||||||
|
return "v4.0"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
|
||||||
|
func apiVersion2domain(apiVersion string) string {
|
||||||
|
switch apiVersion {
|
||||||
|
case "v1.1":
|
||||||
|
return "lite"
|
||||||
|
case "v2.1":
|
||||||
|
return "generalv2"
|
||||||
|
case "v3.1":
|
||||||
|
return "generalv3"
|
||||||
|
case "v3.1-128K":
|
||||||
|
return "pro-128k"
|
||||||
|
case "v3.5":
|
||||||
|
return "generalv3.5"
|
||||||
|
case "v3.5-32K":
|
||||||
|
return "max-32k"
|
||||||
|
case "v4.0":
|
||||||
|
return "4.0Ultra"
|
||||||
|
}
|
||||||
|
return "general" + apiVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func getXunfeiAuthUrl(apiVersion string, apiKey string, apiSecret string) (string, string) {
|
||||||
|
var authUrl string
|
||||||
|
domain := apiVersion2domain(apiVersion)
|
||||||
|
switch apiVersion {
|
||||||
|
case "v3.1-128K":
|
||||||
|
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/chat/pro-128k"), apiKey, apiSecret)
|
||||||
|
break
|
||||||
|
case "v3.5-32K":
|
||||||
|
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/chat/max-32k"), apiKey, apiSecret)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/%s/chat", apiVersion), apiKey, apiSecret)
|
||||||
|
}
|
||||||
|
return domain, authUrl
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
@@ -270,48 +271,3 @@ func xunfeiMakeRequest(textRequest model.GeneralOpenAIRequest, domain, authUrl,
|
|||||||
|
|
||||||
return dataChan, stopChan, nil
|
return dataChan, stopChan, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAPIVersionByModelName(modelName string) string {
|
|
||||||
index := strings.IndexAny(modelName, "-")
|
|
||||||
if index != -1 {
|
|
||||||
return modelName[index+1:]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
|
|
||||||
func apiVersion2domain(apiVersion string) string {
|
|
||||||
switch apiVersion {
|
|
||||||
case "v1.1":
|
|
||||||
return "lite"
|
|
||||||
case "v2.1":
|
|
||||||
return "generalv2"
|
|
||||||
case "v3.1":
|
|
||||||
return "generalv3"
|
|
||||||
case "v3.1-128K":
|
|
||||||
return "pro-128k"
|
|
||||||
case "v3.5":
|
|
||||||
return "generalv3.5"
|
|
||||||
case "v3.5-32K":
|
|
||||||
return "max-32k"
|
|
||||||
case "v4.0":
|
|
||||||
return "4.0Ultra"
|
|
||||||
}
|
|
||||||
return "general" + apiVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
func getXunfeiAuthUrl(apiVersion string, apiKey string, apiSecret string) (string, string) {
|
|
||||||
var authUrl string
|
|
||||||
domain := apiVersion2domain(apiVersion)
|
|
||||||
switch apiVersion {
|
|
||||||
case "v3.1-128K":
|
|
||||||
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/chat/pro-128k"), apiKey, apiSecret)
|
|
||||||
break
|
|
||||||
case "v3.5-32K":
|
|
||||||
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/chat/max-32k"), apiKey, apiSecret)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/%s/chat", apiVersion), apiKey, apiSecret)
|
|
||||||
}
|
|
||||||
return domain, authUrl
|
|
||||||
}
|
|
||||||
|
|||||||
12
relay/adaptor/xunfeiv2/constants.go
Normal file
12
relay/adaptor/xunfeiv2/constants.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package xunfeiv2
|
||||||
|
|
||||||
|
// https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html#_3-%E8%AF%B7%E6%B1%82%E8%AF%B4%E6%98%8E
|
||||||
|
|
||||||
|
var ModelList = []string{
|
||||||
|
"lite",
|
||||||
|
"generalv3",
|
||||||
|
"pro-128k",
|
||||||
|
"generalv3.5",
|
||||||
|
"max-32k",
|
||||||
|
"4.0Ultra",
|
||||||
|
}
|
||||||
@@ -48,5 +48,7 @@ const (
|
|||||||
SiliconFlow
|
SiliconFlow
|
||||||
XAI
|
XAI
|
||||||
Replicate
|
Replicate
|
||||||
|
BaiduV2
|
||||||
|
XunfeiV2
|
||||||
Dummy
|
Dummy
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ var ChannelBaseURLs = []string{
|
|||||||
"https://api.siliconflow.cn", // 44
|
"https://api.siliconflow.cn", // 44
|
||||||
"https://api.x.ai", // 45
|
"https://api.x.ai", // 45
|
||||||
"https://api.replicate.com/v1/models/", // 46
|
"https://api.replicate.com/v1/models/", // 46
|
||||||
|
"https://qianfan.baidubce.com", // 47
|
||||||
|
"https://spark-api-open.xf-yun.com", // 48
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
textRequest.Model, _ = getMappedModelName(textRequest.Model, meta.ModelMapping)
|
textRequest.Model, _ = getMappedModelName(textRequest.Model, meta.ModelMapping)
|
||||||
meta.ActualModelName = textRequest.Model
|
meta.ActualModelName = textRequest.Model
|
||||||
// set system prompt if not empty
|
// set system prompt if not empty
|
||||||
systemPromptReset := setSystemPrompt(ctx, textRequest, meta.SystemPrompt)
|
systemPromptReset := setSystemPrompt(ctx, textRequest, meta.ForcedSystemPrompt)
|
||||||
// get model ratio & group ratio
|
// get model ratio & group ratio
|
||||||
modelRatio := billingratio.GetModelRatio(textRequest.Model, meta.ChannelType)
|
modelRatio := billingratio.GetModelRatio(textRequest.Model, meta.ChannelType)
|
||||||
groupRatio := billingratio.GetGroupRatio(meta.Group)
|
groupRatio := billingratio.GetGroupRatio(meta.Group)
|
||||||
@@ -88,7 +88,11 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getRequestBody(c *gin.Context, meta *meta.Meta, textRequest *model.GeneralOpenAIRequest, adaptor adaptor.Adaptor) (io.Reader, error) {
|
func getRequestBody(c *gin.Context, meta *meta.Meta, textRequest *model.GeneralOpenAIRequest, adaptor adaptor.Adaptor) (io.Reader, error) {
|
||||||
if !config.EnforceIncludeUsage && meta.APIType == apitype.OpenAI && meta.OriginModelName == meta.ActualModelName && meta.ChannelType != channeltype.Baichuan {
|
if !config.EnforceIncludeUsage &&
|
||||||
|
meta.APIType == apitype.OpenAI &&
|
||||||
|
meta.OriginModelName == meta.ActualModelName &&
|
||||||
|
meta.ChannelType != channeltype.Baichuan &&
|
||||||
|
meta.ForcedSystemPrompt == "" {
|
||||||
// no need to convert request for openai
|
// no need to convert request for openai
|
||||||
return c.Request.Body, nil
|
return c.Request.Body, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,29 +30,29 @@ type Meta struct {
|
|||||||
// OriginModelName is the model name from the raw user request
|
// OriginModelName is the model name from the raw user request
|
||||||
OriginModelName string
|
OriginModelName string
|
||||||
// ActualModelName is the model name after mapping
|
// ActualModelName is the model name after mapping
|
||||||
ActualModelName string
|
ActualModelName string
|
||||||
RequestURLPath string
|
RequestURLPath string
|
||||||
PromptTokens int // only for DoResponse
|
PromptTokens int // only for DoResponse
|
||||||
SystemPrompt string
|
ForcedSystemPrompt string
|
||||||
StartTime time.Time
|
StartTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetByContext(c *gin.Context) *Meta {
|
func GetByContext(c *gin.Context) *Meta {
|
||||||
meta := Meta{
|
meta := Meta{
|
||||||
Mode: relaymode.GetByPath(c.Request.URL.Path),
|
Mode: relaymode.GetByPath(c.Request.URL.Path),
|
||||||
ChannelType: c.GetInt(ctxkey.Channel),
|
ChannelType: c.GetInt(ctxkey.Channel),
|
||||||
ChannelId: c.GetInt(ctxkey.ChannelId),
|
ChannelId: c.GetInt(ctxkey.ChannelId),
|
||||||
TokenId: c.GetInt(ctxkey.TokenId),
|
TokenId: c.GetInt(ctxkey.TokenId),
|
||||||
TokenName: c.GetString(ctxkey.TokenName),
|
TokenName: c.GetString(ctxkey.TokenName),
|
||||||
UserId: c.GetInt(ctxkey.Id),
|
UserId: c.GetInt(ctxkey.Id),
|
||||||
Group: c.GetString(ctxkey.Group),
|
Group: c.GetString(ctxkey.Group),
|
||||||
ModelMapping: c.GetStringMapString(ctxkey.ModelMapping),
|
ModelMapping: c.GetStringMapString(ctxkey.ModelMapping),
|
||||||
OriginModelName: c.GetString(ctxkey.RequestModel),
|
OriginModelName: c.GetString(ctxkey.RequestModel),
|
||||||
BaseURL: c.GetString(ctxkey.BaseURL),
|
BaseURL: c.GetString(ctxkey.BaseURL),
|
||||||
APIKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "),
|
APIKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "),
|
||||||
RequestURLPath: c.Request.URL.String(),
|
RequestURLPath: c.Request.URL.String(),
|
||||||
SystemPrompt: c.GetString(ctxkey.SystemPrompt),
|
ForcedSystemPrompt: c.GetString(ctxkey.SystemPrompt),
|
||||||
StartTime: time.Now(),
|
StartTime: time.Now(),
|
||||||
}
|
}
|
||||||
cfg, ok := c.Get(ctxkey.Config)
|
cfg, ok := c.Get(ctxkey.Config)
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ type GeneralOpenAIRequest struct {
|
|||||||
Messages []Message `json:"messages,omitempty"`
|
Messages []Message `json:"messages,omitempty"`
|
||||||
Model string `json:"model,omitempty"`
|
Model string `json:"model,omitempty"`
|
||||||
Store *bool `json:"store,omitempty"`
|
Store *bool `json:"store,omitempty"`
|
||||||
|
ReasoningEffort *string `json:"reasoning_effort,omitempty"`
|
||||||
Metadata any `json:"metadata,omitempty"`
|
Metadata any `json:"metadata,omitempty"`
|
||||||
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
|
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
|
||||||
LogitBias any `json:"logit_bias,omitempty"`
|
LogitBias any `json:"logit_bias,omitempty"`
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ type Usage struct {
|
|||||||
PromptTokens int `json:"prompt_tokens"`
|
PromptTokens int `json:"prompt_tokens"`
|
||||||
CompletionTokens int `json:"completion_tokens"`
|
CompletionTokens int `json:"completion_tokens"`
|
||||||
TotalTokens int `json:"total_tokens"`
|
TotalTokens int `json:"total_tokens"`
|
||||||
|
|
||||||
|
CompletionTokensDetails *CompletionTokensDetails `json:"completion_tokens_details,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompletionTokensDetails struct {
|
||||||
|
ReasoningTokens int `json:"reasoning_tokens"`
|
||||||
|
AcceptedPredictionTokens int `json:"accepted_prediction_tokens"`
|
||||||
|
RejectedPredictionTokens int `json:"rejected_prediction_tokens"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Error struct {
|
type Error struct {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const CHANNEL_OPTIONS = [
|
|||||||
{ key: 24, text: 'Google Gemini', value: 24, color: 'orange' },
|
{ key: 24, text: 'Google Gemini', value: 24, color: 'orange' },
|
||||||
{ key: 28, text: 'Mistral AI', value: 28, color: 'orange' },
|
{ key: 28, text: 'Mistral AI', value: 28, color: 'orange' },
|
||||||
{ key: 41, text: 'Novita', value: 41, color: 'purple' },
|
{ key: 41, text: 'Novita', value: 41, color: 'purple' },
|
||||||
{key: 40, text: '火山引擎', value: 40, color: 'blue'},
|
{key: 40, text: '字节火山引擎', value: 40, color: 'blue'},
|
||||||
{ key: 15, text: '百度文心千帆', value: 15, color: 'blue' },
|
{ key: 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' },
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export const CHANNEL_OPTIONS = {
|
|||||||
},
|
},
|
||||||
40: {
|
40: {
|
||||||
key: 40,
|
key: 40,
|
||||||
text: '火山引擎',
|
text: '字节火山引擎',
|
||||||
value: 40,
|
value: 40,
|
||||||
color: 'primary'
|
color: 'primary'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,17 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import {useTranslation} from 'react-i18next';
|
||||||
import {
|
import {Button, Dropdown, Form, Input, Label, Message, Pagination, Popup, Table,} from 'semantic-ui-react';
|
||||||
Button,
|
import {Link} from 'react-router-dom';
|
||||||
Dropdown,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
Label,
|
|
||||||
Message,
|
|
||||||
Pagination,
|
|
||||||
Popup,
|
|
||||||
Table,
|
|
||||||
} from 'semantic-ui-react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import {
|
import {
|
||||||
API,
|
API,
|
||||||
loadChannelModels,
|
loadChannelModels,
|
||||||
@@ -23,8 +13,8 @@ import {
|
|||||||
timestamp2string,
|
timestamp2string,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
|
|
||||||
import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../constants';
|
import {CHANNEL_OPTIONS, ITEMS_PER_PAGE} from '../constants';
|
||||||
import { renderGroup, renderNumber } from '../helpers/render';
|
import {renderGroup, renderNumber} from '../helpers/render';
|
||||||
|
|
||||||
function renderTimestamp(timestamp) {
|
function renderTimestamp(timestamp) {
|
||||||
return <>{timestamp2string(timestamp)}</>;
|
return <>{timestamp2string(timestamp)}</>;
|
||||||
@@ -93,30 +83,32 @@ const ChannelsTable = () => {
|
|||||||
const [showPrompt, setShowPrompt] = useState(shouldShowPrompt(promptID));
|
const [showPrompt, setShowPrompt] = useState(shouldShowPrompt(promptID));
|
||||||
const [showDetail, setShowDetail] = useState(isShowDetail());
|
const [showDetail, setShowDetail] = useState(isShowDetail());
|
||||||
|
|
||||||
|
const processChannelData = (channel) => {
|
||||||
|
if (channel.models === '') {
|
||||||
|
channel.models = [];
|
||||||
|
channel.test_model = '';
|
||||||
|
} else {
|
||||||
|
channel.models = channel.models.split(',');
|
||||||
|
if (channel.models.length > 0) {
|
||||||
|
channel.test_model = channel.models[0];
|
||||||
|
}
|
||||||
|
channel.model_options = channel.models.map((model) => {
|
||||||
|
return {
|
||||||
|
key: model,
|
||||||
|
text: model,
|
||||||
|
value: model,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
console.log('channel', channel);
|
||||||
|
}
|
||||||
|
return channel;
|
||||||
|
};
|
||||||
|
|
||||||
const loadChannels = async (startIdx) => {
|
const loadChannels = async (startIdx) => {
|
||||||
const res = await API.get(`/api/channel/?p=${startIdx}`);
|
const res = await API.get(`/api/channel/?p=${startIdx}`);
|
||||||
const { success, message, data } = res.data;
|
const {success, message, data} = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
let localChannels = data.map((channel) => {
|
let localChannels = data.map(processChannelData);
|
||||||
if (channel.models === '') {
|
|
||||||
channel.models = [];
|
|
||||||
channel.test_model = '';
|
|
||||||
} else {
|
|
||||||
channel.models = channel.models.split(',');
|
|
||||||
if (channel.models.length > 0) {
|
|
||||||
channel.test_model = channel.models[0];
|
|
||||||
}
|
|
||||||
channel.model_options = channel.models.map((model) => {
|
|
||||||
return {
|
|
||||||
key: model,
|
|
||||||
text: model,
|
|
||||||
value: model,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
console.log('channel', channel);
|
|
||||||
}
|
|
||||||
return channel;
|
|
||||||
});
|
|
||||||
if (startIdx === 0) {
|
if (startIdx === 0) {
|
||||||
setChannels(localChannels);
|
setChannels(localChannels);
|
||||||
} else {
|
} else {
|
||||||
@@ -301,7 +293,8 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.get(`/api/channel/search?keyword=${searchKeyword}`);
|
const res = await API.get(`/api/channel/search?keyword=${searchKeyword}`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
setChannels(data);
|
let localChannels = data.map(processChannelData);
|
||||||
|
setChannels(localChannels);
|
||||||
setActivePage(1);
|
setActivePage(1);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
|
|||||||
@@ -1,48 +1,80 @@
|
|||||||
export const CHANNEL_OPTIONS = [
|
export const CHANNEL_OPTIONS = [
|
||||||
{ key: 1, text: 'OpenAI', value: 1, color: 'green' },
|
{key: 1, text: 'OpenAI', value: 1, color: 'green'},
|
||||||
{ key: 14, text: 'Anthropic Claude', value: 14, color: 'black' },
|
{key: 14, text: 'Anthropic Claude', value: 14, color: 'black'},
|
||||||
{ key: 33, text: 'AWS', value: 33, color: 'black' },
|
{key: 33, text: 'AWS', value: 33, 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: 24, text: 'Google Gemini', value: 24, color: 'orange'},
|
||||||
{ key: 28, text: 'Mistral AI', value: 28, color: 'orange' },
|
{key: 28, text: 'Mistral AI', value: 28, color: 'orange'},
|
||||||
{ key: 41, text: 'Novita', value: 41, color: 'purple' },
|
{key: 41, text: 'Novita', value: 41, color: 'purple'},
|
||||||
{key: 40, text: '火山引擎', value: 40, color: 'blue'},
|
{
|
||||||
{ key: 15, text: '百度文心千帆', value: 15, color: 'blue' },
|
key: 40,
|
||||||
{ key: 17, text: '阿里通义千问', value: 17, color: 'orange' },
|
text: '字节火山引擎',
|
||||||
{ key: 18, text: '讯飞星火认知', value: 18, color: 'blue' },
|
value: 40,
|
||||||
{ key: 16, text: '智谱 ChatGLM', value: 16, color: 'violet' },
|
color: 'blue',
|
||||||
{ key: 19, text: '360 智脑', value: 19, color: 'blue' },
|
description: '原字节跳动豆包',
|
||||||
{ key: 25, text: 'Moonshot AI', value: 25, color: 'black' },
|
},
|
||||||
{ key: 23, text: '腾讯混元', value: 23, color: 'teal' },
|
{
|
||||||
{ key: 26, text: '百川大模型', value: 26, color: 'orange' },
|
key: 15,
|
||||||
{ key: 27, text: 'MiniMax', value: 27, color: 'red' },
|
text: '百度文心千帆',
|
||||||
{ key: 29, text: 'Groq', value: 29, color: 'orange' },
|
value: 15,
|
||||||
{ key: 30, text: 'Ollama', value: 30, color: 'black' },
|
color: 'blue',
|
||||||
{ key: 31, text: '零一万物', value: 31, color: 'green' },
|
tip: '请前往<a href="https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application/v1" target="_blank">此处</a>获取 AK(API Key)以及 SK(Secret Key),注意,V2 版本接口请使用 <strong>百度文心千帆 V2 </strong>渠道类型',
|
||||||
{ key: 32, text: '阶跃星辰', value: 32, color: 'blue' },
|
},
|
||||||
{ key: 34, text: 'Coze', value: 34, color: 'blue' },
|
{
|
||||||
{ key: 35, text: 'Cohere', value: 35, color: 'blue' },
|
key: 47,
|
||||||
{ key: 36, text: 'DeepSeek', value: 36, color: 'black' },
|
text: '百度文心千帆 V2',
|
||||||
{ key: 37, text: 'Cloudflare', value: 37, color: 'orange' },
|
value: 47,
|
||||||
{ key: 38, text: 'DeepL', value: 38, color: 'black' },
|
color: 'blue',
|
||||||
{ key: 39, text: 'together.ai', value: 39, color: 'blue' },
|
tip: '请前往<a href="https://console.bce.baidu.com/iam/#/iam/apikey/list" target="_blank">此处</a>获取 API Key,注意本渠道仅支持<a target="_blank" href="https://cloud.baidu.com/doc/WENXINWORKSHOP/s/em4tsqo3v">推理服务 V2</a>相关模型',
|
||||||
{ key: 42, text: 'VertexAI', value: 42, color: 'blue' },
|
},
|
||||||
{ key: 43, text: 'Proxy', value: 43, color: 'blue' },
|
{key: 17, text: '阿里通义千问', value: 17, color: 'orange'},
|
||||||
{ key: 44, text: 'SiliconFlow', value: 44, color: 'blue' },
|
{
|
||||||
{ key: 45, text: 'xAI', value: 45, color: 'blue' },
|
key: 18,
|
||||||
{ key: 46, text: 'Replicate', value: 46, color: 'blue' },
|
text: '讯飞星火认知',
|
||||||
{ key: 8, text: '自定义渠道', value: 8, color: 'pink' },
|
value: 18,
|
||||||
{ key: 22, text: '知识库:FastGPT', value: 22, color: 'blue' },
|
color: 'blue',
|
||||||
{ key: 21, text: '知识库:AI Proxy', value: 21, color: 'purple' },
|
tip: '本渠道基于讯飞 WebSocket 版本 API,如需 HTTP 版本,请使用<strong>讯飞星火认知 V2</strong>渠道',
|
||||||
{ key: 20, text: '代理:OpenRouter', value: 20, color: 'black' },
|
},
|
||||||
{ key: 2, text: '代理:API2D', value: 2, color: 'blue' },
|
{
|
||||||
{ key: 5, text: '代理:OpenAI-SB', value: 5, color: 'brown' },
|
key: 48,
|
||||||
{ key: 7, text: '代理:OhMyGPT', value: 7, color: 'purple' },
|
text: '讯飞星火认知 V2',
|
||||||
{ key: 10, text: '代理:AI Proxy', value: 10, color: 'purple' },
|
value: 48,
|
||||||
{ key: 4, text: '代理:CloseAI', value: 4, color: 'teal' },
|
color: 'blue',
|
||||||
{ key: 6, text: '代理:OpenAI Max', value: 6, color: 'violet' },
|
tip: 'HTTP 版本的讯飞接口,前往<a href="https://console.xfyun.cn/services/cbm" target="_blank">此处</a>获取 HTTP 服务接口认证密钥',
|
||||||
{ key: 9, text: '代理:AI.LS', value: 9, color: 'yellow' },
|
},
|
||||||
{ key: 12, text: '代理:API2GPT', value: 12, color: 'blue' },
|
{key: 16, text: '智谱 ChatGLM', value: 16, color: 'violet'},
|
||||||
{ key: 13, text: '代理:AIGC2D', value: 13, color: 'purple' }
|
{key: 19, text: '360 智脑', value: 19, color: 'blue'},
|
||||||
|
{key: 25, text: 'Moonshot AI', value: 25, color: 'black'},
|
||||||
|
{key: 23, text: '腾讯混元', value: 23, color: 'teal'},
|
||||||
|
{key: 26, text: '百川大模型', value: 26, color: 'orange'},
|
||||||
|
{key: 27, text: 'MiniMax', value: 27, color: 'red'},
|
||||||
|
{key: 29, text: 'Groq', value: 29, color: 'orange'},
|
||||||
|
{key: 30, text: 'Ollama', value: 30, color: 'black'},
|
||||||
|
{key: 31, text: '零一万物', value: 31, color: 'green'},
|
||||||
|
{key: 32, text: '阶跃星辰', value: 32, color: 'blue'},
|
||||||
|
{key: 34, text: 'Coze', value: 34, color: 'blue'},
|
||||||
|
{key: 35, text: 'Cohere', value: 35, color: 'blue'},
|
||||||
|
{key: 36, text: 'DeepSeek', value: 36, color: 'black'},
|
||||||
|
{key: 37, text: 'Cloudflare', value: 37, color: 'orange'},
|
||||||
|
{key: 38, text: 'DeepL', value: 38, color: 'black'},
|
||||||
|
{key: 39, text: 'together.ai', value: 39, color: 'blue'},
|
||||||
|
{key: 42, text: 'VertexAI', value: 42, color: 'blue'},
|
||||||
|
{key: 43, text: 'Proxy', value: 43, color: 'blue'},
|
||||||
|
{key: 44, text: 'SiliconFlow', value: 44, color: 'blue'},
|
||||||
|
{key: 45, text: 'xAI', value: 45, color: 'blue'},
|
||||||
|
{key: 46, text: 'Replicate', value: 46, color: 'blue'},
|
||||||
|
{key: 8, text: '自定义渠道', value: 8, color: 'pink'},
|
||||||
|
{key: 22, text: '知识库:FastGPT', value: 22, color: 'blue'},
|
||||||
|
{key: 21, text: '知识库:AI Proxy', value: 21, color: 'purple'},
|
||||||
|
{key: 20, text: '代理:OpenRouter', value: 20, color: 'black'},
|
||||||
|
{key: 2, text: '代理:API2D', value: 2, color: 'blue'},
|
||||||
|
{key: 5, text: '代理:OpenAI-SB', value: 5, color: 'brown'},
|
||||||
|
{key: 7, text: '代理:OhMyGPT', value: 7, color: 'purple'},
|
||||||
|
{key: 10, text: '代理:AI Proxy', value: 10, color: 'purple'},
|
||||||
|
{key: 4, text: '代理:CloseAI', value: 4, color: 'teal'},
|
||||||
|
{key: 6, text: '代理:OpenAI Max', value: 6, color: 'violet'},
|
||||||
|
{key: 9, text: '代理:AI.LS', value: 9, color: 'yellow'},
|
||||||
|
{key: 12, text: '代理:API2GPT', value: 12, color: 'blue'},
|
||||||
|
{key: 13, text: '代理:AIGC2D', value: 13, color: 'purple'},
|
||||||
];
|
];
|
||||||
|
|||||||
13
web/default/src/helpers/helper.js
Normal file
13
web/default/src/helpers/helper.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import {CHANNEL_OPTIONS} from '../constants';
|
||||||
|
|
||||||
|
let channelMap = undefined;
|
||||||
|
|
||||||
|
export function getChannelOption(channelId) {
|
||||||
|
if (channelMap === undefined) {
|
||||||
|
channelMap = {};
|
||||||
|
CHANNEL_OPTIONS.forEach((option) => {
|
||||||
|
channelMap[option.key] = option;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return channelMap[channelId];
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Label } from 'semantic-ui-react';
|
import {Label, Message} from 'semantic-ui-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import {getChannelOption} from './helper';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
export function renderText(text, limit) {
|
export function renderText(text, limit) {
|
||||||
if (text.length > limit) {
|
if (text.length > limit) {
|
||||||
@@ -98,3 +99,15 @@ export function renderColorLabel(text) {
|
|||||||
</Label>
|
</Label>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderChannelTip(channelId) {
|
||||||
|
let channel = getChannelOption(channelId);
|
||||||
|
if (channel === undefined || channel.tip === undefined) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Message>
|
||||||
|
<div dangerouslySetInnerHTML={{__html: channel.tip}}></div>
|
||||||
|
</Message>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,25 +1,10 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import {useTranslation} from 'react-i18next';
|
||||||
import {
|
import {Button, Card, Form, Input, Message,} from 'semantic-ui-react';
|
||||||
Button,
|
import {useNavigate, useParams} from 'react-router-dom';
|
||||||
Form,
|
import {API, copy, getChannelModels, showError, showInfo, showSuccess, verifyJSON,} from '../../helpers';
|
||||||
Header,
|
import {CHANNEL_OPTIONS} from '../../constants';
|
||||||
Input,
|
import {renderChannelTip} from '../../helpers/render';
|
||||||
Message,
|
|
||||||
Segment,
|
|
||||||
Card,
|
|
||||||
} from 'semantic-ui-react';
|
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
|
||||||
import {
|
|
||||||
API,
|
|
||||||
copy,
|
|
||||||
getChannelModels,
|
|
||||||
showError,
|
|
||||||
showInfo,
|
|
||||||
showSuccess,
|
|
||||||
verifyJSON,
|
|
||||||
} from '../../helpers';
|
|
||||||
import { CHANNEL_OPTIONS } from '../../constants';
|
|
||||||
|
|
||||||
const MODEL_MAPPING_EXAMPLE = {
|
const MODEL_MAPPING_EXAMPLE = {
|
||||||
'gpt-3.5-turbo-0301': 'gpt-3.5-turbo',
|
'gpt-3.5-turbo-0301': 'gpt-3.5-turbo',
|
||||||
@@ -310,6 +295,7 @@ const EditChannel = () => {
|
|||||||
options={groupOptions}
|
options={groupOptions}
|
||||||
/>
|
/>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
|
{renderChannelTip(inputs.type)}
|
||||||
|
|
||||||
{/* Azure OpenAI specific fields */}
|
{/* Azure OpenAI specific fields */}
|
||||||
{inputs.type === 3 && (
|
{inputs.type === 3 && (
|
||||||
|
|||||||
Reference in New Issue
Block a user