diff --git a/common/api_type.go b/common/api_type.go
index 39c1fe9a540..8e575b6a4a6 100644
--- a/common/api_type.go
+++ b/common/api_type.go
@@ -75,6 +75,8 @@ func ChannelType2APIType(channelType int) (int, bool) {
apiType = constant.APITypeReplicate
case constant.ChannelTypeCodex:
apiType = constant.APITypeCodex
+ case constant.ChannelTypeXiaomi:
+ apiType = constant.APITypeXiaomi
}
if apiType == -1 {
return constant.APITypeOpenAI, false
diff --git a/constant/api_type.go b/constant/api_type.go
index 536ebd2c719..b13425a2435 100644
--- a/constant/api_type.go
+++ b/constant/api_type.go
@@ -36,5 +36,6 @@ const (
APITypeMiniMax
APITypeReplicate
APITypeCodex
+ APITypeXiaomi
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 48502bedc52..eff38903f39 100644
--- a/constant/channel.go
+++ b/constant/channel.go
@@ -55,6 +55,7 @@ const (
ChannelTypeSora = 55
ChannelTypeReplicate = 56
ChannelTypeCodex = 57
+ ChannelTypeXiaomi = 58
ChannelTypeDummy // this one is only for count, do not add any channel after this
)
@@ -118,6 +119,7 @@ var ChannelBaseURLs = []string{
"https://api.openai.com", //55
"https://api.replicate.com", //56
"https://chatgpt.com", //57
+ "https://api.xiaomimimo.com", //58
}
var ChannelTypeNames = map[int]string{
@@ -175,6 +177,7 @@ var ChannelTypeNames = map[int]string{
ChannelTypeSora: "Sora",
ChannelTypeReplicate: "Replicate",
ChannelTypeCodex: "Codex",
+ ChannelTypeXiaomi: "Xiaomi",
}
func GetChannelTypeName(channelType int) string {
diff --git a/model/pricing_default.go b/model/pricing_default.go
index db64cafbb1e..17e4d38db95 100644
--- a/model/pricing_default.go
+++ b/model/pricing_default.go
@@ -19,6 +19,7 @@ var defaultVendorRules = map[string]string{
"glm-": "智谱",
"qwen": "阿里巴巴",
"deepseek": "DeepSeek",
+ "mimo": "小米",
"abab": "MiniMax",
"ernie": "百度",
"spark": "讯飞",
@@ -46,6 +47,7 @@ var defaultVendorIcons = map[string]string{
"智谱": "Zhipu.Color",
"阿里巴巴": "Qwen.Color",
"DeepSeek": "DeepSeek.Color",
+ "小米": "XiaomiMiMo",
"MiniMax": "Minimax.Color",
"百度": "Wenxin.Color",
"讯飞": "Spark.Color",
diff --git a/relay/channel/xiaomi/adaptor.go b/relay/channel/xiaomi/adaptor.go
new file mode 100644
index 00000000000..f2a9a5b7106
--- /dev/null
+++ b/relay/channel/xiaomi/adaptor.go
@@ -0,0 +1,75 @@
+package xiaomi
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/QuantumNous/new-api/dto"
+ "github.com/QuantumNous/new-api/relay/channel"
+ "github.com/QuantumNous/new-api/relay/channel/openai"
+ relaycommon "github.com/QuantumNous/new-api/relay/common"
+ relayconstant "github.com/QuantumNous/new-api/relay/constant"
+ "github.com/QuantumNous/new-api/types"
+ "github.com/gin-gonic/gin"
+)
+
+type Adaptor struct {
+ openai.Adaptor
+}
+
+func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
+ if info.RelayMode != relayconstant.RelayModeAudioSpeech {
+ return a.Adaptor.GetRequestURL(info)
+ }
+ return fmt.Sprintf("%s/v1/chat/completions", info.ChannelBaseUrl), nil
+}
+
+func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error {
+ if err := a.Adaptor.SetupRequestHeader(c, req, info); err != nil {
+ return err
+ }
+ req.Set("api-key", info.ApiKey)
+ return nil
+}
+
+func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) (any, error) {
+ if request == nil {
+ return nil, fmt.Errorf("request is nil")
+ }
+ return request, nil
+}
+
+func (a *Adaptor) ConvertAudioRequest(c *gin.Context, info *relaycommon.RelayInfo, request dto.AudioRequest) (io.Reader, error) {
+ if info.RelayMode != relayconstant.RelayModeAudioSpeech {
+ return a.Adaptor.ConvertAudioRequest(c, info, request)
+ }
+ return convertTTSRequest(c, request)
+}
+
+func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, requestBody io.Reader) (any, error) {
+ if info.RelayMode == relayconstant.RelayModeAudioTranscription ||
+ info.RelayMode == relayconstant.RelayModeAudioTranslation ||
+ info.RelayMode == relayconstant.RelayModeImagesEdits {
+ return channel.DoFormRequest(a, c, info, requestBody)
+ }
+ if info.RelayMode == relayconstant.RelayModeRealtime {
+ return channel.DoWssRequest(a, c, info, requestBody)
+ }
+ 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.RelayMode == relayconstant.RelayModeAudioSpeech {
+ return handleTTSResponse(c, resp, info)
+ }
+ return a.Adaptor.DoResponse(c, resp, info)
+}
+
+func (a *Adaptor) GetModelList() []string {
+ return ModelList
+}
+
+func (a *Adaptor) GetChannelName() string {
+ return ChannelName
+}
diff --git a/relay/channel/xiaomi/constants.go b/relay/channel/xiaomi/constants.go
new file mode 100644
index 00000000000..7568b292edb
--- /dev/null
+++ b/relay/channel/xiaomi/constants.go
@@ -0,0 +1,17 @@
+package xiaomi
+
+const (
+ ChannelName = "xiaomi"
+ contextKeyAudioFormat = "xiaomi_audio_format"
+ defaultMimoVoice = "mimo_default"
+)
+
+var ModelList = []string{
+ "mimo-v2-pro",
+ "mimo-v2-flash",
+ "mimo-v2-omni",
+ "mimo-v2-tts",
+ "mimo-v2.5-tts",
+ "mimo-v2.5-pro",
+ "mimo-v2.5",
+}
diff --git a/relay/channel/xiaomi/tts.go b/relay/channel/xiaomi/tts.go
new file mode 100644
index 00000000000..ebd4a8fc983
--- /dev/null
+++ b/relay/channel/xiaomi/tts.go
@@ -0,0 +1,140 @@
+package xiaomi
+
+import (
+ "bytes"
+ "encoding/base64"
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/QuantumNous/new-api/common"
+ "github.com/QuantumNous/new-api/dto"
+ relaycommon "github.com/QuantumNous/new-api/relay/common"
+ "github.com/QuantumNous/new-api/types"
+ "github.com/gin-gonic/gin"
+)
+
+type xiaomiTTSMessage struct {
+ Role string `json:"role"`
+ Content string `json:"content"`
+}
+
+type xiaomiTTSAudio struct {
+ Voice string `json:"voice"`
+ Format string `json:"format"`
+}
+
+type xiaomiTTSRequest struct {
+ Model string `json:"model"`
+ Messages []xiaomiTTSMessage `json:"messages"`
+ Audio xiaomiTTSAudio `json:"audio"`
+}
+
+type xiaomiTTSResponse struct {
+ Choices []struct {
+ Message struct {
+ Audio struct {
+ Data string `json:"data"`
+ } `json:"audio"`
+ } `json:"message"`
+ } `json:"choices"`
+ Usage dto.Usage `json:"usage"`
+}
+
+func convertTTSRequest(c *gin.Context, request dto.AudioRequest) (io.Reader, error) {
+ audioFormat := normalizeMimoAudioFormat(request.ResponseFormat)
+ c.Set(contextKeyAudioFormat, audioFormat)
+
+ voice := request.Voice
+ if voice == "" {
+ voice = defaultMimoVoice
+ }
+
+ messages := make([]xiaomiTTSMessage, 0, 2)
+ if request.Instructions != "" {
+ messages = append(messages, xiaomiTTSMessage{Role: "user", Content: request.Instructions})
+ }
+ messages = append(messages, xiaomiTTSMessage{Role: "assistant", Content: request.Input})
+
+ jsonData, err := common.Marshal(xiaomiTTSRequest{
+ Model: request.Model,
+ Messages: messages,
+ Audio: xiaomiTTSAudio{Voice: voice, Format: audioFormat},
+ })
+ if err != nil {
+ return nil, err
+ }
+ return bytes.NewReader(jsonData), nil
+}
+
+func normalizeMimoAudioFormat(format string) string {
+ switch format {
+ case "":
+ return "wav"
+ case "pcm":
+ return "pcm16"
+ default:
+ return format
+ }
+}
+
+func getTTSContentType(format string) string {
+ switch format {
+ case "mp3":
+ return "audio/mpeg"
+ case "pcm", "pcm16":
+ return "audio/pcm"
+ default:
+ return "audio/wav"
+ }
+}
+
+func handleTTSResponse(c *gin.Context, resp *http.Response, _ *relaycommon.RelayInfo) (any, *types.NewAPIError) {
+ if resp == nil || resp.Body == nil {
+ return nil, types.NewErrorWithStatusCode(
+ fmt.Errorf("invalid xiaomi TTS response"),
+ types.ErrorCodeBadResponse,
+ http.StatusInternalServerError,
+ )
+ }
+ defer resp.Body.Close()
+ audioFormat := c.GetString(contextKeyAudioFormat)
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, types.NewErrorWithStatusCode(
+ fmt.Errorf("read xiaomi TTS response: %w", err),
+ types.ErrorCodeReadResponseBodyFailed,
+ http.StatusInternalServerError,
+ )
+ }
+
+ var ttsResp xiaomiTTSResponse
+ if err := common.Unmarshal(body, &ttsResp); err != nil {
+ return nil, types.NewErrorWithStatusCode(
+ fmt.Errorf("unmarshal xiaomi TTS response: %w", err),
+ types.ErrorCodeBadResponseBody,
+ http.StatusBadGateway,
+ )
+ }
+
+ if len(ttsResp.Choices) == 0 || ttsResp.Choices[0].Message.Audio.Data == "" {
+ return nil, types.NewErrorWithStatusCode(
+ fmt.Errorf("xiaomi TTS response missing audio data"),
+ types.ErrorCodeBadResponse,
+ http.StatusBadGateway,
+ )
+ }
+
+ audioData, err := base64.StdEncoding.DecodeString(ttsResp.Choices[0].Message.Audio.Data)
+ if err != nil {
+ return nil, types.NewErrorWithStatusCode(
+ fmt.Errorf("decode xiaomi TTS audio payload: %w", err),
+ types.ErrorCodeBadResponse,
+ http.StatusBadGateway,
+ )
+ }
+
+ c.Data(http.StatusOK, getTTSContentType(audioFormat), audioData)
+
+ return &ttsResp.Usage, nil
+}
diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go
index 64d4d4eedfa..200da406510 100644
--- a/relay/common/relay_info.go
+++ b/relay/common/relay_info.go
@@ -328,6 +328,7 @@ var streamSupportedChannels = map[int]bool{
constant.ChannelTypeMoonshot: true,
constant.ChannelTypeMiniMax: true,
constant.ChannelTypeSiliconFlow: true,
+ constant.ChannelTypeXiaomi: true,
}
func GenRelayInfoWs(c *gin.Context, ws *websocket.Conn) *RelayInfo {
diff --git a/relay/relay_adaptor.go b/relay/relay_adaptor.go
index 3139c9a2dd4..7e837dc74e2 100644
--- a/relay/relay_adaptor.go
+++ b/relay/relay_adaptor.go
@@ -12,6 +12,7 @@ import (
"github.com/QuantumNous/new-api/relay/channel/claude"
"github.com/QuantumNous/new-api/relay/channel/cloudflare"
"github.com/QuantumNous/new-api/relay/channel/codex"
+ "github.com/QuantumNous/new-api/relay/channel/xiaomi"
"github.com/QuantumNous/new-api/relay/channel/cohere"
"github.com/QuantumNous/new-api/relay/channel/coze"
"github.com/QuantumNous/new-api/relay/channel/deepseek"
@@ -120,6 +121,8 @@ func GetAdaptor(apiType int) channel.Adaptor {
return &replicate.Adaptor{}
case constant.APITypeCodex:
return &codex.Adaptor{}
+ case constant.APITypeXiaomi:
+ return &xiaomi.Adaptor{}
}
return nil
}
diff --git a/setting/ratio_setting/model_ratio.go b/setting/ratio_setting/model_ratio.go
index 80702ee42ad..3c3ee31796f 100644
--- a/setting/ratio_setting/model_ratio.go
+++ b/setting/ratio_setting/model_ratio.go
@@ -249,6 +249,11 @@ var defaultModelRatio = map[string]float64{
"deepseek-chat": 0.27 / 2,
"deepseek-coder": 0.27 / 2,
"deepseek-reasoner": 0.55 / 2, // 0.55 / 1k tokens
+ "mimo-v2.5-pro": 0.5,
+ "mimo-v2-pro": 0.5,
+ "mimo-v2.5": 0.2,
+ "mimo-v2-flash": 0.05,
+ "mimo-v2-omni": 0.2,
// Perplexity online 模型对搜索额外收费,有需要应自行调整,此处不计入搜索费用
"llama-3-sonar-small-32k-chat": 0.2 / 1000 * USD,
"llama-3-sonar-small-32k-online": 0.2 / 1000 * USD,
diff --git a/web/classic/src/constants/channel.constants.js b/web/classic/src/constants/channel.constants.js
index 9fa78779de8..a52a84d85e5 100644
--- a/web/classic/src/constants/channel.constants.js
+++ b/web/classic/src/constants/channel.constants.js
@@ -58,6 +58,7 @@ export const CHANNEL_OPTIONS = [
},
{ value: 39, color: 'grey', label: 'Cloudflare' },
{ value: 43, color: 'blue', label: 'DeepSeek' },
+ { value: 58, color: 'orange', label: 'Xiaomi MiMo' },
{
value: 15,
color: 'blue',
diff --git a/web/classic/src/helpers/render.jsx b/web/classic/src/helpers/render.jsx
index 46c95b23683..dec01a8561a 100644
--- a/web/classic/src/helpers/render.jsx
+++ b/web/classic/src/helpers/render.jsx
@@ -50,17 +50,6 @@ import {
XAI,
Ollama,
Doubao,
- Suno,
- Xinference,
- OpenRouter,
- Dify,
- Coze,
- SiliconCloud,
- FastGPT,
- Kling,
- Jimeng,
- Perplexity,
- Replicate,
} from '@lobehub/icons';
import {
@@ -323,6 +312,51 @@ export const getModelCategories = (() => {
};
})();
+const channelTypeIconMap = {
+ 1: 'OpenAI', // OpenAI
+ 3: 'OpenAI', // Azure OpenAI
+ 57: 'OpenAI', // Codex
+ 2: 'Midjourney', // Midjourney Proxy
+ 5: 'Midjourney', // Midjourney Proxy Plus
+ 36: 'Suno', // Suno API
+ 4: 'Ollama', // Ollama
+ 14: 'Claude.Color', // Anthropic Claude
+ 33: 'Claude.Color', // AWS Claude
+ 41: 'Gemini.Color', // Vertex AI
+ 34: 'Cohere.Color', // Cohere
+ 39: 'Cloudflare.Color', // Cloudflare
+ 43: 'DeepSeek.Color', // DeepSeek
+ 58: 'XiaomiMiMo', // Xiaomi MiMo
+ 15: 'Wenxin.Color', // 百度文心千帆
+ 46: 'Wenxin.Color', // 百度文心千帆V2
+ 17: 'Qwen.Color', // 阿里通义千问
+ 18: 'Spark.Color', // 讯飞星火认知
+ 16: 'Zhipu.Color', // 智谱 ChatGLM
+ 26: 'Zhipu.Color', // 智谱 GLM-4V
+ 24: 'Gemini.Color', // Google Gemini
+ 11: 'Gemini.Color', // Google PaLM2
+ 47: 'Xinference.Color', // Xinference
+ 25: 'Moonshot', // Moonshot
+ 27: 'Perplexity.Color', // Perplexity
+ 20: 'OpenRouter', // OpenRouter
+ 19: 'Ai360.Color', // 360 智脑
+ 23: 'Hunyuan.Color', // 腾讯混元
+ 31: 'Yi.Color', // 零一万物
+ 35: 'Minimax.Color', // MiniMax
+ 37: 'Dify.Color', // Dify
+ 38: 'Jina', // Jina
+ 40: 'SiliconCloud.Color', // SiliconCloud
+ 42: 'Mistral.Color', // Mistral AI
+ 45: 'Doubao.Color', // 字节火山方舟、豆包通用
+ 48: 'XAI', // xAI
+ 49: 'Coze', // Coze
+ 50: 'Kling.Color', // 可灵 Kling
+ 51: 'Jimeng.Color', // 即梦 Jimeng
+ 54: 'Doubao.Color', // 豆包视频 Doubao Video
+ 56: 'Replicate', // Replicate
+ 22: 'FastGPT.Color', // 知识库:FastGPT
+};
+
/**
* 根据渠道类型返回对应的厂商图标
* @param {number} channelType - 渠道类型值
@@ -330,89 +364,11 @@ export const getModelCategories = (() => {
*/
export function getChannelIcon(channelType) {
const iconSize = 14;
-
- switch (channelType) {
- case 1: // OpenAI
- case 3: // Azure OpenAI
- case 57: // Codex
- return ;
- case 2: // Midjourney Proxy
- case 5: // Midjourney Proxy Plus
- return ;
- case 36: // Suno API
- return ;
- case 4: // Ollama
- return ;
- case 14: // Anthropic Claude
- case 33: // AWS Claude
- return ;
- case 41: // Vertex AI
- return ;
- case 34: // Cohere
- return ;
- case 39: // Cloudflare
- return ;
- case 43: // DeepSeek
- return ;
- case 15: // 百度文心千帆
- case 46: // 百度文心千帆V2
- return ;
- case 17: // 阿里通义千问
- return ;
- case 18: // 讯飞星火认知
- return ;
- case 16: // 智谱 ChatGLM
- case 26: // 智谱 GLM-4V
- return ;
- case 24: // Google Gemini
- case 11: // Google PaLM2
- return ;
- case 47: // Xinference
- return ;
- case 25: // Moonshot
- return ;
- case 27: // Perplexity
- return ;
- case 20: // OpenRouter
- return ;
- case 19: // 360 智脑
- return ;
- case 23: // 腾讯混元
- return ;
- case 31: // 零一万物
- return ;
- case 35: // MiniMax
- return ;
- case 37: // Dify
- return ;
- case 38: // Jina
- return ;
- case 40: // SiliconCloud
- return ;
- case 42: // Mistral AI
- return ;
- case 45: // 字节火山方舟、豆包通用
- return ;
- case 48: // xAI
- return ;
- case 49: // Coze
- return ;
- case 50: // 可灵 Kling
- return ;
- case 51: // 即梦 Jimeng
- return ;
- case 54: // 豆包视频 Doubao Video
- return ;
- case 56: // Replicate
- return ;
- case 8: // 自定义渠道
- case 22: // 知识库:FastGPT
- return ;
- case 21: // 知识库:AI Proxy
- case 44: // 嵌入模型:MokaAI M3E
- default:
- return null; // 未知类型或自定义渠道不显示图标
+ const iconName = channelTypeIconMap[channelType];
+ if (!iconName) {
+ return null;
}
+ return getLobeHubIcon(iconName, iconSize);
}
/**
@@ -429,7 +385,7 @@ export function getLobeHubIcon(iconName, size = 14) {
if (typeof iconName === 'string') iconName = iconName.trim();
// 如果没有图标名称,返回 Avatar
if (!iconName) {
- return ?;
+ return ?;
}
// 解析组件路径与点号链式属性
@@ -441,59 +397,59 @@ export function getLobeHubIcon(iconName, size = 14) {
let propStartIndex = 1;
if (BaseIcon && segments.length > 1 && BaseIcon[segments[1]]) {
- IconComponent = BaseIcon[segments[1]];
- propStartIndex = 2;
+ IconComponent = BaseIcon[segments[1]];
+ propStartIndex = 2;
} else {
- IconComponent = LobeIcons[baseKey];
- propStartIndex = 1;
+ IconComponent = LobeIcons[baseKey];
+ propStartIndex = 1;
}
// 失败兜底
if (
- !IconComponent ||
- (typeof IconComponent !== 'function' && typeof IconComponent !== 'object')
+ !IconComponent ||
+ (typeof IconComponent !== 'function' && typeof IconComponent !== 'object')
) {
- const firstLetter = String(iconName).charAt(0).toUpperCase();
- return {firstLetter};
+ const firstLetter = String(iconName).charAt(0).toUpperCase();
+ return {firstLetter};
}
// 解析点号链式属性,形如:key={...}、key='...'、key="..."、key=123、key、key=true/false
const props = {};
const parseValue = (raw) => {
- if (raw == null) return true;
- let v = String(raw).trim();
- // 去除一层花括号包裹
- if (v.startsWith('{') && v.endsWith('}')) {
- v = v.slice(1, -1).trim();
- }
- // 去除引号
- if (
- (v.startsWith('"') && v.endsWith('"')) ||
- (v.startsWith("'") && v.endsWith("'"))
- ) {
- return v.slice(1, -1);
- }
- // 布尔
- if (v === 'true') return true;
- if (v === 'false') return false;
- // 数字
- if (/^-?\d+(?:\.\d+)?$/.test(v)) return Number(v);
- // 其他原样返回字符串
- return v;
- };
+ if (raw == null) return true;
+ let v = String(raw).trim();
+ // 去除一层花括号包裹
+ if (v.startsWith('{') && v.endsWith('}')) {
+ v = v.slice(1, -1).trim();
+ }
+ // 去除引号
+ if (
+ (v.startsWith('"') && v.endsWith('"')) ||
+ (v.startsWith("'") && v.endsWith("'"))
+ ) {
+ return v.slice(1, -1);
+ }
+ // 布尔
+ if (v === 'true') return true;
+ if (v === 'false') return false;
+ // 数字
+ if (/^-?\d+(?:\.\d+)?$/.test(v)) return Number(v);
+ // 其他原样返回字符串
+ return v;
+};
for (let i = propStartIndex; i < segments.length; i++) {
- const seg = segments[i];
- if (!seg) continue;
- const eqIdx = seg.indexOf('=');
- if (eqIdx === -1) {
- props[seg.trim()] = true;
- continue;
- }
- const key = seg.slice(0, eqIdx).trim();
- const valRaw = seg.slice(eqIdx + 1).trim();
- props[key] = parseValue(valRaw);
+ const seg = segments[i];
+ if (!seg) continue;
+ const eqIdx = seg.indexOf('=');
+ if (eqIdx === -1) {
+ props[seg.trim()] = true;
+ continue;
+ }
+ const key = seg.slice(0, eqIdx).trim();
+ const valRaw = seg.slice(eqIdx + 1).trim();
+ props[key] = parseValue(valRaw);
}
// 兼容第二参数 size,若字符串中未显式指定 size,则使用函数入参
diff --git a/web/default/src/features/channels/constants.ts b/web/default/src/features/channels/constants.ts
index 21e15161508..a1a82f30f18 100644
--- a/web/default/src/features/channels/constants.ts
+++ b/web/default/src/features/channels/constants.ts
@@ -58,6 +58,7 @@ export const CHANNEL_TYPES = {
55: 'Sora',
56: 'Replicate',
57: 'Codex',
+ 58: 'Xiaomi',
} as const
const CHANNEL_TYPE_DISPLAY_ORDER: number[] = [
diff --git a/web/default/src/features/channels/lib/channel-utils.ts b/web/default/src/features/channels/lib/channel-utils.ts
index 9d96b62e0f9..9d189c034e1 100644
--- a/web/default/src/features/channels/lib/channel-utils.ts
+++ b/web/default/src/features/channels/lib/channel-utils.ts
@@ -79,6 +79,7 @@ export function getChannelTypeIcon(type: number): string {
50: 'Kling', // Kling
51: 'Jimeng', // Jimeng
52: 'Vidu', // Vidu
+ 58: 'XiaomiMiMo', // Xiaomi
36: 'Suno', // SunoAPI
55: 'OpenAI', // Sora
54: 'Doubao', // DoubaoVideo