Compare commits

...

9 Commits

Author SHA1 Message Date
Soulter dd5a02e8ef chore: bump version to 4.3.2 2025-10-05 01:01:13 +08:00
Soulter 3211ec57ee fix: handle Google search initialization and errors gracefully 2025-10-05 00:55:47 +08:00
Soulter 6796afdaee fix: googlesearch 2025-10-05 00:54:24 +08:00
Soulter cc6fe57773 fix: on_tool_end无法获得工具返回的结果 (#2956)
fixes: #2940
2025-10-05 00:37:51 +08:00
Soulter 1dfc831938 fix: 修复 reset 没有清除群聊上下文感知数据的问题 (#2954) 2025-10-05 00:05:42 +08:00
Futureppo cafeda4abf feat: 为插件市场的搜索增加拼音与首字母搜索功能 (#2936)
* 更新 package.json

* 更新 ExtensionPage.vue
2025-10-03 09:42:57 +08:00
Soulter d951b99718 fix: 发送阶段将 Plain 为空的消息段移除 2025-10-03 00:45:07 +08:00
Soulter 0ad87209e5 chore: bump version to 4.3.1 2025-10-02 17:25:09 +08:00
Soulter 1b50c5404d fix: enhance knowledge base plugin status check to handle empty data response 2025-10-02 17:25:00 +08:00
13 changed files with 84 additions and 17 deletions
@@ -198,6 +198,17 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
func_tool = req.func_tool.get_func(func_tool_name)
logger.info(f"使用工具:{func_tool_name},参数:{func_tool_args}")
if not func_tool:
logger.warning(f"未找到指定的工具: {func_tool_name},将跳过。")
tool_call_result_blocks.append(
ToolCallMessageSegment(
role="tool",
tool_call_id=func_tool_id,
content=f"error: 未找到工具 {func_tool_name}",
)
)
continue
try:
await self.agent_hooks.on_tool_start(
self.run_context, func_tool, func_tool_args
@@ -210,9 +221,12 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
run_context=self.run_context,
**func_tool_args,
)
async for resp in executor:
_final_resp: CallToolResult | None = None
async for resp in executor: # type: ignore
if isinstance(resp, CallToolResult):
res = resp
_final_resp = resp
if isinstance(res.content[0], TextContent):
tool_call_result_blocks.append(
ToolCallMessageSegment(
@@ -279,13 +293,14 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
chain=res.chain, type="tool_direct_result"
)
else:
# 不应该出现其他类型
logger.warning(
f"Tool 返回了不支持的类型: {type(resp)},将忽略。"
)
try:
await self.agent_hooks.on_tool_end(
self.run_context, func_tool, func_tool_args, None
self.run_context, func_tool, func_tool_args, _final_resp
)
except Exception as e:
logger.error(f"Error in on_tool_end hook: {e}", exc_info=True)
+1 -1
View File
@@ -6,7 +6,7 @@ import os
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
VERSION = "4.3.0"
VERSION = "4.3.2"
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
# 默认配置
+10
View File
@@ -190,6 +190,16 @@ class RespondStage(Stage):
except Exception as e:
logger.warning(f"空内容检查异常: {e}")
# 将 Plain 为空的消息段移除
result.chain = [
comp
for comp in result.chain
if not (
isinstance(comp, Comp.Plain)
and (not comp.text or not comp.text.strip())
)
]
# 发送消息链
# Record 需要强制单独发送
need_separately = {ComponentType.Record}
+1
View File
@@ -0,0 +1 @@
# What's Changed
+7
View File
@@ -0,0 +1,7 @@
# What's Changed
1. fix: 修复 /reset 指令没有清除群聊上下文感知数据的问题 ([#2954](https://github.com/AstrBotDevs/AstrBot/issues/2954))
2. fix: 修复自带的 WebSearch 插件可能在部分场景下无法使用的问题
3. fix: 发送阶段强行将 Plain 为空的消息段移除
4. fix: on_tool_end无法获得工具返回的结果 ([#2956](https://github.com/AstrBotDevs/AstrBot/issues/2956))
5. feat: 为插件市场的搜索增加拼音与首字母搜索功能 ([#2936](https://github.com/AstrBotDevs/AstrBot/issues/2936))
+1
View File
@@ -27,6 +27,7 @@
"lodash": "4.17.21",
"marked": "^15.0.7",
"markdown-it": "^14.1.0",
"pinyin-pro": "^3.26.0",
"pinia": "2.1.6",
"remixicon": "3.5.0",
"vee-validate": "4.11.3",
+28 -1
View File
@@ -5,6 +5,7 @@ import ConsoleDisplayer from '@/components/shared/ConsoleDisplayer.vue';
import ReadmeDialog from '@/components/shared/ReadmeDialog.vue';
import ProxySelector from '@/components/shared/ProxySelector.vue';
import axios from 'axios';
import { pinyin } from 'pinyin-pro';
import { useCommonStore } from '@/stores/common';
import { useI18n, useModuleI18n } from '@/i18n/composables';
@@ -65,6 +66,32 @@ const marketSearch = ref("");
const filterKeys = ['name', 'desc', 'author'];
const refreshingMarket = ref(false);
// 插件市场拼音搜索
const normalizeStr = (s) => (s ?? '').toString().toLowerCase().trim();
const toPinyinText = (s) => pinyin(s ?? '', { toneType: 'none' }).toLowerCase().replace(/\s+/g, '');
const toInitials = (s) => pinyin(s ?? '', { pattern: 'first', toneType: 'none' }).toLowerCase().replace(/\s+/g, '');
const marketCustomFilter = (value, query, item) => {
const q = normalizeStr(query);
if (!q) return true;
const candidates = new Set();
if (value != null) candidates.add(String(value));
if (item?.name) candidates.add(String(item.name));
if (item?.trimmedName) candidates.add(String(item.trimmedName));
if (item?.desc) candidates.add(String(item.desc));
if (item?.author) candidates.add(String(item.author));
for (const v of candidates) {
const nv = normalizeStr(v);
if (nv.includes(q)) return true;
const pv = toPinyinText(v);
if (pv.includes(q)) return true;
const iv = toInitials(v);
if (iv.includes(q)) return true;
}
return false;
};
const plugin_handler_info_headers = computed(() => [
{ title: tm('table.headers.eventType'), key: 'event_type_h' },
{ title: tm('table.headers.description'), key: 'desc', maxWidth: '250px' },
@@ -772,7 +799,7 @@ onMounted(async () => {
<v-col cols="12" md="12" style="padding: 0px;">
<v-data-table :headers="pluginMarketHeaders" :items="pluginMarketData" item-key="name"
:loading="loading_" v-model:search="marketSearch" :filter-keys="filterKeys">
:loading="loading_" v-model:search="marketSearch" :filter-keys="filterKeys" :custom-filter="marketCustomFilter">
<template v-slot:item.name="{ item }">
<div class="d-flex align-center"
style="overflow-x: auto; scrollbar-width: thin; scrollbar-track-color: transparent;">
+2 -2
View File
@@ -601,11 +601,11 @@ export default {
checkPlugin() {
axios.get('/api/plugin/get?name=astrbot_plugin_knowledge_base')
.then(response => {
if (response.data.status !== 'ok') {
if (response.data.status !== 'ok' || response.data.data.length === 0) {
this.showSnackbar(this.tm('messages.pluginNotAvailable'), 'error');
return
}
if (!response.data.data.activated) {
if (!response.data.data[0].activated) {
this.showSnackbar(this.tm('messages.pluginNotActivated'), 'error');
return
}
+2 -1
View File
@@ -6,6 +6,7 @@ from astrbot.core.platform.message_type import MessageType
from astrbot.core.provider.sources.dify_source import ProviderDify
from astrbot.core.provider.sources.coze_source import ProviderCoze
from astrbot.api import sp, logger
from ..long_term_memory import LongTermMemory
from typing import Union
from enum import Enum
@@ -36,7 +37,7 @@ class RstScene(Enum):
class ConversationCommands:
def __init__(self, context: star.Context, ltm=None):
def __init__(self, context: star.Context, ltm: LongTermMemory | None = None):
self.context = context
self.ltm = ltm
+1 -1
View File
@@ -41,7 +41,7 @@ class Main(star.Star):
self.tool_c = ToolCommands(self.context)
self.plugin_c = PluginCommands(self.context)
self.admin_c = AdminCommands(self.context)
self.conversation_c = ConversationCommands(self.context)
self.conversation_c = ConversationCommands(self.context, self.ltm)
self.provider_c = ProviderCommands(self.context)
self.persona_c = PersonaCommands(self.context)
self.alter_cmd_c = AlterCmdCommands(self.context)
+3 -3
View File
@@ -1,5 +1,5 @@
import os
from googlesearch import search
from googlesearch.asearch import asearch
from . import SearchEngine, SearchResult
@@ -14,14 +14,14 @@ class Google(SearchEngine):
async def search(self, query: str, num_results: int) -> List[SearchResult]:
results = []
try:
ls = search(
ls = asearch(
query,
advanced=True,
num_results=num_results,
timeout=3,
proxy=self.proxy,
)
for i in ls:
async for i in ls:
results.append(
SearchResult(title=i.title, url=i.url, snippet=i.description)
)
+10 -5
View File
@@ -46,7 +46,11 @@ class Main(star.Star):
self.bing_search = Bing()
self.sogo_search = Sogo()
self.google = Google()
self.google = None
try:
self.google = Google()
except Exception as e:
logger.error(f"google search init error: {e}, disable google search")
async def _tidy_text(self, text: str) -> str:
"""清理文本,去除空格、换行符等"""
@@ -89,10 +93,11 @@ class Main(star.Star):
self, query, num_results: int = 5
) -> list[SearchResult]:
results = []
try:
results = await self.google.search(query, num_results)
except Exception as e:
logger.error(f"google search error: {e}, try the next one...")
if self.google:
try:
results = await self.google.search(query, num_results)
except Exception as e:
logger.error(f"google search error: {e}, try the next one...")
if len(results) == 0:
logger.debug("search google failed")
try:
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "AstrBot"
version = "4.3.0"
version = "4.3.2"
description = "易上手的多平台 LLM 聊天机器人及开发框架"
readme = "README.md"
requires-python = ">=3.10"