Compare commits
16 Commits
fix/3815
...
fix/llm-me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
889ce97f2b | ||
|
|
9588d87d11 | ||
|
|
a130db5cf4 | ||
|
|
7faeb5cea8 | ||
|
|
8d3ff61e0d | ||
|
|
4c03e82570 | ||
|
|
e7e8664ab4 | ||
|
|
1dd1623e7d | ||
|
|
80d8161d58 | ||
|
|
fc80d7d681 | ||
|
|
c2f036b27c | ||
|
|
4087bbb512 | ||
|
|
e1c728582d | ||
|
|
93c69a639a | ||
|
|
a7fdc98b29 | ||
|
|
85b7f104df |
@@ -1 +1 @@
|
|||||||
__version__ = "4.7.1"
|
__version__ = "4.7.3"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
from typing import Any, ClassVar, Literal, cast
|
from typing import Any, ClassVar, Literal, cast
|
||||||
|
|
||||||
from pydantic import BaseModel, GetCoreSchemaHandler
|
from pydantic import BaseModel, GetCoreSchemaHandler, model_validator
|
||||||
from pydantic_core import core_schema
|
from pydantic_core import core_schema
|
||||||
|
|
||||||
|
|
||||||
@@ -145,22 +145,39 @@ class Message(BaseModel):
|
|||||||
"tool",
|
"tool",
|
||||||
]
|
]
|
||||||
|
|
||||||
content: str | list[ContentPart]
|
content: str | list[ContentPart] | None = None
|
||||||
"""The content of the message."""
|
"""The content of the message."""
|
||||||
|
|
||||||
|
tool_calls: list[ToolCall] | list[dict] | None = None
|
||||||
|
"""The tool calls of the message."""
|
||||||
|
|
||||||
|
tool_call_id: str | None = None
|
||||||
|
"""The ID of the tool call."""
|
||||||
|
|
||||||
|
@model_validator(mode="after")
|
||||||
|
def check_content_required(self):
|
||||||
|
# assistant + tool_calls is not None: allow content to be None
|
||||||
|
if self.role == "assistant" and self.tool_calls is not None:
|
||||||
|
return self
|
||||||
|
|
||||||
|
# other all cases: content is required
|
||||||
|
if self.content is None:
|
||||||
|
raise ValueError(
|
||||||
|
"content is required unless role='assistant' and tool_calls is not None"
|
||||||
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class AssistantMessageSegment(Message):
|
class AssistantMessageSegment(Message):
|
||||||
"""A message segment from the assistant."""
|
"""A message segment from the assistant."""
|
||||||
|
|
||||||
role: Literal["assistant"] = "assistant"
|
role: Literal["assistant"] = "assistant"
|
||||||
tool_calls: list[ToolCall] | list[dict] | None = None
|
|
||||||
|
|
||||||
|
|
||||||
class ToolCallMessageSegment(Message):
|
class ToolCallMessageSegment(Message):
|
||||||
"""A message segment representing a tool call."""
|
"""A message segment representing a tool call."""
|
||||||
|
|
||||||
role: Literal["tool"] = "tool"
|
role: Literal["tool"] = "tool"
|
||||||
tool_call_id: str
|
|
||||||
|
|
||||||
|
|
||||||
class UserMessageSegment(Message):
|
class UserMessageSegment(Message):
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import os
|
|||||||
|
|
||||||
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
||||||
|
|
||||||
VERSION = "4.7.1"
|
VERSION = "4.7.3"
|
||||||
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
|
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
|
||||||
|
|
||||||
# 默认配置
|
# 默认配置
|
||||||
@@ -90,6 +90,7 @@ DEFAULT_CONFIG = {
|
|||||||
"group_icl_enable": False,
|
"group_icl_enable": False,
|
||||||
"group_message_max_cnt": 300,
|
"group_message_max_cnt": 300,
|
||||||
"image_caption": False,
|
"image_caption": False,
|
||||||
|
"image_caption_provider_id": "",
|
||||||
"active_reply": {
|
"active_reply": {
|
||||||
"enable": False,
|
"enable": False,
|
||||||
"method": "possibility_reply",
|
"method": "possibility_reply",
|
||||||
@@ -2109,6 +2110,9 @@ CONFIG_METADATA_2 = {
|
|||||||
"image_caption": {
|
"image_caption": {
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
},
|
},
|
||||||
|
"image_caption_provider_id": {
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
"image_caption_prompt": {
|
"image_caption_prompt": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
@@ -2785,7 +2789,16 @@ CONFIG_METADATA_3 = {
|
|||||||
"provider_ltm_settings.image_caption": {
|
"provider_ltm_settings.image_caption": {
|
||||||
"description": "自动理解图片",
|
"description": "自动理解图片",
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"hint": "需要设置默认图片转述模型。",
|
"hint": "需要设置群聊图片转述模型。",
|
||||||
|
},
|
||||||
|
"provider_ltm_settings.image_caption_provider_id": {
|
||||||
|
"description": "群聊图片转述模型",
|
||||||
|
"type": "string",
|
||||||
|
"_special": "select_provider",
|
||||||
|
"hint": "用于群聊上下文感知的图片理解,与默认图片转述模型分开配置。",
|
||||||
|
"condition": {
|
||||||
|
"provider_ltm_settings.image_caption": True,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"provider_ltm_settings.active_reply.enable": {
|
"provider_ltm_settings.active_reply.enable": {
|
||||||
"description": "主动回复",
|
"description": "主动回复",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import asyncio
|
|||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from astrbot.core import logger
|
from astrbot.core import astrbot_config, logger
|
||||||
from astrbot.core.agent.runners.coze.coze_agent_runner import CozeAgentRunner
|
from astrbot.core.agent.runners.coze.coze_agent_runner import CozeAgentRunner
|
||||||
from astrbot.core.agent.runners.dashscope.dashscope_agent_runner import (
|
from astrbot.core.agent.runners.dashscope.dashscope_agent_runner import (
|
||||||
DashscopeAgentRunner,
|
DashscopeAgentRunner,
|
||||||
@@ -88,7 +88,7 @@ class ThirdPartyAgentSubStage(Stage):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.prov_cfg: dict = next(
|
self.prov_cfg: dict = next(
|
||||||
(p for p in self.conf["provider"] if p["id"] == self.prov_id),
|
(p for p in astrbot_config["provider"] if p["id"] == self.prov_id),
|
||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
if not self.prov_id:
|
if not self.prov_id:
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ class DingtalkPlatformAdapter(Platform):
|
|||||||
|
|
||||||
async def terminate(self):
|
async def terminate(self):
|
||||||
def monkey_patch_close():
|
def monkey_patch_close():
|
||||||
raise Exception("Graceful shutdown")
|
raise KeyboardInterrupt("Graceful shutdown")
|
||||||
|
|
||||||
self.client_.open_connection = monkey_patch_close
|
self.client_.open_connection = monkey_patch_close
|
||||||
await self.client_.websocket.close(code=1000, reason="Graceful shutdown")
|
await self.client_.websocket.close(code=1000, reason="Graceful shutdown")
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import ssl
|
import ssl
|
||||||
@@ -19,6 +20,10 @@ from astrbot.core.star.star_manager import PluginManager
|
|||||||
|
|
||||||
from .route import Response, Route, RouteContext
|
from .route import Response, Route, RouteContext
|
||||||
|
|
||||||
|
PLUGIN_UPDATE_CONCURRENCY = (
|
||||||
|
3 # limit concurrent updates to avoid overwhelming plugin sources
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PluginRoute(Route):
|
class PluginRoute(Route):
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -33,6 +38,7 @@ class PluginRoute(Route):
|
|||||||
"/plugin/install": ("POST", self.install_plugin),
|
"/plugin/install": ("POST", self.install_plugin),
|
||||||
"/plugin/install-upload": ("POST", self.install_plugin_upload),
|
"/plugin/install-upload": ("POST", self.install_plugin_upload),
|
||||||
"/plugin/update": ("POST", self.update_plugin),
|
"/plugin/update": ("POST", self.update_plugin),
|
||||||
|
"/plugin/update-all": ("POST", self.update_all_plugins),
|
||||||
"/plugin/uninstall": ("POST", self.uninstall_plugin),
|
"/plugin/uninstall": ("POST", self.uninstall_plugin),
|
||||||
"/plugin/market_list": ("GET", self.get_online_plugins),
|
"/plugin/market_list": ("GET", self.get_online_plugins),
|
||||||
"/plugin/off": ("POST", self.off_plugin),
|
"/plugin/off": ("POST", self.off_plugin),
|
||||||
@@ -63,7 +69,7 @@ class PluginRoute(Route):
|
|||||||
.__dict__
|
.__dict__
|
||||||
)
|
)
|
||||||
|
|
||||||
data = await request.json
|
data = await request.get_json()
|
||||||
plugin_name = data.get("name", None)
|
plugin_name = data.get("name", None)
|
||||||
try:
|
try:
|
||||||
success, message = await self.plugin_manager.reload(plugin_name)
|
success, message = await self.plugin_manager.reload(plugin_name)
|
||||||
@@ -346,7 +352,7 @@ class PluginRoute(Route):
|
|||||||
.__dict__
|
.__dict__
|
||||||
)
|
)
|
||||||
|
|
||||||
post_data = await request.json
|
post_data = await request.get_json()
|
||||||
repo_url = post_data["url"]
|
repo_url = post_data["url"]
|
||||||
|
|
||||||
proxy: str = post_data.get("proxy", None)
|
proxy: str = post_data.get("proxy", None)
|
||||||
@@ -393,7 +399,7 @@ class PluginRoute(Route):
|
|||||||
.__dict__
|
.__dict__
|
||||||
)
|
)
|
||||||
|
|
||||||
post_data = await request.json
|
post_data = await request.get_json()
|
||||||
plugin_name = post_data["name"]
|
plugin_name = post_data["name"]
|
||||||
delete_config = post_data.get("delete_config", False)
|
delete_config = post_data.get("delete_config", False)
|
||||||
delete_data = post_data.get("delete_data", False)
|
delete_data = post_data.get("delete_data", False)
|
||||||
@@ -418,7 +424,7 @@ class PluginRoute(Route):
|
|||||||
.__dict__
|
.__dict__
|
||||||
)
|
)
|
||||||
|
|
||||||
post_data = await request.json
|
post_data = await request.get_json()
|
||||||
plugin_name = post_data["name"]
|
plugin_name = post_data["name"]
|
||||||
proxy: str = post_data.get("proxy", None)
|
proxy: str = post_data.get("proxy", None)
|
||||||
try:
|
try:
|
||||||
@@ -432,6 +438,59 @@ class PluginRoute(Route):
|
|||||||
logger.error(f"/api/plugin/update: {traceback.format_exc()}")
|
logger.error(f"/api/plugin/update: {traceback.format_exc()}")
|
||||||
return Response().error(str(e)).__dict__
|
return Response().error(str(e)).__dict__
|
||||||
|
|
||||||
|
async def update_all_plugins(self):
|
||||||
|
if DEMO_MODE:
|
||||||
|
return (
|
||||||
|
Response()
|
||||||
|
.error("You are not permitted to do this operation in demo mode")
|
||||||
|
.__dict__
|
||||||
|
)
|
||||||
|
|
||||||
|
post_data = await request.get_json()
|
||||||
|
plugin_names: list[str] = post_data.get("names") or []
|
||||||
|
proxy: str = post_data.get("proxy", "")
|
||||||
|
|
||||||
|
if not isinstance(plugin_names, list) or not plugin_names:
|
||||||
|
return Response().error("插件列表不能为空").__dict__
|
||||||
|
|
||||||
|
results = []
|
||||||
|
sem = asyncio.Semaphore(PLUGIN_UPDATE_CONCURRENCY)
|
||||||
|
|
||||||
|
async def _update_one(name: str):
|
||||||
|
async with sem:
|
||||||
|
try:
|
||||||
|
logger.info(f"批量更新插件 {name}")
|
||||||
|
await self.plugin_manager.update_plugin(name, proxy)
|
||||||
|
return {"name": name, "status": "ok", "message": "更新成功"}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
f"/api/plugin/update-all: 更新插件 {name} 失败: {traceback.format_exc()}",
|
||||||
|
)
|
||||||
|
return {"name": name, "status": "error", "message": str(e)}
|
||||||
|
|
||||||
|
raw_results = await asyncio.gather(
|
||||||
|
*(_update_one(name) for name in plugin_names),
|
||||||
|
return_exceptions=True,
|
||||||
|
)
|
||||||
|
for name, result in zip(plugin_names, raw_results):
|
||||||
|
if isinstance(result, asyncio.CancelledError):
|
||||||
|
raise result
|
||||||
|
if isinstance(result, BaseException):
|
||||||
|
results.append(
|
||||||
|
{"name": name, "status": "error", "message": str(result)}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
results.append(result)
|
||||||
|
|
||||||
|
failed = [r for r in results if r["status"] == "error"]
|
||||||
|
message = (
|
||||||
|
"批量更新完成,全部成功。"
|
||||||
|
if not failed
|
||||||
|
else f"批量更新完成,其中 {len(failed)}/{len(results)} 个插件失败。"
|
||||||
|
)
|
||||||
|
|
||||||
|
return Response().ok({"results": results}, message).__dict__
|
||||||
|
|
||||||
async def off_plugin(self):
|
async def off_plugin(self):
|
||||||
if DEMO_MODE:
|
if DEMO_MODE:
|
||||||
return (
|
return (
|
||||||
@@ -440,7 +499,7 @@ class PluginRoute(Route):
|
|||||||
.__dict__
|
.__dict__
|
||||||
)
|
)
|
||||||
|
|
||||||
post_data = await request.json
|
post_data = await request.get_json()
|
||||||
plugin_name = post_data["name"]
|
plugin_name = post_data["name"]
|
||||||
try:
|
try:
|
||||||
await self.plugin_manager.turn_off_plugin(plugin_name)
|
await self.plugin_manager.turn_off_plugin(plugin_name)
|
||||||
@@ -458,7 +517,7 @@ class PluginRoute(Route):
|
|||||||
.__dict__
|
.__dict__
|
||||||
)
|
)
|
||||||
|
|
||||||
post_data = await request.json
|
post_data = await request.get_json()
|
||||||
plugin_name = post_data["name"]
|
plugin_name = post_data["name"]
|
||||||
try:
|
try:
|
||||||
await self.plugin_manager.turn_on_plugin(plugin_name)
|
await self.plugin_manager.turn_on_plugin(plugin_name)
|
||||||
|
|||||||
25
changelogs/v4.7.3.md
Normal file
25
changelogs/v4.7.3.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
## What's Changed
|
||||||
|
|
||||||
|
1. 修复使用非默认配置文件情况下时,第三方 Agent Runner (Dify、Coze、阿里云百炼应用等)无法正常工作的问题
|
||||||
|
2. 修复当“聊天模型”未设置,并且模型提供商中仅有 Agent Runner 时,无法正常使用 Agent Runner 的问题
|
||||||
|
3. 修复部分情况下报错 `pydantic_core._pydantic_core.ValidationError: 1 validation error for Message content` 的问题
|
||||||
|
4. 新增群聊模式下的专用图片转述模型配置 ([#3822](https://github.com/AstrBotDevs/AstrBot/issues/3822))
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
重构:
|
||||||
|
- 将 Dify、Coze、阿里云百炼应用等 LLMOps 提供商迁移到 Agent 执行器层,理清和本地 Agent 执行器的边界。详见:[Agent 执行器](https://docs.astrbot.app/use/agent-runner.html)
|
||||||
|
- 将「会话管理」功能重构为「自定义规则」功能,理清和多配置文件功能的边界。详见:[自定义规则](https://docs.astrbot.app/use/custom-rules.html)
|
||||||
|
|
||||||
|
优化:
|
||||||
|
- Dify、阿里云百炼应用支持流式输出
|
||||||
|
- 防止分段回复正则表达式解析错误导致消息不发送
|
||||||
|
- 群聊上下文感知记录 At 信息
|
||||||
|
- 优化模型提供商页面的测试提供商功能
|
||||||
|
|
||||||
|
新增:
|
||||||
|
- 支持在配置文件页面快速测试对话
|
||||||
|
- 为配置文件配置项内容添加国际化支持
|
||||||
|
|
||||||
|
修复:
|
||||||
|
- 在更新 MCP Server 配置后,MCP 无法正常重启的问题
|
||||||
@@ -379,7 +379,11 @@
|
|||||||
},
|
},
|
||||||
"image_caption": {
|
"image_caption": {
|
||||||
"description": "Auto-understand Images",
|
"description": "Auto-understand Images",
|
||||||
"hint": "Requires setting a default image caption model."
|
"hint": "Requires setting a group chat image caption model."
|
||||||
|
},
|
||||||
|
"image_caption_provider_id": {
|
||||||
|
"description": "Group Chat Image Caption Model",
|
||||||
|
"hint": "Used for image understanding in group chat context awareness, configured separately from the default image caption model."
|
||||||
},
|
},
|
||||||
"active_reply": {
|
"active_reply": {
|
||||||
"enable": {
|
"enable": {
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"selectFile": "Select File",
|
"selectFile": "Select File",
|
||||||
"refresh": "Refresh"
|
"refresh": "Refresh",
|
||||||
|
"updateAll": "Update All"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"enabled": "Enabled",
|
"enabled": "Enabled",
|
||||||
@@ -141,7 +142,9 @@
|
|||||||
"confirmDelete": "Are you sure you want to delete this extension?",
|
"confirmDelete": "Are you sure you want to delete this extension?",
|
||||||
"fillUrlOrFile": "Please fill in extension URL or upload extension file",
|
"fillUrlOrFile": "Please fill in extension URL or upload extension file",
|
||||||
"dontFillBoth": "Please don't fill in both extension URL and upload file",
|
"dontFillBoth": "Please don't fill in both extension URL and upload file",
|
||||||
"supportedFormats": "Supports .zip extension files"
|
"supportedFormats": "Supports .zip extension files",
|
||||||
|
"updateAllSuccess": "All upgradable extensions have been updated!",
|
||||||
|
"updateAllFailed": "{failed} of {total} extensions failed to update:"
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
"fromFile": "Install from File",
|
"fromFile": "Install from File",
|
||||||
|
|||||||
@@ -379,7 +379,11 @@
|
|||||||
},
|
},
|
||||||
"image_caption": {
|
"image_caption": {
|
||||||
"description": "自动理解图片",
|
"description": "自动理解图片",
|
||||||
"hint": "需要设置默认图片转述模型。"
|
"hint": "需要设置群聊图片转述模型。"
|
||||||
|
},
|
||||||
|
"image_caption_provider_id": {
|
||||||
|
"description": "群聊图片转述模型",
|
||||||
|
"hint": "用于群聊上下文感知的图片理解,与默认图片转述模型分开配置。"
|
||||||
},
|
},
|
||||||
"active_reply": {
|
"active_reply": {
|
||||||
"enable": {
|
"enable": {
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
"actions": "操作",
|
"actions": "操作",
|
||||||
"back": "返回",
|
"back": "返回",
|
||||||
"selectFile": "选择文件",
|
"selectFile": "选择文件",
|
||||||
"refresh": "刷新"
|
"refresh": "刷新",
|
||||||
|
"updateAll": "更新全部插件"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"enabled": "启用",
|
"enabled": "启用",
|
||||||
@@ -141,7 +142,9 @@
|
|||||||
"confirmDelete": "确定要删除插件吗?",
|
"confirmDelete": "确定要删除插件吗?",
|
||||||
"fillUrlOrFile": "请填写插件链接或上传插件文件",
|
"fillUrlOrFile": "请填写插件链接或上传插件文件",
|
||||||
"dontFillBoth": "请不要同时填写插件链接和上传文件",
|
"dontFillBoth": "请不要同时填写插件链接和上传文件",
|
||||||
"supportedFormats": "支持 .zip 格式的插件文件"
|
"supportedFormats": "支持 .zip 格式的插件文件",
|
||||||
|
"updateAllSuccess": "所有可更新的插件都已更新!",
|
||||||
|
"updateAllFailed": "有 {failed}/{total} 个插件更新失败:"
|
||||||
},
|
},
|
||||||
"upload": {
|
"upload": {
|
||||||
"fromFile": "从文件安装",
|
"fromFile": "从文件安装",
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const loadingDialog = reactive({
|
|||||||
const showPluginInfoDialog = ref(false);
|
const showPluginInfoDialog = ref(false);
|
||||||
const selectedPlugin = ref({});
|
const selectedPlugin = ref({});
|
||||||
const curr_namespace = ref("");
|
const curr_namespace = ref("");
|
||||||
|
const updatingAll = ref(false);
|
||||||
|
|
||||||
const readmeDialog = reactive({
|
const readmeDialog = reactive({
|
||||||
show: false,
|
show: false,
|
||||||
@@ -226,6 +227,10 @@ const paginatedPlugins = computed(() => {
|
|||||||
return sortedPlugins.value.slice(start, end);
|
return sortedPlugins.value.slice(start, end);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const updatableExtensions = computed(() => {
|
||||||
|
return extension_data?.data?.filter(ext => ext.has_update) || [];
|
||||||
|
});
|
||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
const toggleShowReserved = () => {
|
const toggleShowReserved = () => {
|
||||||
showReserved.value = !showReserved.value;
|
showReserved.value = !showReserved.value;
|
||||||
@@ -372,6 +377,56 @@ const updateExtension = async (extension_name) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateAllExtensions = async () => {
|
||||||
|
if (updatingAll.value || updatableExtensions.value.length === 0) return;
|
||||||
|
updatingAll.value = true;
|
||||||
|
loadingDialog.title = tm('status.loading');
|
||||||
|
loadingDialog.statusCode = 0;
|
||||||
|
loadingDialog.result = "";
|
||||||
|
loadingDialog.show = true;
|
||||||
|
|
||||||
|
const targets = updatableExtensions.value.map(ext => ext.name);
|
||||||
|
try {
|
||||||
|
const res = await axios.post('/api/plugin/update-all', {
|
||||||
|
names: targets,
|
||||||
|
proxy: localStorage.getItem('selectedGitHubProxy') || ""
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.data.status === "error") {
|
||||||
|
onLoadingDialogResult(2, res.data.message || tm('messages.updateAllFailed', {
|
||||||
|
failed: targets.length,
|
||||||
|
total: targets.length
|
||||||
|
}), -1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = res.data.data?.results || [];
|
||||||
|
const failures = results.filter(r => r.status !== 'ok');
|
||||||
|
try {
|
||||||
|
await getExtensions();
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err.response?.data?.message || err.message || String(err);
|
||||||
|
failures.push({ name: 'refresh', status: 'error', message: errorMsg });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures.length === 0) {
|
||||||
|
onLoadingDialogResult(1, tm('messages.updateAllSuccess'));
|
||||||
|
} else {
|
||||||
|
const failureText = tm('messages.updateAllFailed', {
|
||||||
|
failed: failures.length,
|
||||||
|
total: targets.length
|
||||||
|
});
|
||||||
|
const detail = failures.map(f => `${f.name}: ${f.message}`).join('\n');
|
||||||
|
onLoadingDialogResult(2, `${failureText}\n${detail}`, -1);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err.response?.data?.message || err.message || String(err);
|
||||||
|
onLoadingDialogResult(2, errorMsg, -1);
|
||||||
|
} finally {
|
||||||
|
updatingAll.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const pluginOn = async (extension) => {
|
const pluginOn = async (extension) => {
|
||||||
try {
|
try {
|
||||||
const res = await axios.post('/api/plugin/on', { name: extension.name });
|
const res = await axios.post('/api/plugin/on', { name: extension.name });
|
||||||
@@ -720,6 +775,12 @@ watch(marketSearch, (newVal) => {
|
|||||||
{{ showReserved ? tm('buttons.hideSystemPlugins') : tm('buttons.showSystemPlugins') }}
|
{{ showReserved ? tm('buttons.hideSystemPlugins') : tm('buttons.showSystemPlugins') }}
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
|
||||||
|
<v-btn class="ml-2" color="warning" variant="tonal" :disabled="updatableExtensions.length === 0"
|
||||||
|
:loading="updatingAll" @click="updateAllExtensions">
|
||||||
|
<v-icon>mdi-update</v-icon>
|
||||||
|
{{ tm('buttons.updateAll') }}
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
<v-btn class="ml-2" color="primary" variant="tonal" @click="dialog = true">
|
<v-btn class="ml-2" color="primary" variant="tonal" @click="dialog = true">
|
||||||
<v-icon>mdi-plus</v-icon>
|
<v-icon>mdi-plus</v-icon>
|
||||||
{{ tm('buttons.install') }}
|
{{ tm('buttons.install') }}
|
||||||
|
|||||||
@@ -30,16 +30,13 @@ class LongTermMemory:
|
|||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
max_cnt = 300
|
max_cnt = 300
|
||||||
image_caption = (
|
|
||||||
True
|
|
||||||
if cfg["provider_settings"]["default_image_caption_provider_id"]
|
|
||||||
and cfg["provider_ltm_settings"]["image_caption"]
|
|
||||||
else False
|
|
||||||
)
|
|
||||||
image_caption_prompt = cfg["provider_settings"]["image_caption_prompt"]
|
image_caption_prompt = cfg["provider_settings"]["image_caption_prompt"]
|
||||||
image_caption_provider_id = cfg["provider_settings"][
|
image_caption_provider_id = cfg["provider_ltm_settings"].get(
|
||||||
"default_image_caption_provider_id"
|
"image_caption_provider_id"
|
||||||
]
|
)
|
||||||
|
image_caption = cfg["provider_ltm_settings"]["image_caption"] and bool(
|
||||||
|
image_caption_provider_id
|
||||||
|
)
|
||||||
active_reply = cfg["provider_ltm_settings"]["active_reply"]
|
active_reply = cfg["provider_ltm_settings"]["active_reply"]
|
||||||
enable_active_reply = active_reply.get("enable", False)
|
enable_active_reply = active_reply.get("enable", False)
|
||||||
ar_method = active_reply["method"]
|
ar_method = active_reply["method"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "AstrBot"
|
name = "AstrBot"
|
||||||
version = "4.7.1"
|
version = "4.7.3"
|
||||||
description = "Easy-to-use multi-platform LLM chatbot and development framework"
|
description = "Easy-to-use multi-platform LLM chatbot and development framework"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
Reference in New Issue
Block a user