AstrBot 4.11.4__py3-none-any.whl → 4.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- astrbot/cli/__init__.py +1 -1
- astrbot/core/agent/runners/tool_loop_agent_runner.py +10 -8
- astrbot/core/config/default.py +66 -2
- astrbot/core/db/__init__.py +84 -2
- astrbot/core/db/po.py +65 -0
- astrbot/core/db/sqlite.py +225 -4
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +103 -49
- astrbot/core/pipeline/process_stage/utils.py +40 -0
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +2 -0
- astrbot/core/platform/sources/telegram/tg_adapter.py +2 -0
- astrbot/core/platform/sources/webchat/webchat_adapter.py +3 -2
- astrbot/core/platform/sources/webchat/webchat_event.py +17 -4
- astrbot/core/provider/sources/anthropic_source.py +44 -0
- astrbot/core/sandbox/booters/base.py +31 -0
- astrbot/core/sandbox/booters/boxlite.py +186 -0
- astrbot/core/sandbox/booters/shipyard.py +67 -0
- astrbot/core/sandbox/olayer/__init__.py +5 -0
- astrbot/core/sandbox/olayer/filesystem.py +33 -0
- astrbot/core/sandbox/olayer/python.py +19 -0
- astrbot/core/sandbox/olayer/shell.py +21 -0
- astrbot/core/sandbox/sandbox_client.py +52 -0
- astrbot/core/sandbox/tools/__init__.py +10 -0
- astrbot/core/sandbox/tools/fs.py +188 -0
- astrbot/core/sandbox/tools/python.py +74 -0
- astrbot/core/sandbox/tools/shell.py +55 -0
- astrbot/core/star/context.py +162 -44
- astrbot/dashboard/routes/__init__.py +2 -0
- astrbot/dashboard/routes/chat.py +40 -12
- astrbot/dashboard/routes/chatui_project.py +245 -0
- astrbot/dashboard/routes/session_management.py +545 -0
- astrbot/dashboard/server.py +1 -0
- {astrbot-4.11.4.dist-info → astrbot-4.12.0.dist-info}/METADATA +2 -1
- {astrbot-4.11.4.dist-info → astrbot-4.12.0.dist-info}/RECORD +36 -27
- astrbot/builtin_stars/python_interpreter/main.py +0 -536
- astrbot/builtin_stars/python_interpreter/metadata.yaml +0 -4
- astrbot/builtin_stars/python_interpreter/requirements.txt +0 -1
- astrbot/builtin_stars/python_interpreter/shared/api.py +0 -22
- {astrbot-4.11.4.dist-info → astrbot-4.12.0.dist-info}/WHEEL +0 -0
- {astrbot-4.11.4.dist-info → astrbot-4.12.0.dist-info}/entry_points.txt +0 -0
- {astrbot-4.11.4.dist-info → astrbot-4.12.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -35,6 +35,14 @@ class SessionManagementRoute(Route):
|
|
|
35
35
|
"/session/delete-rule": ("POST", self.delete_session_rule),
|
|
36
36
|
"/session/batch-delete-rule": ("POST", self.batch_delete_session_rule),
|
|
37
37
|
"/session/active-umos": ("GET", self.list_umos),
|
|
38
|
+
"/session/list-all-with-status": ("GET", self.list_all_umos_with_status),
|
|
39
|
+
"/session/batch-update-service": ("POST", self.batch_update_service),
|
|
40
|
+
"/session/batch-update-provider": ("POST", self.batch_update_provider),
|
|
41
|
+
# 分组管理 API
|
|
42
|
+
"/session/groups": ("GET", self.list_groups),
|
|
43
|
+
"/session/group/create": ("POST", self.create_group),
|
|
44
|
+
"/session/group/update": ("POST", self.update_group),
|
|
45
|
+
"/session/group/delete": ("POST", self.delete_group),
|
|
38
46
|
}
|
|
39
47
|
self.conv_mgr = core_lifecycle.conversation_manager
|
|
40
48
|
self.core_lifecycle = core_lifecycle
|
|
@@ -391,3 +399,540 @@ class SessionManagementRoute(Route):
|
|
|
391
399
|
except Exception as e:
|
|
392
400
|
logger.error(f"获取 UMO 列表失败: {e!s}")
|
|
393
401
|
return Response().error(f"获取 UMO 列表失败: {e!s}").__dict__
|
|
402
|
+
|
|
403
|
+
async def list_all_umos_with_status(self):
|
|
404
|
+
"""获取所有有对话记录的 UMO 及其服务状态(支持分页、搜索、筛选)
|
|
405
|
+
|
|
406
|
+
Query 参数:
|
|
407
|
+
page: 页码,默认为 1
|
|
408
|
+
page_size: 每页数量,默认为 20
|
|
409
|
+
search: 搜索关键词
|
|
410
|
+
message_type: 筛选消息类型 (group/private/all)
|
|
411
|
+
platform: 筛选平台
|
|
412
|
+
"""
|
|
413
|
+
try:
|
|
414
|
+
page = request.args.get("page", 1, type=int)
|
|
415
|
+
page_size = request.args.get("page_size", 20, type=int)
|
|
416
|
+
search = request.args.get("search", "", type=str).strip()
|
|
417
|
+
message_type = request.args.get("message_type", "all", type=str)
|
|
418
|
+
platform = request.args.get("platform", "", type=str)
|
|
419
|
+
|
|
420
|
+
if page < 1:
|
|
421
|
+
page = 1
|
|
422
|
+
if page_size < 1:
|
|
423
|
+
page_size = 20
|
|
424
|
+
if page_size > 100:
|
|
425
|
+
page_size = 100
|
|
426
|
+
|
|
427
|
+
# 从 Conversation 表获取所有 distinct user_id (即 umo)
|
|
428
|
+
async with self.db_helper.get_db() as session:
|
|
429
|
+
session: AsyncSession
|
|
430
|
+
result = await session.execute(
|
|
431
|
+
select(ConversationV2.user_id)
|
|
432
|
+
.distinct()
|
|
433
|
+
.order_by(ConversationV2.user_id)
|
|
434
|
+
)
|
|
435
|
+
all_umos = [row[0] for row in result.fetchall()]
|
|
436
|
+
|
|
437
|
+
# 获取所有 umo 的规则配置
|
|
438
|
+
umo_rules, _ = await self._get_umo_rules(page=1, page_size=99999, search="")
|
|
439
|
+
|
|
440
|
+
# 构建带状态的 umo 列表
|
|
441
|
+
umos_with_status = []
|
|
442
|
+
for umo in all_umos:
|
|
443
|
+
parts = umo.split(":")
|
|
444
|
+
umo_platform = parts[0] if len(parts) >= 1 else "unknown"
|
|
445
|
+
umo_message_type = parts[1] if len(parts) >= 2 else "unknown"
|
|
446
|
+
umo_session_id = parts[2] if len(parts) >= 3 else umo
|
|
447
|
+
|
|
448
|
+
# 筛选消息类型
|
|
449
|
+
if message_type != "all":
|
|
450
|
+
if message_type == "group" and umo_message_type not in [
|
|
451
|
+
"group",
|
|
452
|
+
"GroupMessage",
|
|
453
|
+
]:
|
|
454
|
+
continue
|
|
455
|
+
if message_type == "private" and umo_message_type not in [
|
|
456
|
+
"private",
|
|
457
|
+
"FriendMessage",
|
|
458
|
+
"friend",
|
|
459
|
+
]:
|
|
460
|
+
continue
|
|
461
|
+
|
|
462
|
+
# 筛选平台
|
|
463
|
+
if platform and umo_platform != platform:
|
|
464
|
+
continue
|
|
465
|
+
|
|
466
|
+
# 获取服务配置
|
|
467
|
+
rules = umo_rules.get(umo, {})
|
|
468
|
+
svc_config = rules.get("session_service_config", {})
|
|
469
|
+
|
|
470
|
+
custom_name = svc_config.get("custom_name", "") if svc_config else ""
|
|
471
|
+
session_enabled = (
|
|
472
|
+
svc_config.get("session_enabled", True) if svc_config else True
|
|
473
|
+
)
|
|
474
|
+
llm_enabled = (
|
|
475
|
+
svc_config.get("llm_enabled", True) if svc_config else True
|
|
476
|
+
)
|
|
477
|
+
tts_enabled = (
|
|
478
|
+
svc_config.get("tts_enabled", True) if svc_config else True
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
# 搜索过滤
|
|
482
|
+
if search:
|
|
483
|
+
search_lower = search.lower()
|
|
484
|
+
if (
|
|
485
|
+
search_lower not in umo.lower()
|
|
486
|
+
and search_lower not in custom_name.lower()
|
|
487
|
+
):
|
|
488
|
+
continue
|
|
489
|
+
|
|
490
|
+
# 获取 provider 配置
|
|
491
|
+
chat_provider_key = (
|
|
492
|
+
f"provider_perf_{ProviderType.CHAT_COMPLETION.value}"
|
|
493
|
+
)
|
|
494
|
+
tts_provider_key = f"provider_perf_{ProviderType.TEXT_TO_SPEECH.value}"
|
|
495
|
+
stt_provider_key = f"provider_perf_{ProviderType.SPEECH_TO_TEXT.value}"
|
|
496
|
+
|
|
497
|
+
umos_with_status.append(
|
|
498
|
+
{
|
|
499
|
+
"umo": umo,
|
|
500
|
+
"platform": umo_platform,
|
|
501
|
+
"message_type": umo_message_type,
|
|
502
|
+
"session_id": umo_session_id,
|
|
503
|
+
"custom_name": custom_name,
|
|
504
|
+
"session_enabled": session_enabled,
|
|
505
|
+
"llm_enabled": llm_enabled,
|
|
506
|
+
"tts_enabled": tts_enabled,
|
|
507
|
+
"has_rules": umo in umo_rules,
|
|
508
|
+
"chat_provider": rules.get(chat_provider_key),
|
|
509
|
+
"tts_provider": rules.get(tts_provider_key),
|
|
510
|
+
"stt_provider": rules.get(stt_provider_key),
|
|
511
|
+
}
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
# 分页
|
|
515
|
+
total = len(umos_with_status)
|
|
516
|
+
start_idx = (page - 1) * page_size
|
|
517
|
+
end_idx = start_idx + page_size
|
|
518
|
+
paginated = umos_with_status[start_idx:end_idx]
|
|
519
|
+
|
|
520
|
+
# 获取可用的平台列表
|
|
521
|
+
platforms = list({u["platform"] for u in umos_with_status})
|
|
522
|
+
|
|
523
|
+
# 获取可用的 providers
|
|
524
|
+
provider_manager = self.core_lifecycle.provider_manager
|
|
525
|
+
available_chat_providers = [
|
|
526
|
+
{"id": p.meta().id, "name": p.meta().id, "model": p.meta().model}
|
|
527
|
+
for p in provider_manager.provider_insts
|
|
528
|
+
]
|
|
529
|
+
available_tts_providers = [
|
|
530
|
+
{"id": p.meta().id, "name": p.meta().id, "model": p.meta().model}
|
|
531
|
+
for p in provider_manager.tts_provider_insts
|
|
532
|
+
]
|
|
533
|
+
available_stt_providers = [
|
|
534
|
+
{"id": p.meta().id, "name": p.meta().id, "model": p.meta().model}
|
|
535
|
+
for p in provider_manager.stt_provider_insts
|
|
536
|
+
]
|
|
537
|
+
|
|
538
|
+
return (
|
|
539
|
+
Response()
|
|
540
|
+
.ok(
|
|
541
|
+
{
|
|
542
|
+
"sessions": paginated,
|
|
543
|
+
"total": total,
|
|
544
|
+
"page": page,
|
|
545
|
+
"page_size": page_size,
|
|
546
|
+
"platforms": platforms,
|
|
547
|
+
"available_chat_providers": available_chat_providers,
|
|
548
|
+
"available_tts_providers": available_tts_providers,
|
|
549
|
+
"available_stt_providers": available_stt_providers,
|
|
550
|
+
}
|
|
551
|
+
)
|
|
552
|
+
.__dict__
|
|
553
|
+
)
|
|
554
|
+
except Exception as e:
|
|
555
|
+
logger.error(f"获取会话状态列表失败: {e!s}")
|
|
556
|
+
return Response().error(f"获取会话状态列表失败: {e!s}").__dict__
|
|
557
|
+
|
|
558
|
+
async def batch_update_service(self):
|
|
559
|
+
"""批量更新多个 UMO 的服务状态 (LLM/TTS/Session)
|
|
560
|
+
|
|
561
|
+
请求体:
|
|
562
|
+
{
|
|
563
|
+
"umos": ["平台:消息类型:会话ID", ...], // 可选,如果不传则根据 scope 筛选
|
|
564
|
+
"scope": "all" | "group" | "private" | "custom_group", // 可选,批量范围
|
|
565
|
+
"group_id": "分组ID", // 当 scope 为 custom_group 时必填
|
|
566
|
+
"llm_enabled": true/false/null, // 可选,null表示不修改
|
|
567
|
+
"tts_enabled": true/false/null, // 可选
|
|
568
|
+
"session_enabled": true/false/null // 可选
|
|
569
|
+
}
|
|
570
|
+
"""
|
|
571
|
+
try:
|
|
572
|
+
data = await request.get_json()
|
|
573
|
+
umos = data.get("umos", [])
|
|
574
|
+
scope = data.get("scope", "")
|
|
575
|
+
group_id = data.get("group_id", "")
|
|
576
|
+
llm_enabled = data.get("llm_enabled")
|
|
577
|
+
tts_enabled = data.get("tts_enabled")
|
|
578
|
+
session_enabled = data.get("session_enabled")
|
|
579
|
+
|
|
580
|
+
# 如果没有任何修改
|
|
581
|
+
if llm_enabled is None and tts_enabled is None and session_enabled is None:
|
|
582
|
+
return Response().error("至少需要指定一个要修改的状态").__dict__
|
|
583
|
+
|
|
584
|
+
# 如果指定了 scope,获取符合条件的所有 umo
|
|
585
|
+
if scope and not umos:
|
|
586
|
+
# 如果是自定义分组
|
|
587
|
+
if scope == "custom_group":
|
|
588
|
+
if not group_id:
|
|
589
|
+
return Response().error("请指定分组 ID").__dict__
|
|
590
|
+
groups = self._get_groups()
|
|
591
|
+
if group_id not in groups:
|
|
592
|
+
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
|
593
|
+
umos = groups[group_id].get("umos", [])
|
|
594
|
+
else:
|
|
595
|
+
async with self.db_helper.get_db() as session:
|
|
596
|
+
session: AsyncSession
|
|
597
|
+
result = await session.execute(
|
|
598
|
+
select(ConversationV2.user_id).distinct()
|
|
599
|
+
)
|
|
600
|
+
all_umos = [row[0] for row in result.fetchall()]
|
|
601
|
+
|
|
602
|
+
if scope == "group":
|
|
603
|
+
umos = [
|
|
604
|
+
u
|
|
605
|
+
for u in all_umos
|
|
606
|
+
if ":group:" in u.lower() or ":groupmessage:" in u.lower()
|
|
607
|
+
]
|
|
608
|
+
elif scope == "private":
|
|
609
|
+
umos = [
|
|
610
|
+
u
|
|
611
|
+
for u in all_umos
|
|
612
|
+
if ":private:" in u.lower() or ":friend" in u.lower()
|
|
613
|
+
]
|
|
614
|
+
elif scope == "all":
|
|
615
|
+
umos = all_umos
|
|
616
|
+
|
|
617
|
+
if not umos:
|
|
618
|
+
return Response().error("没有找到符合条件的会话").__dict__
|
|
619
|
+
|
|
620
|
+
# 批量更新
|
|
621
|
+
success_count = 0
|
|
622
|
+
failed_umos = []
|
|
623
|
+
|
|
624
|
+
for umo in umos:
|
|
625
|
+
try:
|
|
626
|
+
# 获取现有配置
|
|
627
|
+
session_config = (
|
|
628
|
+
sp.get("session_service_config", {}, scope="umo", scope_id=umo)
|
|
629
|
+
or {}
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
# 更新状态
|
|
633
|
+
if llm_enabled is not None:
|
|
634
|
+
session_config["llm_enabled"] = llm_enabled
|
|
635
|
+
if tts_enabled is not None:
|
|
636
|
+
session_config["tts_enabled"] = tts_enabled
|
|
637
|
+
if session_enabled is not None:
|
|
638
|
+
session_config["session_enabled"] = session_enabled
|
|
639
|
+
|
|
640
|
+
# 保存
|
|
641
|
+
sp.put(
|
|
642
|
+
"session_service_config",
|
|
643
|
+
session_config,
|
|
644
|
+
scope="umo",
|
|
645
|
+
scope_id=umo,
|
|
646
|
+
)
|
|
647
|
+
success_count += 1
|
|
648
|
+
except Exception as e:
|
|
649
|
+
logger.error(f"更新 {umo} 服务状态失败: {e!s}")
|
|
650
|
+
failed_umos.append(umo)
|
|
651
|
+
|
|
652
|
+
status_changes = []
|
|
653
|
+
if llm_enabled is not None:
|
|
654
|
+
status_changes.append(f"LLM={'启用' if llm_enabled else '禁用'}")
|
|
655
|
+
if tts_enabled is not None:
|
|
656
|
+
status_changes.append(f"TTS={'启用' if tts_enabled else '禁用'}")
|
|
657
|
+
if session_enabled is not None:
|
|
658
|
+
status_changes.append(f"会话={'启用' if session_enabled else '禁用'}")
|
|
659
|
+
|
|
660
|
+
return (
|
|
661
|
+
Response()
|
|
662
|
+
.ok(
|
|
663
|
+
{
|
|
664
|
+
"message": f"已更新 {success_count} 个会话 ({', '.join(status_changes)})",
|
|
665
|
+
"success_count": success_count,
|
|
666
|
+
"failed_count": len(failed_umos),
|
|
667
|
+
"failed_umos": failed_umos,
|
|
668
|
+
}
|
|
669
|
+
)
|
|
670
|
+
.__dict__
|
|
671
|
+
)
|
|
672
|
+
except Exception as e:
|
|
673
|
+
logger.error(f"批量更新服务状态失败: {e!s}")
|
|
674
|
+
return Response().error(f"批量更新服务状态失败: {e!s}").__dict__
|
|
675
|
+
|
|
676
|
+
async def batch_update_provider(self):
|
|
677
|
+
"""批量更新多个 UMO 的 Provider 配置
|
|
678
|
+
|
|
679
|
+
请求体:
|
|
680
|
+
{
|
|
681
|
+
"umos": ["平台:消息类型:会话ID", ...], // 可选
|
|
682
|
+
"scope": "all" | "group" | "private", // 可选
|
|
683
|
+
"provider_type": "chat_completion" | "text_to_speech" | "speech_to_text",
|
|
684
|
+
"provider_id": "provider_id"
|
|
685
|
+
}
|
|
686
|
+
"""
|
|
687
|
+
try:
|
|
688
|
+
data = await request.get_json()
|
|
689
|
+
umos = data.get("umos", [])
|
|
690
|
+
scope = data.get("scope", "")
|
|
691
|
+
provider_type = data.get("provider_type")
|
|
692
|
+
provider_id = data.get("provider_id")
|
|
693
|
+
|
|
694
|
+
if not provider_type or not provider_id:
|
|
695
|
+
return (
|
|
696
|
+
Response()
|
|
697
|
+
.error("缺少必要参数: provider_type, provider_id")
|
|
698
|
+
.__dict__
|
|
699
|
+
)
|
|
700
|
+
|
|
701
|
+
# 转换 provider_type
|
|
702
|
+
provider_type_map = {
|
|
703
|
+
"chat_completion": ProviderType.CHAT_COMPLETION,
|
|
704
|
+
"text_to_speech": ProviderType.TEXT_TO_SPEECH,
|
|
705
|
+
"speech_to_text": ProviderType.SPEECH_TO_TEXT,
|
|
706
|
+
}
|
|
707
|
+
if provider_type not in provider_type_map:
|
|
708
|
+
return (
|
|
709
|
+
Response()
|
|
710
|
+
.error(f"不支持的 provider_type: {provider_type}")
|
|
711
|
+
.__dict__
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
provider_type_enum = provider_type_map[provider_type]
|
|
715
|
+
|
|
716
|
+
# 如果指定了 scope,获取符合条件的所有 umo
|
|
717
|
+
group_id = data.get("group_id", "")
|
|
718
|
+
if scope and not umos:
|
|
719
|
+
# 如果是自定义分组
|
|
720
|
+
if scope == "custom_group":
|
|
721
|
+
if not group_id:
|
|
722
|
+
return Response().error("请指定分组 ID").__dict__
|
|
723
|
+
groups = self._get_groups()
|
|
724
|
+
if group_id not in groups:
|
|
725
|
+
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
|
726
|
+
umos = groups[group_id].get("umos", [])
|
|
727
|
+
else:
|
|
728
|
+
async with self.db_helper.get_db() as session:
|
|
729
|
+
session: AsyncSession
|
|
730
|
+
result = await session.execute(
|
|
731
|
+
select(ConversationV2.user_id).distinct()
|
|
732
|
+
)
|
|
733
|
+
all_umos = [row[0] for row in result.fetchall()]
|
|
734
|
+
|
|
735
|
+
if scope == "group":
|
|
736
|
+
umos = [
|
|
737
|
+
u
|
|
738
|
+
for u in all_umos
|
|
739
|
+
if ":group:" in u.lower() or ":groupmessage:" in u.lower()
|
|
740
|
+
]
|
|
741
|
+
elif scope == "private":
|
|
742
|
+
umos = [
|
|
743
|
+
u
|
|
744
|
+
for u in all_umos
|
|
745
|
+
if ":private:" in u.lower() or ":friend" in u.lower()
|
|
746
|
+
]
|
|
747
|
+
elif scope == "all":
|
|
748
|
+
umos = all_umos
|
|
749
|
+
|
|
750
|
+
if not umos:
|
|
751
|
+
return Response().error("没有找到符合条件的会话").__dict__
|
|
752
|
+
|
|
753
|
+
# 批量更新
|
|
754
|
+
success_count = 0
|
|
755
|
+
failed_umos = []
|
|
756
|
+
provider_manager = self.core_lifecycle.provider_manager
|
|
757
|
+
|
|
758
|
+
for umo in umos:
|
|
759
|
+
try:
|
|
760
|
+
await provider_manager.set_provider(
|
|
761
|
+
provider_id=provider_id,
|
|
762
|
+
provider_type=provider_type_enum,
|
|
763
|
+
umo=umo,
|
|
764
|
+
)
|
|
765
|
+
success_count += 1
|
|
766
|
+
except Exception as e:
|
|
767
|
+
logger.error(f"更新 {umo} Provider 失败: {e!s}")
|
|
768
|
+
failed_umos.append(umo)
|
|
769
|
+
|
|
770
|
+
return (
|
|
771
|
+
Response()
|
|
772
|
+
.ok(
|
|
773
|
+
{
|
|
774
|
+
"message": f"已更新 {success_count} 个会话的 {provider_type} 为 {provider_id}",
|
|
775
|
+
"success_count": success_count,
|
|
776
|
+
"failed_count": len(failed_umos),
|
|
777
|
+
"failed_umos": failed_umos,
|
|
778
|
+
}
|
|
779
|
+
)
|
|
780
|
+
.__dict__
|
|
781
|
+
)
|
|
782
|
+
except Exception as e:
|
|
783
|
+
logger.error(f"批量更新 Provider 失败: {e!s}")
|
|
784
|
+
return Response().error(f"批量更新 Provider 失败: {e!s}").__dict__
|
|
785
|
+
|
|
786
|
+
# ==================== 分组管理 API ====================
|
|
787
|
+
|
|
788
|
+
def _get_groups(self) -> dict:
|
|
789
|
+
"""获取所有分组"""
|
|
790
|
+
return sp.get("session_groups", {})
|
|
791
|
+
|
|
792
|
+
def _save_groups(self, groups: dict) -> None:
|
|
793
|
+
"""保存分组"""
|
|
794
|
+
sp.put("session_groups", groups)
|
|
795
|
+
|
|
796
|
+
async def list_groups(self):
|
|
797
|
+
"""获取所有分组列表"""
|
|
798
|
+
try:
|
|
799
|
+
groups = self._get_groups()
|
|
800
|
+
# 转换为列表格式,方便前端使用
|
|
801
|
+
groups_list = []
|
|
802
|
+
for group_id, group_data in groups.items():
|
|
803
|
+
groups_list.append(
|
|
804
|
+
{
|
|
805
|
+
"id": group_id,
|
|
806
|
+
"name": group_data.get("name", ""),
|
|
807
|
+
"umos": group_data.get("umos", []),
|
|
808
|
+
"umo_count": len(group_data.get("umos", [])),
|
|
809
|
+
}
|
|
810
|
+
)
|
|
811
|
+
return Response().ok({"groups": groups_list}).__dict__
|
|
812
|
+
except Exception as e:
|
|
813
|
+
logger.error(f"获取分组列表失败: {e!s}")
|
|
814
|
+
return Response().error(f"获取分组列表失败: {e!s}").__dict__
|
|
815
|
+
|
|
816
|
+
async def create_group(self):
|
|
817
|
+
"""创建新分组"""
|
|
818
|
+
try:
|
|
819
|
+
data = await request.json
|
|
820
|
+
name = data.get("name", "").strip()
|
|
821
|
+
umos = data.get("umos", [])
|
|
822
|
+
|
|
823
|
+
if not name:
|
|
824
|
+
return Response().error("分组名称不能为空").__dict__
|
|
825
|
+
|
|
826
|
+
groups = self._get_groups()
|
|
827
|
+
|
|
828
|
+
# 生成唯一 ID
|
|
829
|
+
import uuid
|
|
830
|
+
|
|
831
|
+
group_id = str(uuid.uuid4())[:8]
|
|
832
|
+
|
|
833
|
+
groups[group_id] = {
|
|
834
|
+
"name": name,
|
|
835
|
+
"umos": umos,
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
self._save_groups(groups)
|
|
839
|
+
|
|
840
|
+
return (
|
|
841
|
+
Response()
|
|
842
|
+
.ok(
|
|
843
|
+
{
|
|
844
|
+
"message": f"分组 '{name}' 创建成功",
|
|
845
|
+
"group": {
|
|
846
|
+
"id": group_id,
|
|
847
|
+
"name": name,
|
|
848
|
+
"umos": umos,
|
|
849
|
+
"umo_count": len(umos),
|
|
850
|
+
},
|
|
851
|
+
}
|
|
852
|
+
)
|
|
853
|
+
.__dict__
|
|
854
|
+
)
|
|
855
|
+
except Exception as e:
|
|
856
|
+
logger.error(f"创建分组失败: {e!s}")
|
|
857
|
+
return Response().error(f"创建分组失败: {e!s}").__dict__
|
|
858
|
+
|
|
859
|
+
async def update_group(self):
|
|
860
|
+
"""更新分组(改名、增删成员)"""
|
|
861
|
+
try:
|
|
862
|
+
data = await request.json
|
|
863
|
+
group_id = data.get("id")
|
|
864
|
+
name = data.get("name")
|
|
865
|
+
umos = data.get("umos")
|
|
866
|
+
add_umos = data.get("add_umos", [])
|
|
867
|
+
remove_umos = data.get("remove_umos", [])
|
|
868
|
+
|
|
869
|
+
if not group_id:
|
|
870
|
+
return Response().error("分组 ID 不能为空").__dict__
|
|
871
|
+
|
|
872
|
+
groups = self._get_groups()
|
|
873
|
+
|
|
874
|
+
if group_id not in groups:
|
|
875
|
+
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
|
876
|
+
|
|
877
|
+
group = groups[group_id]
|
|
878
|
+
|
|
879
|
+
# 更新名称
|
|
880
|
+
if name is not None:
|
|
881
|
+
group["name"] = name.strip()
|
|
882
|
+
|
|
883
|
+
# 直接设置 umos 列表
|
|
884
|
+
if umos is not None:
|
|
885
|
+
group["umos"] = umos
|
|
886
|
+
else:
|
|
887
|
+
# 增量更新
|
|
888
|
+
current_umos = set(group.get("umos", []))
|
|
889
|
+
if add_umos:
|
|
890
|
+
current_umos.update(add_umos)
|
|
891
|
+
if remove_umos:
|
|
892
|
+
current_umos.difference_update(remove_umos)
|
|
893
|
+
group["umos"] = list(current_umos)
|
|
894
|
+
|
|
895
|
+
self._save_groups(groups)
|
|
896
|
+
|
|
897
|
+
return (
|
|
898
|
+
Response()
|
|
899
|
+
.ok(
|
|
900
|
+
{
|
|
901
|
+
"message": f"分组 '{group['name']}' 更新成功",
|
|
902
|
+
"group": {
|
|
903
|
+
"id": group_id,
|
|
904
|
+
"name": group["name"],
|
|
905
|
+
"umos": group["umos"],
|
|
906
|
+
"umo_count": len(group["umos"]),
|
|
907
|
+
},
|
|
908
|
+
}
|
|
909
|
+
)
|
|
910
|
+
.__dict__
|
|
911
|
+
)
|
|
912
|
+
except Exception as e:
|
|
913
|
+
logger.error(f"更新分组失败: {e!s}")
|
|
914
|
+
return Response().error(f"更新分组失败: {e!s}").__dict__
|
|
915
|
+
|
|
916
|
+
async def delete_group(self):
|
|
917
|
+
"""删除分组"""
|
|
918
|
+
try:
|
|
919
|
+
data = await request.json
|
|
920
|
+
group_id = data.get("id")
|
|
921
|
+
|
|
922
|
+
if not group_id:
|
|
923
|
+
return Response().error("分组 ID 不能为空").__dict__
|
|
924
|
+
|
|
925
|
+
groups = self._get_groups()
|
|
926
|
+
|
|
927
|
+
if group_id not in groups:
|
|
928
|
+
return Response().error(f"分组 '{group_id}' 不存在").__dict__
|
|
929
|
+
|
|
930
|
+
group_name = groups[group_id].get("name", group_id)
|
|
931
|
+
del groups[group_id]
|
|
932
|
+
|
|
933
|
+
self._save_groups(groups)
|
|
934
|
+
|
|
935
|
+
return Response().ok({"message": f"分组 '{group_name}' 已删除"}).__dict__
|
|
936
|
+
except Exception as e:
|
|
937
|
+
logger.error(f"删除分组失败: {e!s}")
|
|
938
|
+
return Response().error(f"删除分组失败: {e!s}").__dict__
|
astrbot/dashboard/server.py
CHANGED
|
@@ -74,6 +74,7 @@ class AstrBotDashboard:
|
|
|
74
74
|
self.sfr = StaticFileRoute(self.context)
|
|
75
75
|
self.ar = AuthRoute(self.context)
|
|
76
76
|
self.chat_route = ChatRoute(self.context, db, core_lifecycle)
|
|
77
|
+
self.chatui_project_route = ChatUIProjectRoute(self.context, db)
|
|
77
78
|
self.tools_root = ToolsRoute(self.context, core_lifecycle)
|
|
78
79
|
self.conversation_route = ConversationRoute(self.context, db, core_lifecycle)
|
|
79
80
|
self.file_route = FileRoute(self.context)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AstrBot
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.12.0
|
|
4
4
|
Summary: Easy-to-use multi-platform LLM chatbot and development framework
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Keywords: Astrbot,Astrbot Module,Astrbot Plugin
|
|
@@ -47,6 +47,7 @@ Requires-Dist: qq-botpy>=1.2.1
|
|
|
47
47
|
Requires-Dist: quart>=0.20.0
|
|
48
48
|
Requires-Dist: rank-bm25>=0.2.2
|
|
49
49
|
Requires-Dist: readability-lxml>=0.8.4.1
|
|
50
|
+
Requires-Dist: shipyard-python-sdk>=0.2.4
|
|
50
51
|
Requires-Dist: silk-python>=0.2.6
|
|
51
52
|
Requires-Dist: slack-sdk>=3.35.0
|
|
52
53
|
Requires-Dist: sqlalchemy[asyncio]>=2.0.41
|