AstrBot 4.2.1__py3-none-any.whl → 4.3.1__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.
@@ -95,9 +95,8 @@ class TelegramPlatformAdapter(Platform):
95
95
 
96
96
  @override
97
97
  def meta(self) -> PlatformMetadata:
98
- return PlatformMetadata(
99
- name="telegram", description="telegram 适配器", id=self.config.get("id")
100
- )
98
+ id_ = self.config.get("id") or "telegram"
99
+ return PlatformMetadata(name="telegram", description="telegram 适配器", id=id_)
101
100
 
102
101
  @override
103
102
  async def run(self):
@@ -117,6 +116,10 @@ class TelegramPlatformAdapter(Platform):
117
116
  )
118
117
  self.scheduler.start()
119
118
 
119
+ if not self.application.updater:
120
+ logger.error("Telegram Updater is not initialized. Cannot start polling.")
121
+ return
122
+
120
123
  queue = self.application.updater.start_polling()
121
124
  logger.info("Telegram Platform Adapter is running.")
122
125
  await queue
@@ -194,6 +197,11 @@ class TelegramPlatformAdapter(Platform):
194
197
  return cmd_name, description
195
198
 
196
199
  async def start(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
200
+ if not update.effective_chat:
201
+ logger.warning(
202
+ "Received a start command without an effective chat, skipping /start reply."
203
+ )
204
+ return
197
205
  await context.bot.send_message(
198
206
  chat_id=update.effective_chat.id, text=self.config["start_message"]
199
207
  )
@@ -206,15 +214,20 @@ class TelegramPlatformAdapter(Platform):
206
214
 
207
215
  async def convert_message(
208
216
  self, update: Update, context: ContextTypes.DEFAULT_TYPE, get_reply=True
209
- ) -> AstrBotMessage:
217
+ ) -> AstrBotMessage | None:
210
218
  """转换 Telegram 的消息对象为 AstrBotMessage 对象。
211
219
 
212
220
  @param update: Telegram 的 Update 对象。
213
221
  @param context: Telegram 的 Context 对象。
214
222
  @param get_reply: 是否获取回复消息。这个参数是为了防止多个回复嵌套。
215
223
  """
224
+ if not update.message:
225
+ logger.warning("Received an update without a message.")
226
+ return None
227
+
216
228
  message = AstrBotMessage()
217
229
  message.session_id = str(update.message.chat.id)
230
+
218
231
  # 获得是群聊还是私聊
219
232
  if update.message.chat.type == ChatType.PRIVATE:
220
233
  message.type = MessageType.FRIEND_MESSAGE
@@ -225,10 +238,13 @@ class TelegramPlatformAdapter(Platform):
225
238
  # Topic Group
226
239
  message.group_id += "#" + str(update.message.message_thread_id)
227
240
  message.session_id = message.group_id
228
-
229
241
  message.message_id = str(update.message.message_id)
242
+ _from_user = update.message.from_user
243
+ if not _from_user:
244
+ logger.warning("[Telegram] Received a message without a from_user.")
245
+ return None
230
246
  message.sender = MessageMember(
231
- str(update.message.from_user.id), update.message.from_user.username
247
+ str(_from_user.id), _from_user.username or "Unknown"
232
248
  )
233
249
  message.self_id = str(context.bot.username)
234
250
  message.raw_message = update
@@ -247,22 +263,32 @@ class TelegramPlatformAdapter(Platform):
247
263
  )
248
264
  reply_abm = await self.convert_message(reply_update, context, False)
249
265
 
