mirror of
				https://github.com/songquanpeng/one-api.git
				synced 2025-11-04 07:43:41 +08:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			v0.6.11-pr
			...
			v0.6.11-pr
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6916debf66 | ||
| 
						 | 
					53da209134 | ||
| 
						 | 
					517f6ad211 | ||
| 
						 | 
					10aba11f18 | ||
| 
						 | 
					4d011c5f98 | ||
| 
						 | 
					eb96aa635e | ||
| 
						 | 
					c715f2bc1d | ||
| 
						 | 
					aed090dd55 | ||
| 
						 | 
					696265774e | ||
| 
						 | 
					974729426d | ||
| 
						 | 
					57c1367ec8 | ||
| 
						 | 
					44233d5c04 | ||
| 
						 | 
					bf45a955c3 | 
@@ -163,4 +163,4 @@ var UserContentRequestProxy = env.String("USER_CONTENT_REQUEST_PROXY", "")
 | 
			
		||||
var UserContentRequestTimeout = env.Int("USER_CONTENT_REQUEST_TIMEOUT", 30)
 | 
			
		||||
 | 
			
		||||
var EnforceIncludeUsage = env.Bool("ENFORCE_INCLUDE_USAGE", false)
 | 
			
		||||
var TestPrompt = env.String("TEST_PROMPT", "Print your model name exactly and do not output without any other text.")
 | 
			
		||||
var TestPrompt = env.String("TEST_PROMPT", "Output only your specific model name with no additional text.")
 | 
			
		||||
 
 | 
			
		||||
@@ -112,6 +112,13 @@ type DeepSeekUsageResponse struct {
 | 
			
		||||
	} `json:"balance_infos"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OpenRouterResponse struct {
 | 
			
		||||
	Data struct {
 | 
			
		||||
		TotalCredits float64 `json:"total_credits"`
 | 
			
		||||
		TotalUsage   float64 `json:"total_usage"`
 | 
			
		||||
	} `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAuthHeader get auth header
 | 
			
		||||
