Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd5a02e8ef | |||
| 3211ec57ee | |||
| 6796afdaee | |||
| cc6fe57773 | |||
| 1dfc831938 | |||
| cafeda4abf | |||
| d951b99718 | |||
| 0ad87209e5 | |||
| 1b50c5404d |
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
# 默认配置
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# What's Changed
|
||||
@@ -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))
|
||||
@@ -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",
|
||||
|
||||
@@ -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;">
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "AstrBot"
|
||||
version = "4.3.0"
|
||||
version = "4.3.2"
|
||||
description = "易上手的多平台 LLM 聊天机器人及开发框架"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
Reference in New Issue
Block a user