From f3ad53e949926b2d1ebd9de43eb18fb3f2c4a9cb Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Wed, 2 Jul 2025 17:12:30 +0800 Subject: [PATCH] feat: add supports for selecting provider and models in webchat --- astrbot/dashboard/routes/config.py | 24 ++ astrbot/dashboard/routes/multi_user_chat.py | 0 dashboard/src/views/ChatPage.vue | 314 +++++++++++++++++++- 3 files changed, 324 insertions(+), 14 deletions(-) delete mode 100644 astrbot/dashboard/routes/multi_user_chat.py diff --git a/astrbot/dashboard/routes/config.py b/astrbot/dashboard/routes/config.py index c225c762a..b55f0b21c 100644 --- a/astrbot/dashboard/routes/config.py +++ b/astrbot/dashboard/routes/config.py @@ -9,6 +9,7 @@ from astrbot.core.platform.register import platform_registry from astrbot.core.provider.register import provider_registry from astrbot.core.star.star import star_registry from astrbot.core import logger +from astrbot.core.provider import Provider import asyncio @@ -168,6 +169,7 @@ class ConfigRoute(Route): "/config/llmtools": ("GET", self.get_llm_tools), "/config/provider/check_status": ("GET", self.check_all_providers_status), "/config/provider/list": ("GET", self.get_provider_config_list), + "/config/provider/model_list": ("GET", self.get_provider_model_list), "/config/provider/get_session_seperate": ( "GET", lambda: Response() @@ -319,6 +321,28 @@ class ConfigRoute(Route): provider_list.append(provider) return Response().ok(provider_list).__dict__ + async def get_provider_model_list(self): + """获取指定提供商的模型列表""" + provider_id = request.args.get("provider_id", None) + if not provider_id: + return Response().error("缺少参数 provider_id").__dict__ + + prov_mgr = self.core_lifecycle.provider_manager + provider: Provider | None = prov_mgr.inst_map.get(provider_id, None) + if not provider: + return Response().error(f"未找到 ID 为 {provider_id} 的提供商").__dict__ + + try: + models = await provider.get_models() + ret = { + "models": models, + "provider_id": provider_id, + } + return Response().ok(ret).__dict__ + except Exception as e: + logger.error(traceback.format_exc()) + return Response().error(str(e)).__dict__ + async def post_astrbot_configs(self): post_configs = await request.json try: diff --git a/astrbot/dashboard/routes/multi_user_chat.py b/astrbot/dashboard/routes/multi_user_chat.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/dashboard/src/views/ChatPage.vue b/dashboard/src/views/ChatPage.vue index 8c68f083e..1d849c8cb 100644 --- a/dashboard/src/views/ChatPage.vue +++ b/dashboard/src/views/ChatPage.vue @@ -50,10 +50,12 @@ @@ -78,7 +80,7 @@

{{ getCurrentConversation.title || tm('conversation.newConversation') - }}

+ }}
{{ formatDate(getCurrentConversation.updated_at) }}
@@ -100,8 +102,8 @@ @@ -188,14 +190,29 @@ -
- - +
+
+ + + + {{ selectedProviderId }} / {{ selectedModelName }} + + + 选择模型 + + +
+
+ + +
+
@@ -236,6 +253,89 @@ + + + + + + 选择提供商和模型 + + +
+ +
+
+

提供商

+
+ + + {{ provider.id }} + {{ provider.api_base }} + + +
+ +
暂无可用提供商
+
+
+ + +
+
+

模型

+ + +
+ + + {{ model }} + {{ model.description }} + + +
+ +
请先选择提供商
+
+
+ +
该提供商暂无可用模型
+
+
+
+
+ + + 取消 + + 确认选择 + + +
+
@@ -1709,4 +1917,82 @@ export default { flex-shrink: 0; /* 防止header被压缩 */ } + +/* 提供商和模型选择对话框样式 */ +.provider-model-container { + display: flex; + height: 500px; + border: 1px solid var(--v-theme-border); + border-radius: 8px; + overflow: hidden; +} + +.provider-list-panel, +.model-list-panel { + flex: 1; + display: flex; + flex-direction: column; + background-color: var(--v-theme-surface); +} + +.provider-list-panel { + border-right: 1px solid var(--v-theme-border); +} + +.panel-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px; + border-bottom: 1px solid var(--v-theme-border); + background-color: var(--v-theme-containerBg); +} + +.panel-header h4 { + margin: 0; + font-size: 16px; + font-weight: 500; + color: var(--v-theme-primaryText); +} + +.provider-list, +.model-list { + flex: 1; + overflow-y: auto; + padding: 8px; +} + +.provider-item, +.model-item { + margin-bottom: 4px; + border-radius: 8px !important; + transition: all 0.2s ease; + cursor: pointer; +} + +.provider-item:hover, +.model-item:hover { + background-color: rgba(103, 58, 183, 0.05); +} + +.provider-item.v-list-item--active, +.model-item.v-list-item--active { + background-color: rgba(103, 58, 183, 0.1); + color: var(--v-theme-secondary); +} + +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 200px; + opacity: 0.6; + gap: 12px; +} + +.empty-text { + font-size: 14px; + color: var(--v-theme-secondaryText); +} \ No newline at end of file