From 511489db0950744bad18a2d2a26b2f972d366844 Mon Sep 17 00:00:00 2001 From: DD <1083962986@qq.com> Date: Mon, 8 Sep 2025 16:21:21 +0800 Subject: [PATCH] add submodel.ai --- common/api_type.go | 2 + constant/api_type.go | 1 + constant/channel.go | 2 + relay/channel/submodel/adaptor.go | 82 ++++++++++++++++++++++++++ relay/channel/submodel/constants.go | 16 +++++ relay/relay_adaptor.go | 3 + setting/ratio_setting/model_ratio.go | 13 ++++ web/src/constants/channel.constants.js | 5 ++ web/src/helpers/render.js | 2 + web/src/pages/Channel/EditTagModal.js | 3 + 10 files changed, 129 insertions(+) create mode 100644 relay/channel/submodel/adaptor.go create mode 100644 relay/channel/submodel/constants.go diff --git a/common/api_type.go b/common/api_type.go index f045866ac..6204451dc 100644 --- a/common/api_type.go +++ b/common/api_type.go @@ -65,6 +65,8 @@ func ChannelType2APIType(channelType int) (int, bool) { apiType = constant.APITypeCoze case constant.ChannelTypeJimeng: apiType = constant.APITypeJimeng + case constant.ChannelTypeSubmodel: + apiType = constant.APITypeSubmodel } if apiType == -1 { return constant.APITypeOpenAI, false diff --git a/constant/api_type.go b/constant/api_type.go index 6ba5f2574..0c7b1fdde 100644 --- a/constant/api_type.go +++ b/constant/api_type.go @@ -31,5 +31,6 @@ const ( APITypeXai APITypeCoze APITypeJimeng + APITypeSubmodel APITypeDummy // this one is only for count, do not add any channel after this ) diff --git a/constant/channel.go b/constant/channel.go index 224121e70..3d7158b11 100644 --- a/constant/channel.go +++ b/constant/channel.go @@ -49,6 +49,7 @@ const ( ChannelTypeCoze = 49 ChannelTypeKling = 50 ChannelTypeJimeng = 51 + ChannelTypeSubmodel = 52 ChannelTypeDummy // this one is only for count, do not add any channel after this ) @@ -106,4 +107,5 @@ var ChannelBaseURLs = []string{ "https://api.coze.cn", //49 "https://api.klingai.com", //50 "https://visual.volcengineapi.com", //51 + "https://llm.submodel.ai", //52 } diff --git a/relay/channel/submodel/adaptor.go b/relay/channel/submodel/adaptor.go new file mode 100644 index 000000000..371fb055c --- /dev/null +++ b/relay/channel/submodel/adaptor.go @@ -0,0 +1,82 @@ +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) ConvertClaudeRequest(*gin.Context, *relaycommon.RelayInfo, *dto.ClaudeRequest) (any, error) { + return nil, errors.New("not implemented") +} + +func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) { + return nil, errors.New("not implemented") +} + +func (a *Adaptor) ConvertImageRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.ImageRequest) (any, error) { + return nil, errors.New("not implemented") +} + +func (a *Adaptor) Init(info *relaycommon.RelayInfo) { +} + +func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) { + return relaycommon.GetFullRequestURL(info.BaseUrl, 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("not implemented") +} + +func (a *Adaptor) ConvertEmbeddingRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.EmbeddingRequest) (any, error) { + return nil, errors.New("not implemented") +} + +func (a *Adaptor) ConvertOpenAIResponsesRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.OpenAIResponsesRequest) (any, error) { + return nil, errors.New("not implemented") +} + +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 +} \ No newline at end of file diff --git a/relay/channel/submodel/constants.go b/relay/channel/submodel/constants.go new file mode 100644 index 000000000..962682bb9 --- /dev/null +++ b/relay/channel/submodel/constants.go @@ -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", +} + +var ChannelName = "submodel" \ No newline at end of file diff --git a/relay/relay_adaptor.go b/relay/relay_adaptor.go index 2ce12a872..946053a28 100644 --- a/relay/relay_adaptor.go +++ b/relay/relay_adaptor.go @@ -34,6 +34,7 @@ import ( "one-api/relay/channel/xunfei" "one-api/relay/channel/zhipu" "one-api/relay/channel/zhipu_4v" + "one-api/relay/channel/submodel" ) func GetAdaptor(apiType int) channel.Adaptor { @@ -96,6 +97,8 @@ func GetAdaptor(apiType int) channel.Adaptor { return &coze.Adaptor{} case constant.APITypeJimeng: return &jimeng.Adaptor{} + case constant.APITypeSubmodel: + return &submodel.Adaptor{} } return nil } diff --git a/setting/ratio_setting/model_ratio.go b/setting/ratio_setting/model_ratio.go index 8a1d6aaed..0bcb6ff58 100644 --- a/setting/ratio_setting/model_ratio.go +++ b/setting/ratio_setting/model_ratio.go @@ -223,6 +223,19 @@ var defaultModelRatio = map[string]float64{ "grok-vision-beta": 2.5, "grok-3-fast-beta": 2.5, "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{ diff --git a/web/src/constants/channel.constants.js b/web/src/constants/channel.constants.js index b145ea11f..6a4566a7d 100644 --- a/web/src/constants/channel.constants.js +++ b/web/src/constants/channel.constants.js @@ -135,6 +135,11 @@ export const CHANNEL_OPTIONS = [ color: 'blue', label: '即梦', }, + { + value: 52, + color: 'blue', + label: 'SubModel', + }, ]; export const MODEL_TABLE_PAGE_SIZE = 10; diff --git a/web/src/helpers/render.js b/web/src/helpers/render.js index 34ba78d7a..abf246b8c 100644 --- a/web/src/helpers/render.js +++ b/web/src/helpers/render.js @@ -398,6 +398,8 @@ export function getChannelIcon(channelType) { return ; case 21: // 知识库:AI Proxy case 44: // 嵌入模型:MokaAI M3E + case 52: // SubModel + return null; default: return null; // 未知类型或自定义渠道不显示图标 } diff --git a/web/src/pages/Channel/EditTagModal.js b/web/src/pages/Channel/EditTagModal.js index 433d4f092..35fc1646f 100644 --- a/web/src/pages/Channel/EditTagModal.js +++ b/web/src/pages/Channel/EditTagModal.js @@ -98,6 +98,9 @@ const EditTagModal = (props) => { case 36: localModels = ['suno_music', 'suno_lyrics']; break; + case 52: + localModels = ['NousResearch/Hermes-4-405B-FP8', 'Qwen/Qwen3-235B-A22B-Thinking-2507', 'Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8', '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: localModels = getChannelModels(value); break;