func GetAuthHeader(token string) http.Header {
 | 
			
		||||
	h := http.Header{}
 | 
			
		||||
@@ -285,6 +292,22 @@ func updateChannelDeepSeekBalance(channel *model.Channel) (float64, error) {
 | 
			
		||||
	return balance, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateChannelOpenRouterBalance(channel *model.Channel) (float64, error) {
 | 
			
		||||
	url := "https://openrouter.ai/api/v1/credits"
 | 
			
		||||
	body, err := GetResponseBody("GET", url, channel, GetAuthHeader(channel.Key))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	response := OpenRouterResponse{}
 | 
			
		||||
	err = json.Unmarshal(body, &response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	balance := response.Data.TotalCredits - response.Data.TotalUsage
 | 
			
		||||
	channel.UpdateBalance(balance)
 | 
			
		||||
	return balance, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateChannelBalance(channel *model.Channel) (float64, error) {
 | 
			
		||||
	baseURL := channeltype.ChannelBaseURLs[channel.Type]
 | 
			
		||||
	if channel.GetBaseURL() == "" {
 | 
			
		||||
@@ -313,6 +336,8 @@ func updateChannelBalance(channel *model.Channel) (float64, error) {
 | 
			
		||||
		return updateChannelSiliconFlowBalance(channel)
 | 
			
		||||
	case channeltype.DeepSeek:
 | 
			
		||||
		return updateChannelDeepSeekBalance(channel)
 | 
			
		||||
	case channeltype.OpenRouter:
 | 
			
		||||
		return updateChannelOpenRouterBalance(channel)
 | 
			
		||||
	default:
 | 
			
		||||
		return 0, errors.New("尚未实现")
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -153,6 +153,7 @@ func testChannel(ctx context.Context, channel *model.Channel, request *relaymode
 | 
			
		||||
	rawResponse := w.Body.String()
 | 
			
		||||
	_, responseMessage, err = parseTestResponse(rawResponse)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.SysError(fmt.Sprintf("failed to parse error: %s, \nresponse: %s", err.Error(), rawResponse))
 | 
			
		||||
		return "", err, nil
 | 
			
		||||
	}
 | 
			
		||||
	result := w.Result()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								relay/adaptor/alibailian/constants.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								relay/adaptor/alibailian/constants.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
package alibailian
 | 
			
		||||
 | 
			
		||||
// https://help.aliyun.com/zh/model-studio/getting-started/models
 | 
			
		||||
 | 
			
		||||
var ModelList = []string{
 | 
			
		||||
	"qwen-turbo",
 | 
			
		||||
	"qwen-plus",
 | 
			
		||||
	"qwen-long",
 | 
			
		||||
	"qwen-max",
 | 
			
		||||
	"qwen-coder-plus",
 | 
			
		||||
	"qwen-coder-plus-latest",
 | 
			
		||||
	"qwen-coder-turbo",
 | 
			
		||||
	"qwen-coder-turbo-latest",
 | 
			
		||||
	"qwen-mt-plus",
 | 
			
		||||
	"qwen-mt-turbo",
 | 
			
		||||
	"qwq-32b-preview",
 | 
			
		||||
 | 
			
		||||
	"deepseek-r1",
 | 
			
		||||
	"deepseek-v3",
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								relay/adaptor/alibailian/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								relay/adaptor/alibailian/main.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
package alibailian
 | 
			
		||||
 | 
			
		||||
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/compatible-mode/v1/chat/completions", meta.BaseURL), nil
 | 
			
		||||
	case relaymode.Embeddings:
 | 
			
		||||
		return fmt.Sprintf("%s/compatible-mode/v1/embeddings", meta.BaseURL), nil
 | 
			
		||||
	default:
 | 
			
		||||
	}
 | 
			
		||||
	return "", fmt.Errorf("unsupported relay mode %d for ali bailian", meta.Mode)
 | 
			
		||||
}
 | 
			
		||||
@@ -8,4 +8,6 @@ var ModelList = []string{
 | 
			
		||||
	"abab6-chat",
 | 
			
		||||
	"abab5.5-chat",
 | 
			
		||||
	"abab5.5s-chat",
 | 
			
		||||
	"MiniMax-VL-01",
 | 
			
		||||
	"MiniMax-Text-01",
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import (
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/alibailian"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/baiduv2"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/doubao"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/minimax"
 | 
			
		||||
@@ -56,6 +57,8 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
 | 
			
		||||
		return novita.GetRequestURL(meta)
 | 
			
		||||
	case channeltype.BaiduV2:
 | 
			
		||||
		return baiduv2.GetRequestURL(meta)
 | 
			
		||||
	case channeltype.AliBailian:
 | 
			
		||||
		return alibailian.GetRequestURL(meta)
 | 
			
		||||
	default:
 | 
			
		||||
		return GetFullRequestURL(meta.BaseURL, meta.RequestURLPath, meta.ChannelType), nil
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package openai
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/ai360"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/alibailian"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/baichuan"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/baiduv2"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/deepseek"
 | 
			
		||||
@@ -12,10 +13,12 @@ import (
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/mistral"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/moonshot"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/novita"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/openrouter"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/siliconflow"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/stepfun"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/togetherai"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/xai"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/adaptor/xunfeiv2"
 | 
			
		||||
	"github.com/songquanpeng/one-api/relay/channeltype"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -36,6 +39,7 @@ var CompatibleChannels = []int{
 | 
			
		||||
	channeltype.SiliconFlow,
 | 
			
		||||
	channeltype.XAI,
 | 
			
		||||
	channeltype.BaiduV2,
 | 
			
		||||
	channeltype.XunfeiV2,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetCompatibleChannelMeta(channelType int) (string, []string) {
 | 
			
		||||
@@ -72,6 +76,12 @@ func GetCompatibleChannelMeta(channelType int) (string, []string) {
 | 
			
		||||
		return "xai", xai.ModelList
 | 
			
		||||
	case channeltype.BaiduV2:
 | 
			
		||||
		return "baiduv2", baiduv2.ModelList
 | 
			
		||||
	case channeltype.XunfeiV2:
 | 
			
		||||
		return "xunfeiv2", xunfeiv2.ModelList
 | 
			
		||||
	case channeltype.OpenRouter:
 | 
			
		||||
		return "openrouter", openrouter.ModelList
 | 
			
		||||
	case channeltype.AliBailian:
 | 
			
		||||
		return "alibailian", alibailian.ModelList
 | 
			
		||||
	default:
 | 
			
		||||
		return "openai", ModelList
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										20
									
								
								relay/adaptor/openrouter/constants.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								relay/adaptor/openrouter/constants.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
package openrouter
 | 
			
		||||
 | 
			
		||||
var ModelList = []string{
 | 
			
		||||
	"openai/gpt-3.5-turbo",
 | 
			
		||||
	"openai/chatgpt-4o-latest",
 | 
			
		||||
	"openai/o1",
 | 
			
		||||
	"openai/o1-preview",
 | 
			
		||||
	"openai/o1-mini",
 | 
			
		||||
	"openai/o3-mini",
 | 
			
		||||
	"google/gemini-2.0-flash-001",
 | 
			
		||||
	"google/gemini-2.0-flash-thinking-exp:free",
 | 
			
		||||
	"google/gemini-2.0-flash-lite-preview-02-05:free",
 | 
			
		||||
	"google/gemini-2.0-pro-exp-02-05:free",
 | 
			
		||||
	"google/gemini-flash-1.5-8b",
 | 
			
		||||
	"anthropic/claude-3.5-sonnet",
 | 
			
		||||
	"anthropic/claude-3.5-haiku",
 | 
			
		||||
	"deepseek/deepseek-r1:free",
 | 
			
		||||
	"deepseek/deepseek-r1",
 | 
			
		||||
	"qwen/qwen-vl-plus:free",
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,14 @@
 | 
			
		||||
package xai
 | 
			
		||||
 | 
			
		||||
//https://console.x.ai/
 | 
			
		||||
 | 
			
		||||
var ModelList = []string{
 | 
			
		||||
	"grok-2",
 | 
			
		||||
	"grok-vision-beta",
 | 
			
		||||
	"grok-2-vision-1212",
 | 
			
		||||
	"grok-2-vision",
 | 
			
		||||
	"grok-2-vision-latest",
 | 
			
		||||
	"grok-2-1212",
 | 
			
		||||
	"grok-2-latest",
 | 
			
		||||
	"grok-beta",
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,10 @@
 | 
			
		||||
package xunfei
 | 
			
		||||
 | 
			
		||||
var ModelList = []string{
 | 
			
		||||
	"SparkDesk",
 | 
			
		||||
	"SparkDesk-v1.1",
 | 
			
		||||
	"SparkDesk-v2.1",
 | 
			
		||||
	"SparkDesk-v3.1",
 | 
			
		||||
	"SparkDesk-v3.1-128K",
 | 
			
		||||
	"SparkDesk-v3.5",
 | 
			
		||||
	"SparkDesk-v3.5-32K",
 | 
			
		||||
	"SparkDesk-v4.0",
 | 
			
		||||
	"Spark-Lite",
 | 
			
		||||
	"Spark-Pro",
 | 
			
		||||
	"Spark-Pro-128K",
 | 
			
		||||
	"Spark-Max",
 | 
			
		||||
	"Spark-Max-32K",
 | 
			
		||||
	"Spark-4.0-Ultra",
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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/gorilla/websocket"
 | 
			
		||||
 | 
			
		||||
	"github.com/songquanpeng/one-api/common"
 | 
			
		||||
	"github.com/songquanpeng/one-api/common/helper"
 | 
			
		||||
	"github.com/songquanpeng/one-api/common/logger"
 | 
			
		||||
@@ -270,48 +271,3 @@ func xunfeiMakeRequest(textRequest model.GeneralOpenAIRequest, domain, authUrl,
 | 
			
		||||
 | 
			
		||||
	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",
 | 
			
		||||
}
 | 
			
		||||
@@ -49,5 +49,7 @@ const (
 | 
			
		||||
	XAI
 | 
			
		||||
	Replicate
 | 
			
		||||
	BaiduV2
 | 
			
		||||
	XunfeiV2
 | 
			
		||||
	AliBailian
 | 
			
		||||
	Dummy
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,8 @@ var ChannelBaseURLs = []string{
 | 
			
		||||
	"https://api.x.ai",                          // 45
 | 
			
		||||
	"https://api.replicate.com/v1/models/",      // 46
 | 
			
		||||
	"https://qianfan.baidubce.com",              // 47
 | 
			
		||||
	"https://spark-api-open.xf-yun.com",         // 48
 | 
			
		||||
	"https://dashscope.aliyuncs.com",            // 49
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,7 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
 | 
			
		||||
	textRequest.Model, _ = getMappedModelName(textRequest.Model, meta.ModelMapping)
 | 
			
		||||
	meta.ActualModelName = textRequest.Model
 | 
			
		||||
	// set system prompt if not empty
 | 
			
		||||
	systemPromptReset := setSystemPrompt(ctx, textRequest, meta.SystemPrompt)
 | 
			
		||||
	systemPromptReset := setSystemPrompt(ctx, textRequest, meta.ForcedSystemPrompt)
 | 
			
		||||
	// get model ratio & group ratio
 | 
			
		||||
	modelRatio := billingratio.GetModelRatio(textRequest.Model, meta.ChannelType)
 | 
			
		||||
	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) {
 | 
			
		||||
	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
 | 
			
		||||
		return c.Request.Body, nil
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,29 +30,29 @@ type Meta struct {
 | 
			
		||||
	// OriginModelName is the model name from the raw user request
 | 
			
		||||
	OriginModelName string
 | 
			
		||||
	// ActualModelName is the model name after mapping
 | 
			
		||||
	ActualModelName string
 | 
			
		||||
	RequestURLPath  string
 | 
			
		||||
	PromptTokens    int // only for DoResponse
 | 
			
		||||
	SystemPrompt    string
 | 
			
		||||
	StartTime       time.Time
 | 
			
		||||
	ActualModelName    string
 | 
			
		||||
	RequestURLPath     string
 | 
			
		||||
	PromptTokens       int // only for DoResponse
 | 
			
		||||
	ForcedSystemPrompt string
 | 
			
		||||
	StartTime          time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetByContext(c *gin.Context) *Meta {
 | 
			
		||||
	meta := Meta{
 | 
			
		||||
		Mode:            relaymode.GetByPath(c.Request.URL.Path),
 | 
			
		||||
		ChannelType:     c.GetInt(ctxkey.Channel),
 | 
			
		||||
		ChannelId:       c.GetInt(ctxkey.ChannelId),
 | 
			
		||||
		TokenId:         c.GetInt(ctxkey.TokenId),
 | 
			
		||||
		TokenName:       c.GetString(ctxkey.TokenName),
 | 
			
		||||
		UserId:          c.GetInt(ctxkey.Id),
 | 
			
		||||
		Group:           c.GetString(ctxkey.Group),
 | 
			
		||||
		ModelMapping:    c.GetStringMapString(ctxkey.ModelMapping),
 | 
			
		||||
		OriginModelName: c.GetString(ctxkey.RequestModel),
 | 
			
		||||
		BaseURL:         c.GetString(ctxkey.BaseURL),
 | 
			
		||||
		APIKey:          strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "),
 | 
			
		||||
		RequestURLPath:  c.Request.URL.String(),
 | 
			
		||||
		SystemPrompt:    c.GetString(ctxkey.SystemPrompt),
 | 
			
		||||
		StartTime:       time.Now(),
 | 
			
		||||
		Mode:               relaymode.GetByPath(c.Request.URL.Path),
 | 
			
		||||
		ChannelType:        c.GetInt(ctxkey.Channel),
 | 
			
		||||
		ChannelId:          c.GetInt(ctxkey.ChannelId),
 | 
			
		||||
		TokenId:            c.GetInt(ctxkey.TokenId),
 | 
			
		||||
		TokenName:          c.GetString(ctxkey.TokenName),
 | 
			
		||||
		UserId:             c.GetInt(ctxkey.Id),
 | 
			
		||||
		Group:              c.GetString(ctxkey.Group),
 | 
			
		||||
		ModelMapping:       c.GetStringMapString(ctxkey.ModelMapping),
 | 
			
		||||
		OriginModelName:    c.GetString(ctxkey.RequestModel),
 | 
			
		||||
		BaseURL:            c.GetString(ctxkey.BaseURL),
 | 
			
		||||
		APIKey:             strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "),
 | 
			
		||||
		RequestURLPath:     c.Request.URL.String(),
 | 
			
		||||
		ForcedSystemPrompt: c.GetString(ctxkey.SystemPrompt),
 | 
			
		||||
		StartTime:          time.Now(),
 | 
			
		||||
	}
 | 
			
		||||
	cfg, ok := c.Get(ctxkey.Config)
 | 
			
		||||
	if ok {
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ type GeneralOpenAIRequest struct {
 | 
			
		||||
	Messages            []Message       `json:"messages,omitempty"`
 | 
			
		||||
	Model               string          `json:"model,omitempty"`
 | 
			
		||||
	Store               *bool           `json:"store,omitempty"`
 | 
			
		||||
	ReasoningEffort     *string         `json:"reasoning_effort,omitempty"`
 | 
			
		||||
	Metadata            any             `json:"metadata,omitempty"`
 | 
			
		||||
	FrequencyPenalty    *float64        `json:"frequency_penalty,omitempty"`
 | 
			
		||||
	LogitBias           any             `json:"logit_bias,omitempty"`
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,14 @@ type Usage struct {
 | 
			
		||||
	PromptTokens     int `json:"prompt_tokens"`
 | 
			
		||||
	CompletionTokens int `json:"completion_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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -35,7 +35,7 @@ export const CHANNEL_OPTIONS = [
 | 
			
		||||
  { 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: 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' },
 | 
			
		||||
 
 | 
			
		||||
@@ -217,7 +217,7 @@ export const CHANNEL_OPTIONS = {
 | 
			
		||||
  },
 | 
			
		||||
  20: {
 | 
			
		||||
    key: 20,
 | 
			
		||||
    text: '代理:OpenRouter',
 | 
			
		||||
      text: 'OpenRouter',
 | 
			
		||||
    value: 20,
 | 
			
		||||
    color: 'success'
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,7 @@
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import {
 | 
			
		||||
  Button,
 | 
			
		||||
  Dropdown,
 | 
			
		||||
  Form,
 | 
			
		||||
  Input,
 | 
			
		||||
  Label,
 | 
			
		||||
  Message,
 | 
			
		||||
  Pagination,
 | 
			
		||||
  Popup,
 | 
			
		||||
  Table,
 | 
			
		||||
} from 'semantic-ui-react';
 | 
			
		||||
import { Link } from 'react-router-dom';
 | 
			
		||||
import React, {useEffect, useState} from 'react';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import {Button, Dropdown, Form, Input, Label, Message, Pagination, Popup, Table,} from 'semantic-ui-react';
 | 
			
		||||
import {Link} from 'react-router-dom';
 | 
			
		||||
import {
 | 
			
		||||
  API,
 | 
			
		||||
  loadChannelModels,
 | 
			
		||||
@@ -23,8 +13,8 @@ import {
 | 
			
		||||
  timestamp2string,
 | 
			
		||||
} from '../helpers';
 | 
			
		||||
 | 
			
		||||
import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../constants';
 | 
			
		||||
import { renderGroup, renderNumber } from '../helpers/render';
 | 
			
		||||
import {CHANNEL_OPTIONS, ITEMS_PER_PAGE} from '../constants';
 | 
			
		||||
import {renderGroup, renderNumber} from '../helpers/render';
 | 
			
		||||
 | 
			
		||||
function renderTimestamp(timestamp) {
 | 
			
		||||
  return <>{timestamp2string(timestamp)}</>;
 | 
			
		||||
@@ -67,6 +57,8 @@ function renderBalance(type, balance, t) {
 | 
			
		||||
      return <span>¥{balance.toFixed(2)}</span>;
 | 
			
		||||
    case 13: // AIGC2D
 | 
			
		||||
      return <span>{renderNumber(balance)}</span>;
 | 
			
		||||
    case 20: // OpenRouter
 | 
			
		||||
      return <span>${balance.toFixed(2)}</span>;
 | 
			
		||||
    case 36: // DeepSeek
 | 
			
		||||
      return <span>¥{balance.toFixed(2)}</span>;
 | 
			
		||||
    case 44: // SiliconFlow
 | 
			
		||||
@@ -93,30 +85,32 @@ const ChannelsTable = () => {
 | 
			
		||||
  const [showPrompt, setShowPrompt] = useState(shouldShowPrompt(promptID));
 | 
			
		||||
  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 res = await API.get(`/api/channel/?p=${startIdx}`);
 | 
			
		||||
    const { success, message, data } = res.data;
 | 
			
		||||
    const {success, message, data} = res.data;
 | 
			
		||||
    if (success) {
 | 
			
		||||
      let localChannels = data.map((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;
 | 
			
		||||
      });
 | 
			
		||||
      let localChannels = data.map(processChannelData);
 | 
			
		||||
      if (startIdx === 0) {
 | 
			
		||||
        setChannels(localChannels);
 | 
			
		||||
      } else {
 | 
			
		||||
@@ -301,7 +295,8 @@ const ChannelsTable = () => {
 | 
			
		||||
    const res = await API.get(`/api/channel/search?keyword=${searchKeyword}`);
 | 
			
		||||
    const { success, message, data } = res.data;
 | 
			
		||||
    if (success) {
 | 
			
		||||
      setChannels(data);
 | 
			
		||||
      let localChannels = data.map(processChannelData);
 | 
			
		||||
      setChannels(localChannels);
 | 
			
		||||
      setActivePage(1);
 | 
			
		||||
    } else {
 | 
			
		||||
      showError(message);
 | 
			
		||||
@@ -495,7 +490,6 @@ const ChannelsTable = () => {
 | 
			
		||||
              onClick={() => {
 | 
			
		||||
                sortChannel('balance');
 | 
			
		||||
              }}
 | 
			
		||||
              hidden={!showDetail}
 | 
			
		||||
            >
 | 
			
		||||
              {t('channel.table.balance')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
@@ -504,6 +498,7 @@ const ChannelsTable = () => {
 | 
			
		||||
              onClick={() => {
 | 
			
		||||
                sortChannel('priority');
 | 
			
		||||
              }}
 | 
			
		||||
              hidden={!showDetail}
 | 
			
		||||
            >
 | 
			
		||||
              {t('channel.table.priority')}
 | 
			
		||||
            </Table.HeaderCell>
 | 
			
		||||
@@ -543,7 +538,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                      basic
 | 
			
		||||
                    />
 | 
			
		||||
                  </Table.Cell>
 | 
			
		||||
                  <Table.Cell hidden={!showDetail}>
 | 
			
		||||
                  <Table.Cell>
 | 
			
		||||
                    <Popup
 | 
			
		||||
                      trigger={
 | 
			
		||||
                        <span
 | 
			
		||||
@@ -559,7 +554,7 @@ const ChannelsTable = () => {
 | 
			
		||||
                      basic
 | 
			
		||||
                    />
 | 
			
		||||
                  </Table.Cell>
 | 
			
		||||
                  <Table.Cell>
 | 
			
		||||
                  <Table.Cell hidden={!showDetail}>
 | 
			
		||||
                    <Popup
 | 
			
		||||
                      trigger={
 | 
			
		||||
                        <Input
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
export const CHANNEL_OPTIONS = [
 | 
			
		||||
  {key: 1, text: 'OpenAI', value: 1, color: 'green'},
 | 
			
		||||
  {key: 14, text: 'Anthropic Claude', value: 14, color: 'black'},
 | 
			
		||||
  {key: 33, text: 'AWS', value: 33, color: 'black'},
 | 
			
		||||
  {key: 3, text: 'Azure OpenAI', value: 3, color: 'olive'},
 | 
			
		||||
  {key: 11, text: 'Google PaLM2', value: 11, color: 'orange'},
 | 
			
		||||
  {key: 24, text: 'Google Gemini', value: 24, color: 'orange'},
 | 
			
		||||
  {key: 28, text: 'Mistral AI', value: 28, color: 'orange'},
 | 
			
		||||
  {key: 41, text: 'Novita', value: 41, color: 'purple'},
 | 
			
		||||
    {key: 1, text: 'OpenAI', value: 1, color: 'green'},
 | 
			
		||||
    {key: 14, text: 'Anthropic Claude', value: 14, color: 'black'},
 | 
			
		||||
    {key: 33, text: 'AWS', value: 33, color: 'black'},
 | 
			
		||||
    {key: 3, text: 'Azure OpenAI', value: 3, color: 'olive'},
 | 
			
		||||
    {key: 11, text: 'Google PaLM2', value: 11, color: 'orange'},
 | 
			
		||||
    {key: 24, text: 'Google Gemini', value: 24, color: 'orange'},
 | 
			
		||||
    {key: 28, text: 'Mistral AI', value: 28, color: 'orange'},
 | 
			
		||||
    {key: 41, text: 'Novita', value: 41, color: 'purple'},
 | 
			
		||||
  {
 | 
			
		||||
    key: 40,
 | 
			
		||||
    text: '字节火山引擎',
 | 
			
		||||
@@ -28,40 +28,60 @@ export const CHANNEL_OPTIONS = [
 | 
			
		||||
    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: 17, text: '阿里通义千问', value: 17, color: 'orange'},
 | 
			
		||||
  {key: 18, text: '讯飞星火认知', value: 18, color: 'blue'},
 | 
			
		||||
  {key: 16, text: '智谱 ChatGLM', value: 16, color: 'violet'},
 | 
			
		||||
  {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'},
 | 
			
		||||
    {
 | 
			
		||||
        key: 17,
 | 
			
		||||
        text: '阿里通义千问',
 | 
			
		||||
        value: 17,
 | 
			
		||||
        color: 'orange',
 | 
			
		||||
        tip: '如需使用阿里云百炼,请使用<strong>阿里云百炼</strong>渠道',
 | 
			
		||||
    },
 | 
			
		||||
    {key: 49, text: '阿里云百炼', value: 49, color: 'orange'},
 | 
			
		||||
  {
 | 
			
		||||
    key: 18,
 | 
			
		||||
    text: '讯飞星火认知',
 | 
			
		||||
    value: 18,
 | 
			
		||||
    color: 'blue',
 | 
			
		||||
    tip: '本渠道基于讯飞 WebSocket 版本 API,如需 HTTP 版本,请使用<strong>讯飞星火认知 V2</strong>渠道',
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    key: 48,
 | 
			
		||||
    text: '讯飞星火认知 V2',
 | 
			
		||||
    value: 48,
 | 
			
		||||
    color: 'blue',
 | 
			
		||||
    tip: 'HTTP 版本的讯飞接口,前往<a href="https://console.xfyun.cn/services/cbm" target="_blank">此处</a>获取 HTTP 服务接口认证密钥',
 | 
			
		||||
  },
 | 
			
		||||
    {key: 16, text: '智谱 ChatGLM', value: 16, color: 'violet'},
 | 
			
		||||
    {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'},
 | 
			
		||||
];
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { Card, Grid } from 'semantic-ui-react';
 | 
			
		||||
import React, {useEffect, useState} from 'react';
 | 
			
		||||
import {useTranslation} from 'react-i18next';
 | 
			
		||||
import {Card, Grid} from 'semantic-ui-react';
 | 
			
		||||
import {
 | 
			
		||||
  Bar,
 | 
			
		||||
  BarChart,
 | 
			
		||||
@@ -122,11 +122,11 @@ const Dashboard = () => {
 | 
			
		||||
        ? new Date(Math.min(...dates.map((d) => new Date(d))))
 | 
			
		||||
        : new Date();
 | 
			
		||||
 | 
			
		||||
    // 确保至少显示5天的数据
 | 
			
		||||
    const fiveDaysAgo = new Date();
 | 
			
		||||
    fiveDaysAgo.setDate(fiveDaysAgo.getDate() - 4); // -4是因为包含今天
 | 
			
		||||
    if (minDate > fiveDaysAgo) {
 | 
			
		||||
      minDate = fiveDaysAgo;
 | 
			
		||||
    // 确保至少显示7天的数据
 | 
			
		||||
    const sevenDaysAgo = new Date();
 | 
			
		||||
    sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 6); // -6是因为包含今天
 | 
			
		||||
    if (minDate > sevenDaysAgo) {
 | 
			
		||||
      minDate = sevenDaysAgo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 生成所有日期
 | 
			
		||||
@@ -164,11 +164,11 @@ const Dashboard = () => {
 | 
			
		||||
        ? new Date(Math.min(...dates.map((d) => new Date(d))))
 | 
			
		||||
        : new Date();
 | 
			
		||||
 | 
			
		||||
    // 确保至少显示5天的数据
 | 
			
		||||
    const fiveDaysAgo = new Date();
 | 
			
		||||
    fiveDaysAgo.setDate(fiveDaysAgo.getDate() - 4); // -4是因为包含今天
 | 
			
		||||
    if (minDate > fiveDaysAgo) {
 | 
			
		||||
      minDate = fiveDaysAgo;
 | 
			
		||||
    // 确保至少显示7天的数据
 | 
			
		||||
    const sevenDaysAgo = new Date();
 | 
			
		||||
    sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 6); // -6是因为包含今天
 | 
			
		||||
    if (minDate > sevenDaysAgo) {
 | 
			
		||||
      minDate = sevenDaysAgo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 生成所有日期
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user