@@ -67,6 +67,8 @@ func ChannelType2APIType(channelType int) (int, bool) {
|
|||||||
apiType = constant.APITypeJimeng
|
apiType = constant.APITypeJimeng
|
||||||
case constant.ChannelTypeMoonshot:
|
case constant.ChannelTypeMoonshot:
|
||||||
apiType = constant.APITypeMoonshot
|
apiType = constant.APITypeMoonshot
|
||||||
|
case constant.ChannelTypeSubmodel:
|
||||||
|
apiType = constant.APITypeSubmodel
|
||||||
}
|
}
|
||||||
if apiType == -1 {
|
if apiType == -1 {
|
||||||
return constant.APITypeOpenAI, false
|
return constant.APITypeOpenAI, false
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const (
|
|||||||
APITypeXai
|
APITypeXai
|
||||||
APITypeCoze
|
APITypeCoze
|
||||||
APITypeJimeng
|
APITypeJimeng
|
||||||
APITypeMoonshot // this one is only for count, do not add any channel after this
|
APITypeMoonshot
|
||||||
APITypeDummy // this one is only for count, do not add any channel after this
|
APITypeSubmodel
|
||||||
|
APITypeDummy // this one is only for count, do not add any channel after this
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -50,8 +50,10 @@ const (
|
|||||||
ChannelTypeKling = 50
|
ChannelTypeKling = 50
|
||||||
ChannelTypeJimeng = 51
|
ChannelTypeJimeng = 51
|
||||||
ChannelTypeVidu = 52
|
ChannelTypeVidu = 52
|
||||||
|
ChannelTypeSubmodel = 53
|
||||||
ChannelTypeDummy // this one is only for count, do not add any channel after this
|
ChannelTypeDummy // this one is only for count, do not add any channel after this
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ChannelBaseURLs = []string{
|
var ChannelBaseURLs = []string{
|
||||||
@@ -108,4 +110,5 @@ var ChannelBaseURLs = []string{
|
|||||||
"https://api.klingai.com", //50
|
"https://api.klingai.com", //50
|
||||||
"https://visual.volcengineapi.com", //51
|
"https://visual.volcengineapi.com", //51
|
||||||
"https://api.vidu.cn", //52
|
"https://api.vidu.cn", //52
|
||||||
|
"https://llm.submodel.ai", //53
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-4
@@ -196,10 +196,11 @@ type ClaudeRequest struct {
|
|||||||
TopP float64 `json:"top_p,omitempty"`
|
TopP float64 `json:"top_p,omitempty"`
|
||||||
TopK int `json:"top_k,omitempty"`
|
TopK int `json:"top_k,omitempty"`
|
||||||
//ClaudeMetadata `json:"metadata,omitempty"`
|
//ClaudeMetadata `json:"metadata,omitempty"`
|
||||||
Stream bool `json:"stream,omitempty"`
|
Stream bool `json:"stream,omitempty"`
|
||||||
Tools any `json:"tools,omitempty"`
|
Tools any `json:"tools,omitempty"`
|
||||||
ToolChoice any `json:"tool_choice,omitempty"`
|
ContextManagement json.RawMessage `json:"context_management,omitempty"`
|
||||||
Thinking *Thinking `json:"thinking,omitempty"`
|
ToolChoice any `json:"tool_choice,omitempty"`
|
||||||
|
Thinking *Thinking `json:"thinking,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ClaudeRequest) GetTokenCountMeta() *types.TokenCountMeta {
|
func (c *ClaudeRequest) GetTokenCountMeta() *types.TokenCountMeta {
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
|
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
|
||||||
|
anthropicBeta := c.Request.Header.Get("anthropic-beta")
|
||||||
|
if anthropicBeta != "" {
|
||||||
|
req.Set("anthropic-beta", anthropicBeta)
|
||||||
|
}
|
||||||
model_setting.GetClaudeSettings().WriteHeaders(info.OriginModelName, req)
|
model_setting.GetClaudeSettings().WriteHeaders(info.OriginModelName, req)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ var awsModelIDMap = map[string]string{
|
|||||||
"claude-sonnet-4-20250514": "anthropic.claude-sonnet-4-20250514-v1:0",
|
"claude-sonnet-4-20250514": "anthropic.claude-sonnet-4-20250514-v1:0",
|
||||||
"claude-opus-4-20250514": "anthropic.claude-opus-4-20250514-v1:0",
|
"claude-opus-4-20250514": "anthropic.claude-opus-4-20250514-v1:0",
|
||||||
"claude-opus-4-1-20250805": "anthropic.claude-opus-4-1-20250805-v1:0",
|
"claude-opus-4-1-20250805": "anthropic.claude-opus-4-1-20250805-v1:0",
|
||||||
|
"claude-sonnet-4-5-20250929": "anthropic.claude-sonnet-4-5-20250929-v1:0",
|
||||||
// Nova models
|
// Nova models
|
||||||
"nova-micro-v1:0": "amazon.nova-micro-v1:0",
|
"nova-micro-v1:0": "amazon.nova-micro-v1:0",
|
||||||
"nova-lite-v1:0": "amazon.nova-lite-v1:0",
|
"nova-lite-v1:0": "amazon.nova-lite-v1:0",
|
||||||
@@ -69,6 +70,11 @@ var awsModelCanCrossRegionMap = map[string]map[string]bool{
|
|||||||
"anthropic.claude-opus-4-1-20250805-v1:0": {
|
"anthropic.claude-opus-4-1-20250805-v1:0": {
|
||||||
"us": true,
|
"us": true,
|
||||||
},
|
},
|
||||||
|
"anthropic.claude-sonnet-4-5-20250929-v1:0": {
|
||||||
|
"us": true,
|
||||||
|
"ap": true,
|
||||||
|
"eu": true,
|
||||||
|
},
|
||||||
// Nova models - all support three major regions
|
// Nova models - all support three major regions
|
||||||
"amazon.nova-micro-v1:0": {
|
"amazon.nova-micro-v1:0": {
|
||||||
"us": true,
|
"us": true,
|
||||||
|
|||||||
@@ -52,11 +52,16 @@ func (a *Adaptor) Init(info *relaycommon.RelayInfo) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
|
func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
|
||||||
|
baseURL := ""
|
||||||
if a.RequestMode == RequestModeMessage {
|
if a.RequestMode == RequestModeMessage {
|
||||||
return fmt.Sprintf("%s/v1/messages", info.ChannelBaseUrl), nil
|
baseURL = fmt.Sprintf("%s/v1/messages", info.ChannelBaseUrl)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%s/v1/complete", info.ChannelBaseUrl), nil
|
baseURL = fmt.Sprintf("%s/v1/complete", info.ChannelBaseUrl)
|
||||||
}
|
}
|
||||||
|
if info.IsClaudeBetaQuery {
|
||||||
|
baseURL = baseURL + "?beta=true"
|
||||||
|
}
|
||||||
|
return baseURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
|
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
|
||||||
@@ -67,6 +72,10 @@ func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *rel
|
|||||||
anthropicVersion = "2023-06-01"
|
anthropicVersion = "2023-06-01"
|
||||||
}
|
}
|
||||||
req.Set("anthropic-version", anthropicVersion)
|
req.Set("anthropic-version", anthropicVersion)
|
||||||
|
anthropicBeta := c.Request.Header.Get("anthropic-beta")
|
||||||
|
if anthropicBeta != "" {
|
||||||
|
req.Set("anthropic-beta", anthropicBeta)
|
||||||
|
}
|
||||||
model_setting.GetClaudeSettings().WriteHeaders(info.OriginModelName, req)
|
model_setting.GetClaudeSettings().WriteHeaders(info.OriginModelName, req)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ var ModelList = []string{
|
|||||||
"claude-opus-4-20250514-thinking",
|
"claude-opus-4-20250514-thinking",
|
||||||
"claude-opus-4-1-20250805",
|
"claude-opus-4-1-20250805",
|
||||||
"claude-opus-4-1-20250805-thinking",
|
"claude-opus-4-1-20250805-thinking",
|
||||||
|
"claude-sonnet-4-5-20250929",
|
||||||
|
"claude-sonnet-4-5-20250929-thinking",
|
||||||
}
|
}
|
||||||
|
|
||||||
var ChannelName = "claude"
|
var ChannelName = "claude"
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package submodel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"one-api/dto"
|
||||||
|
"one-api/relay/channel"
|
||||||
|
"one-api/relay/channel/openai"
|
||||||
|
relaycommon "one-api/relay/common"
|
||||||
|
"one-api/types"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Adaptor struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) ConvertGeminiRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeminiChatRequest) (any, error) {
|
||||||
|
return nil, errors.New("submodel channel: endpoint not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) ConvertClaudeRequest(*gin.Context, *relaycommon.RelayInfo, *dto.ClaudeRequest) (any, error) {
|
||||||
|
return nil, errors.New("submodel channel: endpoint not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) {
|
||||||
|
return nil, errors.New("submodel channel: endpoint not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (any, error) {
|
||||||
|
return nil, errors.New("submodel channel: endpoint not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) Init(info *relaycommon.RelayInfo) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
|
||||||
|
return relaycommon.GetFullRequestURL(info.ChannelBaseUrl, info.RequestURLPath, info.ChannelType), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
|
||||||
|
channel.SetupApiRequestHeader(info, c, req)
|
||||||
|
req.Set("Authorization", "Bearer "+info.ApiKey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) (any, error) {
|
||||||
|
if request == nil {
|
||||||
|
return nil, errors.New("request is nil")
|
||||||
|
}
|
||||||
|
return request, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) ConvertRerankRequest(c *gin.Context, relayMode int, request dto.RerankRequest) (any, error) {
|
||||||
|
return nil, errors.New("submodel channel: endpoint not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) ConvertEmbeddingRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.EmbeddingRequest) (any, error) {
|
||||||
|
return nil, errors.New("submodel channel: endpoint not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.OpenAIResponsesRequest) (any, error) {
|
||||||
|
return nil, errors.New("submodel channel: endpoint not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, requestBody io.Reader) (any, error) {
|
||||||
|
return channel.DoApiRequest(a, c, info, requestBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (usage any, err *types.NewAPIError) {
|
||||||
|
if info.IsStream {
|
||||||
|
usage, err = openai.OaiStreamHandler(c, info, resp)
|
||||||
|
} else {
|
||||||
|
usage, err = openai.OpenaiHandler(c, info, resp)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) GetModelList() []string {
|
||||||
|
return ModelList
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adaptor) GetChannelName() string {
|
||||||
|
return ChannelName
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package submodel
|
||||||
|
|
||||||
|
var ModelList = []string{
|
||||||
|
"NousResearch/Hermes-4-405B-FP8",
|
||||||
|
"Qwen/Qwen3-235B-A22B-Thinking-2507",
|
||||||
|
"Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8",
|
||||||
|
"Qwen/Qwen3-235B-A22B-Instruct-2507",
|
||||||
|
"zai-org/GLM-4.5-FP8",
|
||||||
|
"openai/gpt-oss-120b",
|
||||||
|
"deepseek-ai/DeepSeek-R1-0528",
|
||||||
|
"deepseek-ai/DeepSeek-R1",
|
||||||
|
"deepseek-ai/DeepSeek-V3-0324",
|
||||||
|
"deepseek-ai/DeepSeek-V3.1",
|
||||||
|
}
|
||||||
|
|
||||||
|
const ChannelName = "submodel"
|
||||||
@@ -37,6 +37,7 @@ var claudeModelMap = map[string]string{
|
|||||||
"claude-sonnet-4-20250514": "claude-sonnet-4@20250514",
|
"claude-sonnet-4-20250514": "claude-sonnet-4@20250514",
|
||||||
"claude-opus-4-20250514": "claude-opus-4@20250514",
|
"claude-opus-4-20250514": "claude-opus-4@20250514",
|
||||||
"claude-opus-4-1-20250805": "claude-opus-4-1@20250805",
|
"claude-opus-4-1-20250805": "claude-opus-4-1@20250805",
|
||||||
|
"claude-sonnet-4-5-20250929": "claude-sonnet-4-5@20250929",
|
||||||
}
|
}
|
||||||
|
|
||||||
const anthropicVersion = "vertex-2023-10-16"
|
const anthropicVersion = "vertex-2023-10-16"
|
||||||
|
|||||||
@@ -105,7 +105,8 @@ type RelayInfo struct {
|
|||||||
UserQuota int
|
UserQuota int
|
||||||
RelayFormat types.RelayFormat
|
RelayFormat types.RelayFormat
|
||||||
SendResponseCount int
|
SendResponseCount int
|
||||||
FinalPreConsumedQuota int // 最终预消耗的配额
|
FinalPreConsumedQuota int // 最终预消耗的配额
|
||||||
|
IsClaudeBetaQuery bool // /v1/messages?beta=true
|
||||||
|
|
||||||
PriceData types.PriceData
|
PriceData types.PriceData
|
||||||
|
|
||||||
@@ -279,6 +280,9 @@ func GenRelayInfoClaude(c *gin.Context, request dto.Request) *RelayInfo {
|
|||||||
info.ClaudeConvertInfo = &ClaudeConvertInfo{
|
info.ClaudeConvertInfo = &ClaudeConvertInfo{
|
||||||
LastMessagesType: LastMessageTypeNone,
|
LastMessagesType: LastMessageTypeNone,
|
||||||
}
|
}
|
||||||
|
if c.Query("beta") == "true" {
|
||||||
|
info.IsClaudeBetaQuery = true
|
||||||
|
}
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import (
|
|||||||
"one-api/relay/channel/zhipu"
|
"one-api/relay/channel/zhipu"
|
||||||
"one-api/relay/channel/zhipu_4v"
|
"one-api/relay/channel/zhipu_4v"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"one-api/relay/channel/submodel"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -103,6 +103,8 @@ func GetAdaptor(apiType int) channel.Adaptor {
|
|||||||
return &jimeng.Adaptor{}
|
return &jimeng.Adaptor{}
|
||||||
case constant.APITypeMoonshot:
|
case constant.APITypeMoonshot:
|
||||||
return &moonshot.Adaptor{} // Moonshot uses Claude API
|
return &moonshot.Adaptor{} // Moonshot uses Claude API
|
||||||
|
case constant.APITypeSubmodel:
|
||||||
|
return &submodel.Adaptor{}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ var defaultCacheRatio = map[string]float64{
|
|||||||
"claude-opus-4-20250514-thinking": 0.1,
|
"claude-opus-4-20250514-thinking": 0.1,
|
||||||
"claude-opus-4-1-20250805": 0.1,
|
"claude-opus-4-1-20250805": 0.1,
|
||||||
"claude-opus-4-1-20250805-thinking": 0.1,
|
"claude-opus-4-1-20250805-thinking": 0.1,
|
||||||
|
"claude-sonnet-4-5-20250929": 0.1,
|
||||||
|
"claude-sonnet-4-5-20250929-thinking": 0.1,
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultCreateCacheRatio = map[string]float64{
|
var defaultCreateCacheRatio = map[string]float64{
|
||||||
@@ -69,6 +71,8 @@ var defaultCreateCacheRatio = map[string]float64{
|
|||||||
"claude-opus-4-20250514-thinking": 1.25,
|
"claude-opus-4-20250514-thinking": 1.25,
|
||||||
"claude-opus-4-1-20250805": 1.25,
|
"claude-opus-4-1-20250805": 1.25,
|
||||||
"claude-opus-4-1-20250805-thinking": 1.25,
|
"claude-opus-4-1-20250805-thinking": 1.25,
|
||||||
|
"claude-sonnet-4-5-20250929": 1.25,
|
||||||
|
"claude-sonnet-4-5-20250929-thinking": 1.25,
|
||||||
}
|
}
|
||||||
|
|
||||||
//var defaultCreateCacheRatio = map[string]float64{}
|
//var defaultCreateCacheRatio = map[string]float64{}
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ var defaultModelRatio = map[string]float64{
|
|||||||
"claude-3-7-sonnet-20250219": 1.5,
|
"claude-3-7-sonnet-20250219": 1.5,
|
||||||
"claude-3-7-sonnet-20250219-thinking": 1.5,
|
"claude-3-7-sonnet-20250219-thinking": 1.5,
|
||||||
"claude-sonnet-4-20250514": 1.5,
|
"claude-sonnet-4-20250514": 1.5,
|
||||||
|
"claude-sonnet-4-5-20250929": 1.5,
|
||||||
"claude-3-opus-20240229": 7.5, // $15 / 1M tokens
|
"claude-3-opus-20240229": 7.5, // $15 / 1M tokens
|
||||||
"claude-opus-4-20250514": 7.5,
|
"claude-opus-4-20250514": 7.5,
|
||||||
"claude-opus-4-1-20250805": 7.5,
|
"claude-opus-4-1-20250805": 7.5,
|
||||||
@@ -251,6 +252,17 @@ var defaultModelRatio = map[string]float64{
|
|||||||
"grok-vision-beta": 2.5,
|
"grok-vision-beta": 2.5,
|
||||||
"grok-3-fast-beta": 2.5,
|
"grok-3-fast-beta": 2.5,
|
||||||
"grok-3-mini-fast-beta": 0.3,
|
"grok-3-mini-fast-beta": 0.3,
|
||||||
|
// submodel
|
||||||
|
"NousResearch/Hermes-4-405B-FP8": 0.8,
|
||||||
|
"Qwen/Qwen3-235B-A22B-Thinking-2507": 0.6,
|
||||||
|
"Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8": 0.8,
|
||||||
|
"Qwen/Qwen3-235B-A22B-Instruct-2507": 0.3,
|
||||||
|
"zai-org/GLM-4.5-FP8": 0.8,
|
||||||
|
"openai/gpt-oss-120b": 0.5,
|
||||||
|
"deepseek-ai/DeepSeek-R1-0528": 0.8,
|
||||||
|
"deepseek-ai/DeepSeek-R1": 0.8,
|
||||||
|
"deepseek-ai/DeepSeek-V3-0324": 0.8,
|
||||||
|
"deepseek-ai/DeepSeek-V3.1": 0.8,
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultModelPrice = map[string]float64{
|
var defaultModelPrice = map[string]float64{
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const SiderBar = ({ onNavigate = () => {} }) => {
|
|||||||
loading: sidebarLoading,
|
loading: sidebarLoading,
|
||||||
} = useSidebar();
|
} = useSidebar();
|
||||||
|
|
||||||
const showSkeleton = useMinimumLoadingTime(sidebarLoading);
|
const showSkeleton = useMinimumLoadingTime(sidebarLoading, 200);
|
||||||
|
|
||||||
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
||||||
const [chatItems, setChatItems] = useState([]);
|
const [chatItems, setChatItems] = useState([]);
|
||||||
|
|||||||
@@ -118,6 +118,9 @@ const EditTagModal = (props) => {
|
|||||||
case 36:
|
case 36:
|
||||||
localModels = ['suno_music', 'suno_lyrics'];
|
localModels = ['suno_music', 'suno_lyrics'];
|
||||||
break;
|
break;
|
||||||
|
case 53:
|
||||||
|
localModels = ['NousResearch/Hermes-4-405B-FP8', 'Qwen/Qwen3-235B-A22B-Thinking-2507', 'Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8','Qwen/Qwen3-235B-A22B-Instruct-2507', 'zai-org/GLM-4.5-FP8', 'openai/gpt-oss-120b', 'deepseek-ai/DeepSeek-R1-0528', 'deepseek-ai/DeepSeek-R1', 'deepseek-ai/DeepSeek-V3-0324', 'deepseek-ai/DeepSeek-V3.1'];
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
localModels = getChannelModels(value);
|
localModels = getChannelModels(value);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -159,6 +159,11 @@ export const CHANNEL_OPTIONS = [
|
|||||||
color: 'purple',
|
color: 'purple',
|
||||||
label: 'Vidu',
|
label: 'Vidu',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 53,
|
||||||
|
color: 'blue',
|
||||||
|
label: 'SubModel',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const MODEL_TABLE_PAGE_SIZE = 10;
|
export const MODEL_TABLE_PAGE_SIZE = 10;
|
||||||
|
|||||||
+68
-68
@@ -1200,25 +1200,25 @@ export function renderModelPrice(
|
|||||||
const extraServices = [
|
const extraServices = [
|
||||||
webSearch && webSearchCallCount > 0
|
webSearch && webSearchCallCount > 0
|
||||||
? i18next.t(
|
? i18next.t(
|
||||||
' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
|
' + Web搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
|
||||||
{
|
{
|
||||||
count: webSearchCallCount,
|
count: webSearchCallCount,
|
||||||
price: webSearchPrice,
|
price: webSearchPrice,
|
||||||
ratio: groupRatio,
|
ratio: groupRatio,
|
||||||
ratioType: ratioLabel,
|
ratioType: ratioLabel,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
fileSearch && fileSearchCallCount > 0
|
fileSearch && fileSearchCallCount > 0
|
||||||
? i18next.t(
|
? i18next.t(
|
||||||
' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
|
' + 文件搜索 {{count}}次 / 1K 次 * ${{price}} * {{ratioType}} {{ratio}}',
|
||||||
{
|
{
|
||||||
count: fileSearchCallCount,
|
count: fileSearchCallCount,
|
||||||
price: fileSearchPrice,
|
price: fileSearchPrice,
|
||||||
ratio: groupRatio,
|
ratio: groupRatio,
|
||||||
ratioType: ratioLabel,
|
ratioType: ratioLabel,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: '',
|
: '',
|
||||||
imageGenerationCall && imageGenerationCallPrice > 0
|
imageGenerationCall && imageGenerationCallPrice > 0
|
||||||
? i18next.t(
|
? i18next.t(
|
||||||
@@ -1398,10 +1398,10 @@ export function renderAudioModelPrice(
|
|||||||
let audioPrice =
|
let audioPrice =
|
||||||
(audioInputTokens / 1000000) * inputRatioPrice * audioRatio * groupRatio +
|
(audioInputTokens / 1000000) * inputRatioPrice * audioRatio * groupRatio +
|
||||||
(audioCompletionTokens / 1000000) *
|
(audioCompletionTokens / 1000000) *
|
||||||
inputRatioPrice *
|
inputRatioPrice *
|
||||||
audioRatio *
|
audioRatio *
|
||||||
audioCompletionRatio *
|
audioCompletionRatio *
|
||||||
groupRatio;
|
groupRatio;
|
||||||
let price = textPrice + audioPrice;
|
let price = textPrice + audioPrice;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -1457,27 +1457,27 @@ export function renderAudioModelPrice(
|
|||||||
<p>
|
<p>
|
||||||
{cacheTokens > 0
|
{cacheTokens > 0
|
||||||
? i18next.t(
|
? i18next.t(
|
||||||
'文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
|
'文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
|
||||||
{
|
{
|
||||||
nonCacheInput: inputTokens - cacheTokens,
|
nonCacheInput: inputTokens - cacheTokens,
|
||||||
cacheInput: cacheTokens,
|
cacheInput: cacheTokens,
|
||||||
cachePrice: inputRatioPrice * cacheRatio,
|
cachePrice: inputRatioPrice * cacheRatio,
|
||||||
price: inputRatioPrice,
|
price: inputRatioPrice,
|
||||||
completion: completionTokens,
|
completion: completionTokens,
|
||||||
compPrice: completionRatioPrice,
|
compPrice: completionRatioPrice,
|
||||||
total: textPrice.toFixed(6),
|
total: textPrice.toFixed(6),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: i18next.t(
|
: i18next.t(
|
||||||
'文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
|
'文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
|
||||||
{
|
{
|
||||||
input: inputTokens,
|
input: inputTokens,
|
||||||
price: inputRatioPrice,
|
price: inputRatioPrice,
|
||||||
completion: completionTokens,
|
completion: completionTokens,
|
||||||
compPrice: completionRatioPrice,
|
compPrice: completionRatioPrice,
|
||||||
total: textPrice.toFixed(6),
|
total: textPrice.toFixed(6),
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{i18next.t(
|
{i18next.t(
|
||||||
@@ -1617,35 +1617,35 @@ export function renderClaudeModelPrice(
|
|||||||
<p>
|
<p>
|
||||||
{cacheTokens > 0 || cacheCreationTokens > 0
|
{cacheTokens > 0 || cacheCreationTokens > 0
|
||||||
? i18next.t(
|
? i18next.t(
|
||||||
'提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
|
'提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
|
||||||
{
|
{
|
||||||
nonCacheInput: nonCachedTokens,
|
nonCacheInput: nonCachedTokens,
|
||||||
cacheInput: cacheTokens,
|
cacheInput: cacheTokens,
|
||||||
cacheRatio: cacheRatio,
|
cacheRatio: cacheRatio,
|
||||||
cacheCreationInput: cacheCreationTokens,
|
cacheCreationInput: cacheCreationTokens,
|
||||||
cacheCreationRatio: cacheCreationRatio,
|
cacheCreationRatio: cacheCreationRatio,
|
||||||
cachePrice: cacheRatioPrice,
|
cachePrice: cacheRatioPrice,
|
||||||
cacheCreationPrice: cacheCreationRatioPrice,
|
cacheCreationPrice: cacheCreationRatioPrice,
|
||||||
price: inputRatioPrice,
|
price: inputRatioPrice,
|
||||||
completion: completionTokens,
|
completion: completionTokens,
|
||||||
compPrice: completionRatioPrice,
|
compPrice: completionRatioPrice,
|
||||||
ratio: groupRatio,
|
ratio: groupRatio,
|
||||||
ratioType: ratioLabel,
|
ratioType: ratioLabel,
|
||||||
total: price.toFixed(6),
|
total: price.toFixed(6),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: i18next.t(
|
: i18next.t(
|
||||||
'提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
|
'提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * {{ratioType}} {{ratio}} = ${{total}}',
|
||||||
{
|
{
|
||||||
input: inputTokens,
|
input: inputTokens,
|
||||||
price: inputRatioPrice,
|
price: inputRatioPrice,
|
||||||
completion: completionTokens,
|
completion: completionTokens,
|
||||||
compPrice: completionRatioPrice,
|
compPrice: completionRatioPrice,
|
||||||
ratio: groupRatio,
|
ratio: groupRatio,
|
||||||
ratioType: ratioLabel,
|
ratioType: ratioLabel,
|
||||||
total: price.toFixed(6),
|
total: price.toFixed(6),
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p>{i18next.t('仅供参考,以实际扣费为准')}</p>
|
<p>{i18next.t('仅供参考,以实际扣费为准')}</p>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const loading = statusState?.status === undefined;
|
const loading = statusState?.status === undefined;
|
||||||
const isLoading = useMinimumLoadingTime(loading);
|
const isLoading = useMinimumLoadingTime(loading, 200);
|
||||||
|
|
||||||
const systemName = getSystemName();
|
const systemName = getSystemName();
|
||||||
const logo = getLogo();
|
const logo = getLogo();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
For commercial licensing, please contact support@quantumnous.com
|
For commercial licensing, please contact support@quantumnous.com
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useEffect, useMemo, useContext } from 'react';
|
import { useState, useEffect, useMemo, useContext, useRef } from 'react';
|
||||||
import { StatusContext } from '../../context/Status';
|
import { StatusContext } from '../../context/Status';
|
||||||
import { API } from '../../helpers';
|
import { API } from '../../helpers';
|
||||||
|
|
||||||
@@ -29,6 +29,13 @@ export const useSidebar = () => {
|
|||||||
const [statusState] = useContext(StatusContext);
|
const [statusState] = useContext(StatusContext);
|
||||||
const [userConfig, setUserConfig] = useState(null);
|
const [userConfig, setUserConfig] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const instanceIdRef = useRef(null);
|
||||||
|
const hasLoadedOnceRef = useRef(false);
|
||||||
|
|
||||||
|
if (!instanceIdRef.current) {
|
||||||
|
const randomPart = Math.random().toString(16).slice(2);
|
||||||
|
instanceIdRef.current = `sidebar-${Date.now()}-${randomPart}`;
|
||||||
|
}
|
||||||
|
|
||||||
// 默认配置
|
// 默认配置
|
||||||
const defaultAdminConfig = {
|
const defaultAdminConfig = {
|
||||||
@@ -74,9 +81,17 @@ export const useSidebar = () => {
|
|||||||
}, [statusState?.status?.SidebarModulesAdmin]);
|
}, [statusState?.status?.SidebarModulesAdmin]);
|
||||||
|
|
||||||
// 加载用户配置的通用方法
|
// 加载用户配置的通用方法
|
||||||
const loadUserConfig = async () => {
|
const loadUserConfig = async ({ withLoading } = {}) => {
|
||||||
|
const shouldShowLoader =
|
||||||
|
typeof withLoading === 'boolean'
|
||||||
|
? withLoading
|
||||||
|
: !hasLoadedOnceRef.current;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
if (shouldShowLoader) {
|
||||||
|
setLoading(true);
|
||||||
|
}
|
||||||
|
|
||||||
const res = await API.get('/api/user/self');
|
const res = await API.get('/api/user/self');
|
||||||
if (res.data.success && res.data.data.sidebar_modules) {
|
if (res.data.success && res.data.data.sidebar_modules) {
|
||||||
let config;
|
let config;
|
||||||
@@ -122,18 +137,25 @@ export const useSidebar = () => {
|
|||||||
});
|
});
|
||||||
setUserConfig(defaultUserConfig);
|
setUserConfig(defaultUserConfig);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
if (shouldShowLoader) {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
hasLoadedOnceRef.current = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 刷新用户配置的方法(供外部调用)
|
// 刷新用户配置的方法(供外部调用)
|
||||||
const refreshUserConfig = async () => {
|
const refreshUserConfig = async () => {
|
||||||
if (Object.keys(adminConfig).length > 0) {
|
if (Object.keys(adminConfig).length > 0) {
|
||||||
await loadUserConfig();
|
await loadUserConfig({ withLoading: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 触发全局刷新事件,通知所有useSidebar实例更新
|
// 触发全局刷新事件,通知所有useSidebar实例更新
|
||||||
sidebarEventTarget.dispatchEvent(new CustomEvent(SIDEBAR_REFRESH_EVENT));
|
sidebarEventTarget.dispatchEvent(
|
||||||
|
new CustomEvent(SIDEBAR_REFRESH_EVENT, {
|
||||||
|
detail: { sourceId: instanceIdRef.current, skipLoader: true },
|
||||||
|
}),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载用户配置
|
// 加载用户配置
|
||||||
@@ -146,9 +168,15 @@ export const useSidebar = () => {
|
|||||||
|
|
||||||
// 监听全局刷新事件
|
// 监听全局刷新事件
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleRefresh = () => {
|
const handleRefresh = (event) => {
|
||||||
|
if (event?.detail?.sourceId === instanceIdRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(adminConfig).length > 0) {
|
if (Object.keys(adminConfig).length > 0) {
|
||||||
loadUserConfig();
|
loadUserConfig({
|
||||||
|
withLoading: event?.detail?.skipLoader ? false : undefined,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1404,6 +1404,7 @@
|
|||||||
"Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Claude thinking adaptation BudgetTokens = MaxTokens * BudgetTokens percentage",
|
"Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Claude thinking adaptation BudgetTokens = MaxTokens * BudgetTokens percentage",
|
||||||
"思考适配 BudgetTokens 百分比": "Thinking adaptation BudgetTokens percentage",
|
"思考适配 BudgetTokens 百分比": "Thinking adaptation BudgetTokens percentage",
|
||||||
"0.1-1之间的小数": "Decimal between 0.1 and 1",
|
"0.1-1之间的小数": "Decimal between 0.1 and 1",
|
||||||
|
"0.1以上的小数": "Decimal above 0.1",
|
||||||
"模型相关设置": "Model related settings",
|
"模型相关设置": "Model related settings",
|
||||||
"收起侧边栏": "Collapse sidebar",
|
"收起侧边栏": "Collapse sidebar",
|
||||||
"展开侧边栏": "Expand sidebar",
|
"展开侧边栏": "Expand sidebar",
|
||||||
|
|||||||
@@ -1404,6 +1404,7 @@
|
|||||||
"Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Adaptation de la pensée Claude BudgetTokens = MaxTokens * BudgetTokens pourcentage",
|
"Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Adaptation de la pensée Claude BudgetTokens = MaxTokens * BudgetTokens pourcentage",
|
||||||
"思考适配 BudgetTokens 百分比": "Adaptation de la pensée BudgetTokens pourcentage",
|
"思考适配 BudgetTokens 百分比": "Adaptation de la pensée BudgetTokens pourcentage",
|
||||||
"0.1-1之间的小数": "Décimal entre 0,1 et 1",
|
"0.1-1之间的小数": "Décimal entre 0,1 et 1",
|
||||||
|
"0.1以上的小数": "Décimal supérieur à 0,1",
|
||||||
"模型相关设置": "Paramètres liés au modèle",
|
"模型相关设置": "Paramètres liés au modèle",
|
||||||
"收起侧边栏": "Réduire la barre latérale",
|
"收起侧边栏": "Réduire la barre latérale",
|
||||||
"展开侧边栏": "Développer la barre latérale",
|
"展开侧边栏": "Développer la barre latérale",
|
||||||
|
|||||||
@@ -202,9 +202,8 @@ export default function SettingClaudeModel(props) {
|
|||||||
label={t('思考适配 BudgetTokens 百分比')}
|
label={t('思考适配 BudgetTokens 百分比')}
|
||||||
field={'claude.thinking_adapter_budget_tokens_percentage'}
|
field={'claude.thinking_adapter_budget_tokens_percentage'}
|
||||||
initValue={''}
|
initValue={''}
|
||||||
extraText={t('0.1-1之间的小数')}
|
extraText={t('0.1以上的小数')}
|
||||||
min={0.1}
|
min={0.1}
|
||||||
max={1}
|
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
setInputs({
|
setInputs({
|
||||||
...inputs,
|
...inputs,
|
||||||
|
|||||||
Reference in New Issue
Block a user