Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9cdf47603 | |||
| 55ac878648 | |||
| 60abddada3 | |||
| bbc583cc8d | |||
| 7906030037 | |||
| 06b385697d | |||
| 059008a903 | |||
| 97c9e95211 |
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install UV
|
||||
run: pip install uv
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "4.17.2"
|
||||
__version__ = "4.17.3"
|
||||
|
||||
@@ -42,10 +42,10 @@ from astrbot.core.message.components import File, Image, Reply
|
||||
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
||||
from astrbot.core.provider import Provider
|
||||
from astrbot.core.provider.entities import ProviderRequest
|
||||
from astrbot.core.provider.manager import llm_tools
|
||||
from astrbot.core.skills.skill_manager import SkillManager, build_skills_prompt
|
||||
from astrbot.core.star.context import Context
|
||||
from astrbot.core.star.star_handler import star_map
|
||||
from astrbot.core.provider.manager import llm_tools
|
||||
from astrbot.core.tools.cron_tools import (
|
||||
CREATE_CRON_JOB_TOOL,
|
||||
DELETE_CRON_JOB_TOOL,
|
||||
|
||||
@@ -5,8 +5,9 @@ import mcp
|
||||
from astrbot.api import FunctionTool
|
||||
from astrbot.core.agent.run_context import ContextWrapper
|
||||
from astrbot.core.agent.tool import ToolExecResult
|
||||
from astrbot.core.astr_agent_context import AstrAgentContext
|
||||
from astrbot.core.astr_agent_context import AstrAgentContext, AstrMessageEvent
|
||||
from astrbot.core.computer.computer_client import get_booter, get_local_booter
|
||||
from astrbot.core.message.message_event_result import MessageChain
|
||||
|
||||
param_schema = {
|
||||
"type": "object",
|
||||
@@ -25,7 +26,7 @@ param_schema = {
|
||||
}
|
||||
|
||||
|
||||
def handle_result(result: dict) -> ToolExecResult:
|
||||
async def handle_result(result: dict, event: AstrMessageEvent) -> ToolExecResult:
|
||||
data = result.get("data", {})
|
||||
output = data.get("output", {})
|
||||
error = data.get("error", "")
|
||||
@@ -44,6 +45,9 @@ def handle_result(result: dict) -> ToolExecResult:
|
||||
type="image", data=img["image/png"], mimeType="image/png"
|
||||
)
|
||||
)
|
||||
|
||||
if event.get_platform_name() == "webchat":
|
||||
await event.send(message=MessageChain().base64_image(img["image/png"]))
|
||||
if text:
|
||||
resp.content.append(mcp.types.TextContent(type="text", text=text))
|
||||
|
||||
@@ -68,7 +72,7 @@ class PythonTool(FunctionTool):
|
||||
)
|
||||
try:
|
||||
result = await sb.python.exec(code, silent=silent)
|
||||
return handle_result(result)
|
||||
return await handle_result(result, context.context.event)
|
||||
except Exception as e:
|
||||
return f"Error executing code: {str(e)}"
|
||||
|
||||
@@ -89,6 +93,6 @@ class LocalPythonTool(FunctionTool):
|
||||
sb = get_local_booter()
|
||||
try:
|
||||
result = await sb.python.exec(code, silent=silent)
|
||||
return handle_result(result)
|
||||
return await handle_result(result, context.context.event)
|
||||
except Exception as e:
|
||||
return f"Error executing code: {str(e)}"
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any, TypedDict
|
||||
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
||||
|
||||
VERSION = "4.17.2"
|
||||
VERSION = "4.17.3"
|
||||
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
|
||||
|
||||
WEBHOOK_SUPPORTED_PLATFORMS = [
|
||||
|
||||
@@ -25,10 +25,14 @@ import asyncio
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from enum import Enum
|
||||
|
||||
from pydantic.v1 import BaseModel
|
||||
if sys.version_info >= (3, 14):
|
||||
from pydantic import BaseModel
|
||||
else:
|
||||
from pydantic.v1 import BaseModel
|
||||
|
||||
from astrbot.core import astrbot_config, file_token_service, logger
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_temp_path
|
||||
@@ -85,7 +89,7 @@ class BaseMessageComponent(BaseModel):
|
||||
|
||||
|
||||
class Plain(BaseMessageComponent):
|
||||
type = ComponentType.Plain
|
||||
type: ComponentType = ComponentType.Plain
|
||||
text: str
|
||||
convert: bool | None = True
|
||||
|
||||
@@ -100,7 +104,7 @@ class Plain(BaseMessageComponent):
|
||||
|
||||
|
||||
class Face(BaseMessageComponent):
|
||||
type = ComponentType.Face
|
||||
type: ComponentType = ComponentType.Face
|
||||
id: int
|
||||
|
||||
def __init__(self, **_) -> None:
|
||||
@@ -108,7 +112,7 @@ class Face(BaseMessageComponent):
|
||||
|
||||
|
||||
class Record(BaseMessageComponent):
|
||||
type = ComponentType.Record
|
||||
type: ComponentType = ComponentType.Record
|
||||
file: str | None = ""
|
||||
magic: bool | None = False
|
||||
url: str | None = ""
|
||||
@@ -215,7 +219,7 @@ class Record(BaseMessageComponent):
|
||||
|
||||
|
||||
class Video(BaseMessageComponent):
|
||||
type = ComponentType.Video
|
||||
type: ComponentType = ComponentType.Video
|
||||
file: str
|
||||
cover: str | None = ""
|
||||
c: int | None = 2
|
||||
@@ -301,7 +305,7 @@ class Video(BaseMessageComponent):
|
||||
|
||||
|
||||
class At(BaseMessageComponent):
|
||||
type = ComponentType.At
|
||||
type: ComponentType = ComponentType.At
|
||||
qq: int | str # 此处str为all时代表所有人
|
||||
name: str | None = ""
|
||||
|
||||
@@ -323,28 +327,28 @@ class AtAll(At):
|
||||
|
||||
|
||||
class RPS(BaseMessageComponent): # TODO
|
||||
type = ComponentType.RPS
|
||||
type: ComponentType = ComponentType.RPS
|
||||
|
||||
def __init__(self, **_) -> None:
|
||||
super().__init__(**_)
|
||||
|
||||
|
||||
class Dice(BaseMessageComponent): # TODO
|
||||
type = ComponentType.Dice
|
||||
type: ComponentType = ComponentType.Dice
|
||||
|
||||
def __init__(self, **_) -> None:
|
||||
super().__init__(**_)
|
||||
|
||||
|
||||
class Shake(BaseMessageComponent): # TODO
|
||||
type = ComponentType.Shake
|
||||
type: ComponentType = ComponentType.Shake
|
||||
|
||||
def __init__(self, **_) -> None:
|
||||
super().__init__(**_)
|
||||
|
||||
|
||||
class Share(BaseMessageComponent):
|
||||
type = ComponentType.Share
|
||||
type: ComponentType = ComponentType.Share
|
||||
url: str
|
||||
title: str
|
||||
content: str | None = ""
|
||||
@@ -355,7 +359,7 @@ class Share(BaseMessageComponent):
|
||||
|
||||
|
||||
class Contact(BaseMessageComponent): # TODO
|
||||
type = ComponentType.Contact
|
||||
type: ComponentType = ComponentType.Contact
|
||||
_type: str # type 字段冲突
|
||||
id: int | None = 0
|
||||
|
||||
@@ -364,7 +368,7 @@ class Contact(BaseMessageComponent): # TODO
|
||||
|
||||
|
||||
class Location(BaseMessageComponent): # TODO
|
||||
type = ComponentType.Location
|
||||
type: ComponentType = ComponentType.Location
|
||||
lat: float
|
||||
lon: float
|
||||
title: str | None = ""
|
||||
@@ -375,7 +379,7 @@ class Location(BaseMessageComponent): # TODO
|
||||
|
||||
|
||||
class Music(BaseMessageComponent):
|
||||
type = ComponentType.Music
|
||||
type: ComponentType = ComponentType.Music
|
||||
_type: str
|
||||
id: int | None = 0
|
||||
url: str | None = ""
|
||||
@@ -392,7 +396,7 @@ class Music(BaseMessageComponent):
|
||||
|
||||
|
||||
class Image(BaseMessageComponent):
|
||||
type = ComponentType.Image
|
||||
type: ComponentType = ComponentType.Image
|
||||
file: str | None = ""
|
||||
_type: str | None = ""
|
||||
subType: int | None = 0
|
||||
@@ -507,7 +511,7 @@ class Image(BaseMessageComponent):
|
||||
|
||||
|
||||
class Reply(BaseMessageComponent):
|
||||
type = ComponentType.Reply
|
||||
type: ComponentType = ComponentType.Reply
|
||||
id: str | int
|
||||
"""所引用的消息 ID"""
|
||||
chain: list["BaseMessageComponent"] | None = []
|
||||
@@ -543,7 +547,7 @@ class Poke(BaseMessageComponent):
|
||||
|
||||
|
||||
class Forward(BaseMessageComponent):
|
||||
type = ComponentType.Forward
|
||||
type: ComponentType = ComponentType.Forward
|
||||
id: str
|
||||
|
||||
def __init__(self, **_) -> None:
|
||||
@@ -553,7 +557,7 @@ class Forward(BaseMessageComponent):
|
||||
class Node(BaseMessageComponent):
|
||||
"""群合并转发消息"""
|
||||
|
||||
type = ComponentType.Node
|
||||
type: ComponentType = ComponentType.Node
|
||||
id: int | None = 0 # 忽略
|
||||
name: str | None = "" # qq昵称
|
||||
uin: str | None = "0" # qq号
|
||||
@@ -605,7 +609,7 @@ class Node(BaseMessageComponent):
|
||||
|
||||
|
||||
class Nodes(BaseMessageComponent):
|
||||
type = ComponentType.Nodes
|
||||
type: ComponentType = ComponentType.Nodes
|
||||
nodes: list[Node]
|
||||
|
||||
def __init__(self, nodes: list[Node], **_) -> None:
|
||||
@@ -631,7 +635,7 @@ class Nodes(BaseMessageComponent):
|
||||
|
||||
|
||||
class Json(BaseMessageComponent):
|
||||
type = ComponentType.Json
|
||||
type: ComponentType = ComponentType.Json
|
||||
data: dict
|
||||
|
||||
def __init__(self, data: str | dict, **_) -> None:
|
||||
@@ -641,14 +645,14 @@ class Json(BaseMessageComponent):
|
||||
|
||||
|
||||
class Unknown(BaseMessageComponent):
|
||||
type = ComponentType.Unknown
|
||||
type: ComponentType = ComponentType.Unknown
|
||||
text: str
|
||||
|
||||
|
||||
class File(BaseMessageComponent):
|
||||
"""文件消息段"""
|
||||
|
||||
type = ComponentType.File
|
||||
type: ComponentType = ComponentType.File
|
||||
name: str | None = "" # 名字
|
||||
file_: str | None = "" # 本地路径
|
||||
url: str | None = "" # url
|
||||
@@ -783,7 +787,7 @@ class File(BaseMessageComponent):
|
||||
|
||||
|
||||
class WechatEmoji(BaseMessageComponent):
|
||||
type = ComponentType.WechatEmoji
|
||||
type: ComponentType = ComponentType.WechatEmoji
|
||||
md5: str | None = ""
|
||||
md5_len: int | None = 0
|
||||
cdnurl: str | None = ""
|
||||
|
||||
@@ -513,6 +513,16 @@ class PluginManager:
|
||||
)
|
||||
logger.info(metadata)
|
||||
metadata.config = plugin_config
|
||||
p_name = (metadata.name or "unknown").lower().replace("/", "_")
|
||||
p_author = (metadata.author or "unknown").lower().replace("/", "_")
|
||||
plugin_id = f"{p_author}/{p_name}"
|
||||
|
||||
# 在实例化前注入类属性,保证插件 __init__ 可读取这些值
|
||||
if metadata.star_cls_type:
|
||||
setattr(metadata.star_cls_type, "name", p_name)
|
||||
setattr(metadata.star_cls_type, "author", p_author)
|
||||
setattr(metadata.star_cls_type, "plugin_id", plugin_id)
|
||||
|
||||
if path not in inactivated_plugins:
|
||||
# 只有没有禁用插件时才实例化插件类
|
||||
if plugin_config and metadata.star_cls_type:
|
||||
@@ -530,17 +540,10 @@ class PluginManager:
|
||||
context=self.context,
|
||||
)
|
||||
|
||||
p_name = (metadata.name or "unknown").lower().replace("/", "_")
|
||||
p_author = (
|
||||
(metadata.author or "unknown").lower().replace("/", "_")
|
||||
)
|
||||
setattr(metadata.star_cls, "name", p_name)
|
||||
setattr(metadata.star_cls, "author", p_author)
|
||||
setattr(
|
||||
metadata.star_cls,
|
||||
"plugin_id",
|
||||
f"{p_author}/{p_name}",
|
||||
)
|
||||
if metadata.star_cls:
|
||||
setattr(metadata.star_cls, "name", p_name)
|
||||
setattr(metadata.star_cls, "author", p_author)
|
||||
setattr(metadata.star_cls, "plugin_id", plugin_id)
|
||||
else:
|
||||
logger.info(f"插件 {metadata.name} 已被禁用。")
|
||||
|
||||
|
||||
@@ -148,8 +148,8 @@ class AstrBotUpdator(RepoZipUpdator):
|
||||
update_data = await self.fetch_release_info(self.ASTRBOT_RELEASE_API, latest)
|
||||
file_url = None
|
||||
|
||||
if os.environ.get("ASTRBOT_CLI"):
|
||||
raise Exception("不支持更新CLI启动的AstrBot") # 避免版本管理混乱
|
||||
if os.environ.get("ASTRBOT_CLI") or os.environ.get("ASTRBOT_LAUNCHER"):
|
||||
raise Exception("不支持更新此方式启动的AstrBot") # 避免版本管理混乱
|
||||
|
||||
if latest:
|
||||
latest_version = update_data[0]["tag_name"]
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
## What's Changed
|
||||
|
||||
### 修复
|
||||
- ‼️ 修复 Python 3.14 环境下 `'Plain' object has no attribute 'text'` 报错问题 ([#5154](https://github.com/AstrBotDevs/AstrBot/issues/5154))。
|
||||
- ‼️ 修复插件元数据处理流程:在实例化前注入必要属性,避免初始化阶段元数据缺失 ([#5155](https://github.com/AstrBotDevs/AstrBot/issues/5155))。
|
||||
- 修复桌面端后端构建中 AstrBot 内置插件运行时依赖未打包的问题 ([#5146](https://github.com/AstrBotDevs/AstrBot/issues/5146))。
|
||||
- 修复通过 AstrBot Launcher 启动时仍被检测并触发更新的问题。
|
||||
|
||||
### 优化
|
||||
|
||||
- Webchat 下,使用 `astrbot_execute_ipython` 工具如果返回了图片,会自动将图片发送到聊天中。
|
||||
|
||||
### 其他
|
||||
- 执行 `ruff format` 代码格式整理。
|
||||
|
||||
## What's Changed (EN)
|
||||
|
||||
### Fixes
|
||||
- ‼️ Fixed plugin metadata handling by injecting required attributes before instantiation to avoid missing metadata during initialization ([#5155](https://github.com/AstrBotDevs/AstrBot/issues/5155)).
|
||||
- ‼️ Fixed `'Plain' object has no attribute 'text'` error when using Python 3.14 ([#5154](https://github.com/AstrBotDevs/AstrBot/issues/5154)).
|
||||
- Fixed missing runtime dependencies for built-in plugins in desktop backend builds ([#5146](https://github.com/AstrBotDevs/AstrBot/issues/5146)).
|
||||
- Fixed update checks being triggered when AstrBot is launched via AstrBot Launcher.
|
||||
|
||||
### Improvements
|
||||
- In Webchat, when using the `astrbot_execute_ipython` tool, if an image is returned, it will automatically be sent to the chat.
|
||||
### Others
|
||||
- Applied `ruff format` code formatting.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "astrbot-desktop",
|
||||
"version": "4.17.2",
|
||||
"version": "4.17.3",
|
||||
"description": "AstrBot desktop wrapper",
|
||||
"private": true,
|
||||
"main": "main.js",
|
||||
|
||||
@@ -35,6 +35,16 @@ const args = [
|
||||
'aiosqlite',
|
||||
'--collect-all',
|
||||
'pip',
|
||||
'--collect-all',
|
||||
'bs4',
|
||||
'--collect-all',
|
||||
'readability',
|
||||
'--collect-all',
|
||||
'lxml',
|
||||
'--collect-all',
|
||||
'lxml_html_clean',
|
||||
'--collect-all',
|
||||
'rfc3987_syntax',
|
||||
'--collect-submodules',
|
||||
'astrbot.api',
|
||||
'--collect-submodules',
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "AstrBot"
|
||||
version = "4.17.2"
|
||||
version = "4.17.3"
|
||||
description = "Easy-to-use multi-platform LLM chatbot and development framework"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
Reference in New Issue
Block a user