250
- message.message.append(
251
- Comp.Reply(
252
- id=reply_abm.message_id,
253
- chain=reply_abm.message,
254
- sender_id=reply_abm.sender.user_id,
255
- sender_nickname=reply_abm.sender.nickname,
256
- time=reply_abm.timestamp,
257
- message_str=reply_abm.message_str,
258
- text=reply_abm.message_str,
259
- qq=reply_abm.sender.user_id,
266
+ if reply_abm:
267
+ message.message.append(
268
+ Comp.Reply(
269
+ id=reply_abm.message_id,
270
+ chain=reply_abm.message,
271
+ sender_id=reply_abm.sender.user_id,
272
+ sender_nickname=reply_abm.sender.nickname,
273
+ time=reply_abm.timestamp,
274
+ message_str=reply_abm.message_str,
275
+ text=reply_abm.message_str,
276
+ qq=reply_abm.sender.user_id,
277
+ )
260
278
  )
261
- )
262
279
 
263
280
  if update.message.text:
264
281
  # 处理文本消息
265
282
  plain_text = update.message.text
283
+ if (
284
+ message.type == MessageType.GROUP_MESSAGE
285
+ and update.message
286
+ and update.message.reply_to_message
287
+ and update.message.reply_to_message.from_user
288
+ and update.message.reply_to_message.from_user.id == context.bot.id
289
+ ):
290
+ plain_text2 = f"/@{context.bot.username} " + plain_text
291
+ plain_text = plain_text2
266
292
 
267
293
  # 群聊场景命令特殊处理
268
294
  if plain_text.startswith("/"):
@@ -328,15 +354,25 @@ class TelegramPlatformAdapter(Platform):
328
354
 
329
355
  elif update.message.document:
330
356
  file = await update.message.document.get_file()
331
- message.message = [
332
- Comp.File(file=file.file_path, name=update.message.document.file_name),
333
- ]
357
+ file_name = update.message.document.file_name or uuid.uuid4().hex
358
+ file_path = file.file_path
359
+ if file_path is None:
360
+ logger.warning(
361
+ f"Telegram document file_path is None, cannot save the file {file_name}."
362
+ )
363
+ else:
364
+ message.message.append(Comp.File(file=file_path, name=file_name))
334
365
 
335
366
  elif update.message.video:
336
367
  file = await update.message.video.get_file()
337
- message.message = [
338
- Comp.Video(file=file.file_path, path=file.file_path),
339
- ]
368
+ file_name = update.message.video.file_name or uuid.uuid4().hex
369
+ file_path = file.file_path
370
+ if file_path is None:
371
+ logger.warning(
372
+ f"Telegram video file_path is None, cannot save the file {file_name}."
373
+ )
374
+ else:
375
+ message.message.append(Comp.Video(file=file_path, path=file.file_path))
340
376
 
341
377
  return message
342
378
 
@@ -16,6 +16,7 @@ from telegram.ext import ExtBot
16
16
  from astrbot.core.utils.io import download_file
17
17
  from astrbot import logger
18
18
  from astrbot.core.utils.astrbot_path import get_astrbot_data_path
19
+ from telegram import ReactionTypeEmoji, ReactionTypeCustomEmoji
19
20
 
20
21
 
21
22
  class TelegramPlatformEvent(AstrMessageEvent):
@@ -135,6 +136,39 @@ class TelegramPlatformEvent(AstrMessageEvent):
135
136
  await self.send_with_client(self.client, message, self.get_sender_id())
136
137
  await super().send(message)
137
138
 
139
+ async def react(self, emoji: str | None, big: bool = False):
140
+ """
141
+ 给原消息添加 Telegram 反应:
142
+ - 普通 emoji:传入 '👍'、'😂' 等
143
+ - 自定义表情:传入其 custom_emoji_id(纯数字字符串)
144
+ - 取消本机器人的反应:传入 None 或空字符串
145
+ """
146
+ try:
147
+ # 解析 chat_id(去掉超级群的 "#<thread_id>" 片段)
148
+ if self.get_message_type() == MessageType.GROUP_MESSAGE:
149
+ chat_id = (self.message_obj.group_id or "").split("#")[0]
150
+ else:
151
+ chat_id = self.get_sender_id()
152
+
153
+ message_id = int(self.message_obj.message_id)
154
+
155
+ # 组装 reaction 参数(必须是 ReactionType 的列表)
156
+ if not emoji: # 清空本 bot 的反应
157
+ reaction_param = [] # 空列表表示移除本 bot 的反应
158
+ elif emoji.isdigit(): # 自定义表情:传 custom_emoji_id
159
+ reaction_param = [ReactionTypeCustomEmoji(emoji)]
160
+ else: # 普通 emoji
161
+ reaction_param = [ReactionTypeEmoji(emoji)]
162
+
163
+ await self.client.set_message_reaction(
164
+ chat_id=chat_id,
165
+ message_id=message_id,
166
+ reaction=reaction_param, # 注意是列表
167
+ is_big=big, # 可选:大动画
168
+ )
169
+ except Exception as e:
170
+ logger.error(f"[Telegram] 添加反应失败: {e}")
171
+
138
172
  async def send_streaming(self, generator, use_fallback: bool = False):
139
173
  message_thread_id = None
140
174
 
@@ -75,7 +75,7 @@ class Provider(AbstractProvider):
75
75
  raise NotImplementedError()
76
76
 
77
77
  @abc.abstractmethod
78
- def get_models(self) -> List[str]:
78
+ async def get_models(self) -> List[str]:
79
79
  """获得支持的模型列表"""
80
80
  raise NotImplementedError()
81
81
 
@@ -1,12 +1,12 @@
1
- from astrbot import logger
2
- from astrbot.core.provider.func_tool_manager import FuncCall
3
- from typing import List
1
+ # This file was originally created to adapt to glm-4v-flash, which only supports one image in the context.
2
+ # It is no longer specifically adapted to Zhipu's models. To ensure compatibility, this
3
+
4
+
4
5
  from ..register import register_provider_adapter
5
- from astrbot.core.provider.entities import LLMResponse
6
6
  from .openai_source import ProviderOpenAIOfficial
7
7
 
8
8
 
9
- @register_provider_adapter("zhipu_chat_completion", "智浦 Chat Completion 提供商适配器")
9
+ @register_provider_adapter("zhipu_chat_completion", "智谱 Chat Completion 提供商适配器")
10
10
  class ProviderZhipu(ProviderOpenAIOfficial):
11
11
  def __init__(
12
12
  self,
@@ -19,63 +19,3 @@ class ProviderZhipu(ProviderOpenAIOfficial):
19
19
  provider_settings,
20
20
  default_persona,
21
21
  )
22
-
23
- async def text_chat(
24
- self,
25
- prompt: str,
26
- session_id: str = None,
27
- image_urls: List[str] = None,
28
- func_tool: FuncCall = None,
29
- contexts=None,
30
- system_prompt=None,
31
- model=None,
32
- **kwargs,
33
- ) -> LLMResponse:
34
- if contexts is None:
35
- contexts = []
36
- new_record = await self.assemble_context(prompt, image_urls)
37
- context_query = []
38
-
39
- context_query = [*contexts, new_record]
40
-
41
- model_cfgs: dict = self.provider_config.get("model_config", {})
42
- model = model or self.get_model()
43
- # glm-4v-flash 只支持一张图片
44
- if model.lower() == "glm-4v-flash" and image_urls and len(context_query) > 1:
45
- logger.debug("glm-4v-flash 只支持一张图片,将只保留最后一张图片")
46
- logger.debug(context_query)
47
- new_context_query_ = []
48
- for i in range(0, len(context_query) - 1, 2):
49
- if isinstance(context_query[i].get("content", ""), list):
50
- continue
51
- new_context_query_.append(context_query[i])
52
- new_context_query_.append(context_query[i + 1])
53
- new_context_query_.append(context_query[-1]) # 保留最后一条记录
54
- context_query = new_context_query_
55
- logger.debug(context_query)
56
-
57
- if system_prompt:
58
- context_query.insert(0, {"role": "system", "content": system_prompt})
59
-
60
- payloads = {"messages": context_query, **model_cfgs}
61
- try:
62
- llm_response = await self._query(payloads, func_tool)
63
- return llm_response
64
- except Exception as e:
65
- if "maximum context length" in str(e):
66
- retry_cnt = 10
67
- while retry_cnt > 0:
68
- logger.warning(
69
- f"请求失败:{e}。上下文长度超过限制。尝试弹出最早的记录然后重试。"
70
- )
71
- try:
72
- self.pop_record(session_id)
73
- llm_response = await self._query(payloads, func_tool)
74
- break
75
- except Exception as e:
76
- if "maximum context length" in str(e):
77
- retry_cnt -= 1
78
- else:
79
- raise e
80
- else:
81
- raise e
@@ -1,5 +1,7 @@
1
1
  import re
2
2
  import inspect
3
+ import types
4
+ import typing
3
5
  from typing import List, Any, Type, Dict
4
6
  from . import HandlerFilter
5
7
  from astrbot.core.platform.astr_message_event import AstrMessageEvent
@@ -14,6 +16,18 @@ class GreedyStr(str):
14
16
  pass
15
17
 
16
18
 
19
+ def unwrap_optional(annotation) -> tuple:
20
+ """去掉 Optional[T] / Union[T, None] / T|None,返回 T"""
21
+ args = typing.get_args(annotation)
22
+ non_none_args = [a for a in args if a is not type(None)]
23
+ if len(non_none_args) == 1:
24
+ return (non_none_args[0],)
25
+ elif len(non_none_args) > 1:
26
+ return tuple(non_none_args)
27
+ else:
28
+ return ()
29
+
30
+
17
31
  # 标准指令受到 wake_prefix 的制约。
18
32
  class CommandFilter(HandlerFilter):
19
33
  """标准指令过滤器"""
@@ -40,6 +54,8 @@ class CommandFilter(HandlerFilter):
40
54
  for k, v in self.handler_params.items():
41
55
  if isinstance(v, type):
42
56
  result += f"{k}({v.__name__}),"
57
+ elif isinstance(v, types.UnionType) or typing.get_origin(v) is typing.Union:
58
+ result += f"{k}({v}),"
43
59
  else:
44
60
  result += f"{k}({type(v).__name__})={v},"
45
61
  result = result.rstrip(",")
@@ -95,7 +111,8 @@ class CommandFilter(HandlerFilter):
95
111
  # 没有 GreedyStr 的情况
96
112
  if i >= len(params):
97
113
  if (
98
- isinstance(param_type_or_default_val, Type)
114
+ isinstance(param_type_or_default_val, (Type, types.UnionType))
115
+ or typing.get_origin(param_type_or_default_val) is typing.Union
99
116
  or param_type_or_default_val is inspect.Parameter.empty
100
117
  ):
101
118
  # 是类型
@@ -132,7 +149,20 @@ class CommandFilter(HandlerFilter):
132
149
  elif isinstance(param_type_or_default_val, float):
133
150
  result[param_name] = float(params[i])
134
151
  else:
135
- result[param_name] = param_type_or_default_val(params[i])
152
+ origin = typing.get_origin(param_type_or_default_val)
153
+ if origin in (typing.Union, types.UnionType):
154
+ # 注解是联合类型
155
+ # NOTE: 目前没有处理联合类型嵌套相关的注解写法
156
+ nn_types = unwrap_optional(param_type_or_default_val)
157
+ if len(nn_types) == 1:
158
+ # 只有一个非 NoneType 类型
159
+ result[param_name] = nn_types[0](params[i])
160
+ else:
161
+ # 没有或者有多个非 NoneType 类型,这里我们暂时直接赋值为原始值。
162
+ # NOTE: 目前还没有做类型校验
163
+ result[param_name] = params[i]
164
+ else:
165
+ result[param_name] = param_type_or_default_val(params[i])
136
166
  except ValueError:
137
167
  raise ValueError(
138
168
  f"参数 {param_name} 类型错误。完整参数: {self.print_types()}"
@@ -205,7 +205,6 @@ def register_command_group(
205
205
  new_group = CommandGroupFilter(command_group_name, alias)
206
206
 
207
207
  def decorator(obj):
208
- # 根指令组
209
208
  if new_group:
210
209
  handler_md = get_handler_or_create(
211
210
  obj, EventType.AdapterMessageEvent, **kwargs
@@ -213,6 +212,7 @@ def register_command_group(
213
212
  handler_md.event_filters.append(new_group)
214
213
 
215
214
  return RegisteringCommandable(new_group)
215
+ raise ValueError("注册指令组失败。")
216
216
 
217
217
  return decorator
218
218
 
@@ -220,9 +220,11 @@ def register_command_group(
220
220
  class RegisteringCommandable:
221
221
  """用于指令组级联注册"""
222
222
 
223
- group: CommandGroupFilter = register_command_group
224
- command: CommandFilter = register_command
225
- custom_filter = register_custom_filter
223
+ group: Callable[..., Callable[..., "RegisteringCommandable"]] = (
224
+ register_command_group
225
+ )
226
+ command: Callable[..., Callable[..., None]] = register_command
227
+ custom_filter: Callable[..., Callable[..., None]] = register_custom_filter
226
228
 
227
229
  def __init__(self, parent_group: CommandGroupFilter):
228
230
  self.parent_group = parent_group
@@ -6,7 +6,7 @@ class CommandTokens:
6
6
  self.tokens = []
7
7
  self.len = 0
8
8
 
9
- def get(self, idx: int):
9
+ def get(self, idx: int) -> str | None:
10
10
  if idx >= self.len:
11
11
  return None
12
12
  return self.tokens[idx].strip()
@@ -1,6 +1,7 @@
1
1
  import typing
2
2
  import traceback
3
3
  import os
4
+ import inspect
4
5
  from .route import Route, Response, RouteContext
5
6
  from astrbot.core.provider.entities import ProviderType
6
7
  from quart import request
@@ -13,10 +14,10 @@ from astrbot.core.config.default import (
13
14
  from astrbot.core.utils.astrbot_path import get_astrbot_path
14
15
  from astrbot.core.config.astrbot_config import AstrBotConfig
15
16
  from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
16
- from astrbot.core.platform.register import platform_registry
17
+ from astrbot.core.platform.register import platform_registry, platform_cls_map
17
18
  from astrbot.core.provider.register import provider_registry
18
19
  from astrbot.core.star.star import star_registry
19
- from astrbot.core import logger
20
+ from astrbot.core import logger, file_token_service
20
21
  from astrbot.core.provider import Provider
21
22
  from astrbot.core.provider.provider import RerankProvider
22
23
  import asyncio
@@ -149,6 +150,7 @@ class ConfigRoute(Route):
149
150
  super().__init__(context)
150
151
  self.core_lifecycle = core_lifecycle
151
152
  self.config: AstrBotConfig = core_lifecycle.astrbot_config
153
+ self._logo_token_cache = {} # 缓存logo token,避免重复注册
152
154
  self.acm = core_lifecycle.astrbot_config_mgr
153
155
  self.routes = {
154
156
  "/config/abconf/new": ("POST", self.create_abconf),
@@ -655,6 +657,78 @@ class ConfigRoute(Route):
655
657
  return Response().error(str(e)).__dict__
656
658
  return Response().ok(None, "删除成功,已经实时生效~").__dict__
657
659
 
660
+ async def get_llm_tools(self):
661
+ """获取函数调用工具。包含了本地加载的以及 MCP 服务的工具"""
662
+ tool_mgr = self.core_lifecycle.provider_manager.llm_tools
663
+ tools = tool_mgr.get_func_desc_openai_style()
664
+ return Response().ok(tools).__dict__
665
+
666
+ async def _register_platform_logo(self, platform, platform_default_tmpl):
667
+ """注册平台logo文件并生成访问令牌"""
668
+ if not platform.logo_path:
669
+ return
670
+
671
+ try:
672
+ # 检查缓存
673
+ cache_key = f"{platform.name}:{platform.logo_path}"
674
+ if cache_key in self._logo_token_cache:
675
+ cached_token = self._logo_token_cache[cache_key]
676
+ # 确保platform_default_tmpl[platform.name]存在且为字典
677
+ if platform.name not in platform_default_tmpl:
678
+ platform_default_tmpl[platform.name] = {}
679
+ elif not isinstance(platform_default_tmpl[platform.name], dict):
680
+ platform_default_tmpl[platform.name] = {}
681
+ platform_default_tmpl[platform.name]["logo_token"] = cached_token
682
+ logger.debug(f"Using cached logo token for platform {platform.name}")
683
+ return
684
+
685
+ # 获取平台适配器类
686
+ platform_cls = platform_cls_map.get(platform.name)
687
+ if not platform_cls:
688
+ logger.warning(f"Platform class not found for {platform.name}")
689
+ return
690
+
691
+ # 获取插件目录路径
692
+ module_file = inspect.getfile(platform_cls)
693
+ plugin_dir = os.path.dirname(module_file)
694
+
695
+ # 解析logo文件路径
696
+ logo_file_path = os.path.join(plugin_dir, platform.logo_path)
697
+
698
+ # 检查文件是否存在并注册令牌
699
+ if os.path.exists(logo_file_path):
700
+ logo_token = await file_token_service.register_file(
701
+ logo_file_path, timeout=3600
702
+ )
703
+
704
+ # 确保platform_default_tmpl[platform.name]存在且为字典
705
+ if platform.name not in platform_default_tmpl:
706
+ platform_default_tmpl[platform.name] = {}
707
+ elif not isinstance(platform_default_tmpl[platform.name], dict):
708
+ platform_default_tmpl[platform.name] = {}
709
+
710
+ platform_default_tmpl[platform.name]["logo_token"] = logo_token
711
+
712
+ # 缓存token
713
+ self._logo_token_cache[cache_key] = logo_token
714
+
715
+ logger.debug(f"Logo token registered for platform {platform.name}")
716
+ else:
717
+ logger.warning(
718
+ f"Platform {platform.name} logo file not found: {logo_file_path}"
719
+ )
720
+
721
+ except (ImportError, AttributeError) as e:
722
+ logger.warning(
723
+ f"Failed to import required modules for platform {platform.name}: {e}"
724
+ )
725
+ except (OSError, IOError) as e:
726
+ logger.warning(f"File system error for platform {platform.name} logo: {e}")
727
+ except Exception as e:
728
+ logger.warning(
729
+ f"Unexpected error registering logo for platform {platform.name}: {e}"
730
+ )
731
+
658
732
  async def _get_astrbot_config(self):
659
733
  config = self.config
660
734
 
@@ -662,9 +736,21 @@ class ConfigRoute(Route):
662
736
  platform_default_tmpl = CONFIG_METADATA_2["platform_group"]["metadata"][
663
737
  "platform"
664
738
  ]["config_template"]
739
+
740
+ # 收集需要注册logo的平台
741
+ logo_registration_tasks = []
665
742
  for platform in platform_registry:
666
743
  if platform.default_config_tmpl:
667
744
  platform_default_tmpl[platform.name] = platform.default_config_tmpl
745
+ # 收集logo注册任务
746
+ if platform.logo_path:
747
+ logo_registration_tasks.append(
748
+ self._register_platform_logo(platform, platform_default_tmpl)
749
+ )
750
+
751
+ # 并行执行logo注册
752
+ if logo_registration_tasks:
753
+ await asyncio.gather(*logo_registration_tasks, return_exceptions=True)
668
754
 
669
755
  # 服务提供商的默认配置模板注入
670
756
  provider_default_tmpl = CONFIG_METADATA_2["provider_group"]["metadata"][
@@ -20,6 +20,7 @@ class SessionManagementRoute(Route):
20
20
  core_lifecycle: AstrBotCoreLifecycle,
21
21
  ) -> None:
22
22
  super().__init__(context)
23
+ self.db_helper = db_helper
23
24
  self.routes = {
24
25
  "/session/list": ("GET", self.list_sessions),
25
26
  "/session/update_persona": ("POST", self.update_session_persona),
@@ -39,22 +40,42 @@ class SessionManagementRoute(Route):
39
40
  async def list_sessions(self):
40
41
  """获取所有会话的列表,包括 persona 和 provider 信息"""
41
42
  try:
42
- preferences = await sp.session_get(umo=None, key="sel_conv_id", default=[])
43
- session_conversations = {}
44
- for pref in preferences:
45
- session_conversations[pref.scope_id] = pref.value["val"]
43
+ page = int(request.args.get("page", 1))
44
+ page_size = int(request.args.get("page_size", 20))
45
+ search_query = request.args.get("search", "")
46
+ platform = request.args.get("platform", "")
47
+
48
+ # 获取活跃的会话数据(处于对话内的会话)
49
+ sessions_data, total = await self.db_helper.get_session_conversations(
50
+ page, page_size, search_query, platform
51
+ )
52
+
46
53
  provider_manager = self.core_lifecycle.provider_manager
47
54
  persona_mgr = self.core_lifecycle.persona_mgr
48
55
  personas = persona_mgr.personas_v3
49
56
 
50
57
  sessions = []
51
58
 
52
- # 构建会话信息
53
- for session_id, conversation_id in session_conversations.items():
59
+ # 循环补充非数据库信息,如 provider 和 session 状态
60
+ for data in sessions_data:
61
+ session_id = data["session_id"]
62
+ conversation_id = data["conversation_id"]
63
+ conv_persona_id = data["persona_id"]
64
+ title = data["title"]
65
+ persona_name = data["persona_name"]
66
+
67
+ # 处理 persona 显示
68
+ if conv_persona_id == "[%None]":
69
+ persona_name = "无人格"
70
+ else:
71
+ default_persona = persona_mgr.selected_default_persona_v3
72
+ if default_persona:
73
+ persona_name = default_persona["name"]
74
+
54
75
  session_info = {
55
76
  "session_id": session_id,
56
77
  "conversation_id": conversation_id,
57
- "persona_id": None,
78
+ "persona_id": persona_name,
58
79
  "chat_provider_id": None,
59
80
  "stt_provider_id": None,
60
81
  "tts_provider_id": None,
@@ -79,31 +100,10 @@ class SessionManagementRoute(Route):
79
100
  "session_raw_name": session_id.split(":")[2]
80
101
  if session_id.count(":") >= 2
81
102
  else session_id,
103
+ "title": title,
82
104
  }
83
105
 
84
- # 获取对话信息
85
- conversation = await self.conv_mgr.get_conversation(
86
- unified_msg_origin=session_id, conversation_id=conversation_id
87
- )
88
- if conversation:
89
- session_info["persona_id"] = conversation.persona_id
90
-
91
- # 查找 persona 名称
92
- if conversation.persona_id and conversation.persona_id != "[%None]":
93
- for persona in personas:
94
- if persona["name"] == conversation.persona_id:
95
- session_info["persona_id"] = persona["name"]
96
- break
97
- elif conversation.persona_id == "[%None]":
98
- session_info["persona_id"] = "无人格"
99
- else:
100
- # 使用默认人格
101
- default_persona = persona_mgr.selected_default_persona_v3
102
- if default_persona:
103
- session_info["persona_id"] = default_persona["name"]
104
-
105
106
  # 获取 provider 信息
106
- provider_manager = self.core_lifecycle.provider_manager
107
107
  chat_provider = provider_manager.get_using_provider(
108
108
  provider_type=ProviderType.CHAT_COMPLETION, umo=session_id
109
109
  )
@@ -172,6 +172,14 @@ class SessionManagementRoute(Route):
172
172
  "available_chat_providers": available_chat_providers,
173
173
  "available_stt_providers": available_stt_providers,
174
174
  "available_tts_providers": available_tts_providers,
175
+ "pagination": {
176
+ "page": page,
177
+ "page_size": page_size,
178
+ "total": total,
179
+ "total_pages": (total + page_size - 1) // page_size
180
+ if page_size > 0
181
+ else 0,
182
+ },
175
183
  }
176
184
 
177
185
  return Response().ok(result).__dict__
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: AstrBot
3
- Version: 4.2.1
3
+ Version: 4.3.1
4
4
  Summary: 易上手的多平台 LLM 聊天机器人及开发框架
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.10
@@ -25,10 +25,10 @@ Requires-Dist: docstring-parser>=0.16
25
25
  Requires-Dist: faiss-cpu==1.10.0
26
26
  Requires-Dist: filelock>=3.18.0
27
27
  Requires-Dist: google-genai>=1.14.0
28
- Requires-Dist: googlesearch-python>=1.3.0
29
28
  Requires-Dist: lark-oapi>=1.4.15
30
29
  Requires-Dist: lxml-html-clean>=0.4.2
31
30
  Requires-Dist: mcp>=1.8.0
31
+ Requires-Dist: mi-googlesearch-python==1.3.0.post1
32
32
  Requires-Dist: openai>=1.78.0
33
33
  Requires-Dist: ormsgpack>=1.9.1
34
34
  Requires-Dist: pillow>=11.2.1
@@ -246,7 +246,7 @@ pre-commit install
246
246
  - [koishijs/koishi](https://github.com/koishijs/koishi) - 扩展性极强的 Bot 框架
247
247
  - [MaiM-with-u/MaiBot](https://github.com/MaiM-with-u/MaiBot) - 注重拟人功能的 ChatBot
248
248
  - [langbot-app/LangBot](https://github.com/langbot-app/LangBot) - 功能丰富的 Bot 平台
249
- - [LroMiose/nekro-agent](https://github.com/KroMiose/nekro-agent) - 注重 Agent 的 ChatBot
249
+ - [KroMiose/nekro-agent](https://github.com/KroMiose/nekro-agent) - 注重 Agent 的 ChatBot
250
250
  - [zhenxun-org/zhenxun_bot](https://github.com/zhenxun-org/zhenxun_bot) - 功能完善的 ChatBot
251
251
 
252
252
  ## ⭐ Star History