Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions astrbot/core/config/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,31 @@ class ChatProviderTemplate(TypedDict):
"custom_headers": {"User-Agent": "claude-code/0.1.0"},
"anth_thinking_config": {"type": "", "budget": 0, "effort": ""},
},
"Xiaomi": {
"id": "xiaomi",
"provider": "xiaomi",
"type": "xiaomi_chat_completion",
"provider_type": "chat_completion",
"enable": True,
"key": [],
"api_base": "https://api.xiaomimimo.com/v1",
"timeout": 120,
"proxy": "",
"custom_headers": {},
},
"Xiaomi Token Plan": {
"id": "xiaomi-token-plan",
"provider": "xiaomi-token-plan",
"type": "xiaomi_token_plan",
"provider_type": "chat_completion",
"enable": True,
"key": [],
"api_base": "https://token-plan-cn.xiaomimimo.com/anthropic",
"timeout": 120,
"proxy": "",
"custom_headers": {"User-Agent": "claude-code/0.1.0"},
"anth_thinking_config": {"type": "", "budget": 0, "effort": ""},
},
"xAI": {
"id": "xai",
"provider": "xai",
Expand Down
6 changes: 6 additions & 0 deletions astrbot/core/provider/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,12 @@ def dynamic_import_provider(self, type: str) -> None:
from .sources.minimax_token_plan_source import (
ProviderMiniMaxTokenPlan as ProviderMiniMaxTokenPlan,
)
case "xiaomi_chat_completion":
from .sources.xiaomi_source import ProviderXiaomi as ProviderXiaomi
case "xiaomi_token_plan":
from .sources.xiaomi_token_plan_source import (
ProviderXiaomiTokenPlan as ProviderXiaomiTokenPlan,
)
case "zhipu_chat_completion":
from .sources.zhipu_source import ProviderZhipu as ProviderZhipu
case "groq_chat_completion":
Expand Down
70 changes: 70 additions & 0 deletions astrbot/core/provider/sources/xiaomi_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from astrbot import logger
from astrbot.core.provider.sources.openai_source import ProviderOpenAIOfficial

from ..register import register_provider_adapter

XIAOMI_MODELS = [
"mimo-v2.5-pro",
"mimo-v2.5",
"mimo-v2-pro",
"mimo-v2-omni",
"mimo-v2-flash",
]


@register_provider_adapter(
"xiaomi_chat_completion",
"Xiaomi API 提供商适配器 (OpenAI 兼容)",
default_config_tmpl={
"id": "xiaomi",
"provider": "xiaomi",
"type": "xiaomi_chat_completion",
"provider_type": "chat_completion",
"enable": True,
"key": [],
"api_base": "https://api.xiaomimimo.com/v1",
"timeout": 120,
"proxy": "",
"custom_headers": {},
"custom_extra_body": {"temperature": 1, "top_p": 0.95},
},
)
class ProviderXiaomi(ProviderOpenAIOfficial):
"""Xiaomi provider using OpenAI-compatible API.

Supports both standard API and multimodal capabilities.
See https://platform.xiaomimimo.com/docs/api/chat/openai-api
"""

def __init__(
self,
provider_config,
provider_settings,
) -> None:
# Ensure api_base is set to Xiaomi endpoint if not provided
if not provider_config.get("api_base"):
provider_config["api_base"] = "https://api.xiaomimimo.com/v1"

super().__init__(
provider_config,
provider_settings,
)

configured_model = provider_config.get("model", "mimo-v2.5")
self.set_model(configured_model)

logger.debug(f"Xiaomi provider initialized with model: {self.get_model()}")

async def get_models(self) -> list[str]:
"""Return the list of known Xiaomi models.

Tries to fetch from API first, falls back to hard-coded list if unavailable.
"""
try:
models = await super().get_models()
if models:
return models
except Exception as e:
logger.debug(f"Failed to fetch models from Xiaomi API: {e}")

return XIAOMI_MODELS.copy()
76 changes: 76 additions & 0 deletions astrbot/core/provider/sources/xiaomi_token_plan_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from astrbot import logger
from astrbot.core.provider.sources.anthropic_source import ProviderAnthropic

from ..register import register_provider_adapter

XIAOMI_TOKEN_PLAN_MODELS = [
"mimo-v2.5-pro",
"mimo-v2.5",
"mimo-v2-pro",
"mimo-v2-omni",
"mimo-v2-flash",
]
Comment on lines +6 to +12
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Avoid duplication of Xiaomi model lists across providers to reduce drift risk.

The Xiaomi Token Plan and standard Xiaomi provider each maintain their own, nearly identical model lists. To avoid them drifting out of sync when models change, centralize this list (e.g., in a shared module/constant) and reuse it in both providers, or derive one from the other so updates happen in only one place.

Suggested implementation:

from astrbot import logger
from astrbot.core.provider.sources.anthropic_source import ProviderAnthropic

from ..register import register_provider_adapter
from .xiaomi_models import XIAOMI_MODELS

XIAOMI_TOKEN_PLAN_MODELS = XIAOMI_MODELS

To fully avoid duplication and drift, you should also:

  1. Create a new module astrbot/core/provider/sources/xiaomi_models.py that defines the single source of truth, for example:
    • XIAOMI_MODELS = ["mimo-v2.5-pro", "mimo-v2.5", "mimo-v2-pro", "mimo-v2-omni", "mimo-v2-flash"]
  2. Update the standard Xiaomi provider file (likely astrbot/core/provider/sources/xiaomi_source.py) to:
    • Remove its local Xiaomi model list.
    • Import and use XIAOMI_MODELS from .xiaomi_models instead.
  3. If the token plan provider needs a subset/superset in the future, derive XIAOMI_TOKEN_PLAN_MODELS from XIAOMI_MODELS (e.g., filtering) rather than hardcoding a separate list.



@register_provider_adapter(
"xiaomi_token_plan",
"Xiaomi Token Plan 提供商适配器",
default_config_tmpl={
"id": "xiaomi-token-plan",
"provider": "xiaomi-token-plan",
"type": "xiaomi_token_plan",
"provider_type": "chat_completion",
"enable": True,
"key": [],
"api_base": "https://token-plan-cn.xiaomimimo.com/anthropic",
"timeout": 120,
"proxy": "",
"custom_headers": {"User-Agent": "claude-code/0.1.0"},
"custom_extra_body": {"temperature": 1, "top_p": 0.95},
"anth_thinking_config": {"type": "", "budget": 0, "effort": ""},
},
)
class ProviderXiaomiTokenPlan(ProviderAnthropic):
"""Xiaomi Token Plan provider.

The Token Plan API uses Anthropic-compatible endpoint with Bearer token auth.
See https://platform.xiaomimimo.com/docs/tokenplan/quick-access
"""

def __init__(
self,
provider_config,
provider_settings,
) -> None:
# Keep api_base fixed; Token Plan users do not need to configure it.
provider_config["api_base"] = "https://token-plan-cn.xiaomimimo.com/anthropic"

# Xiaomi Token Plan requires the Authorization: Bearer <token> header.
keys = provider_config.get("key", [])
actual_key = keys[0] if isinstance(keys, list) and keys else keys
if actual_key:
provider_config.setdefault("custom_headers", {})["Authorization"] = (
f"Bearer {actual_key}"
)

super().__init__(
provider_config,
provider_settings,
)

configured_model = provider_config.get("model", "mimo-v2.5")
if configured_model not in XIAOMI_TOKEN_PLAN_MODELS:
logger.warning(
f"Configured model {configured_model!r} is not in the known "
f"Token Plan model list "
f"({', '.join(XIAOMI_TOKEN_PLAN_MODELS)}). "
f"The model may still work if your plan supports it. "
f"If you encounter errors, please check your plan's "
f"model availability."
)

self.set_model(configured_model)

async def get_models(self) -> list[str]:
"""Return the hard-coded known model list because Token Plan cannot fetch it dynamically."""
return XIAOMI_TOKEN_PLAN_MODELS.copy()
4 changes: 3 additions & 1 deletion dashboard/src/utils/providerUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export function getProviderIcon(type) {
'fishaudio': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/fishaudio.svg',
'minimax': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/minimax.svg',
'minimax-token-plan': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/minimax.svg',
'mimo': 'https://platform.xiaomimimo.com/favicon.874c9507.png',
'mimo': 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/xiaomi.svg',
'xiaomi': 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/xiaomi.svg',
'xiaomi-token-plan': 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/xiaomi.svg',
'302ai': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@1.53.0/icons/ai302-color.svg',
'microsoft': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/microsoft.svg',
'vllm': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/vllm.svg',
Expand Down