feat: metrics 采用 Tickstats
This commit is contained in:
@@ -12,4 +12,6 @@ from astrbot.core.utils.command_parser import CommandParser, CommandTokens
|
||||
from astrbot.core.utils.func_call import FuncCall
|
||||
from astrbot.core import html_renderer
|
||||
|
||||
from astrbot.core.plugin.config import *
|
||||
|
||||
command_parser = CommandParser()
|
||||
@@ -6,7 +6,6 @@ from core.config.astrbot_config import AstrBotConfig
|
||||
from core.message_event_handler import MessageEventHandler
|
||||
from core.plugin import PluginManager
|
||||
from core import LogBroker
|
||||
from core.utils.metrics import MetricUploader
|
||||
from core.db import BaseDatabase
|
||||
from core.updator import AstrBotUpdator
|
||||
from core import logger
|
||||
@@ -22,7 +21,6 @@ class AstrBotCoreLifecycle:
|
||||
self.event_queue.closed = False
|
||||
self.plugin_manager = PluginManager(self.astrbot_config, self.event_queue, db)
|
||||
self.message_event_handler = MessageEventHandler(self.astrbot_config, self.plugin_manager)
|
||||
self.metrics_uploader = MetricUploader(db)
|
||||
self.astrbot_updator = AstrBotUpdator(self.astrbot_config.plugin_repo_mirror)
|
||||
self.event_bus = EventBus(self.event_queue, self.message_event_handler)
|
||||
self.stop_flag = False
|
||||
@@ -35,9 +33,8 @@ class AstrBotCoreLifecycle:
|
||||
|
||||
platform_tasks = self.load_platform()
|
||||
event_bus_task = asyncio.create_task(self.event_bus.dispatch(), name="event_bus")
|
||||
metrics_uploader_task = asyncio.create_task(self.metrics_uploader.upload_metrics(), name="metrics")
|
||||
|
||||
self.curr_tasks = [event_bus_task, metrics_uploader_task, *platform_tasks]
|
||||
self.curr_tasks = [event_bus_task, *platform_tasks]
|
||||
self.start_time = int(time.time())
|
||||
|
||||
async def start(self):
|
||||
|
||||
@@ -6,7 +6,6 @@ from .platform import AstrMessageEvent
|
||||
from .config.astrbot_config import AstrBotConfig
|
||||
from .message_event_result import MessageEventResult, CommandResult, MessageChain
|
||||
from .plugin import PluginManager, Context, CommandMetadata
|
||||
from .provider import Provider
|
||||
from nakuru.entities.components import *
|
||||
from core import logger
|
||||
from core import html_renderer
|
||||
|
||||
@@ -6,6 +6,7 @@ from core.message_event_result import MessageEventResult, MessageChain
|
||||
from core.platform.message_type import MessageType
|
||||
from typing import List
|
||||
from nakuru.entities.components import BaseMessageComponent, Plain, Image
|
||||
from core.utils.metrics import Metric
|
||||
|
||||
@dataclass
|
||||
class MessageSesion:
|
||||
@@ -134,9 +135,8 @@ class AstrMessageEvent(abc.ABC):
|
||||
'''
|
||||
return self.is_wake
|
||||
|
||||
@abc.abstractmethod
|
||||
async def send(self, message: MessageChain):
|
||||
'''
|
||||
发送消息。
|
||||
发送消息到消息平台。
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
await Metric.upload(msg_event_tick = 1, adapter_name = self.platform_meta.name)
|
||||
@@ -5,6 +5,7 @@ from .platform_metadata import PlatformMetadata
|
||||
from .astr_message_event import AstrMessageEvent
|
||||
from core.message_event_result import MessageChain
|
||||
from .astr_message_event import MessageSesion
|
||||
from core.utils.metrics import Metric
|
||||
|
||||
class Platform(abc.ABC):
|
||||
def __init__(self, event_queue: Queue):
|
||||
@@ -26,14 +27,13 @@ class Platform(abc.ABC):
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
async def send_by_session(self, session: MessageSesion, message_chain: MessageChain) -> Awaitable[Any]:
|
||||
'''
|
||||
通过会话发送消息。该方法旨在让插件能够直接通过**可持久化的会话数据**发送消息,而不需要保存 event 对象。
|
||||
|
||||
异步方法。
|
||||
'''
|
||||
raise NotImplementedError
|
||||
'''
|
||||
await Metric.upload(msg_event_tick = 1, adapter_name = self.meta().name)
|
||||
|
||||
def commit_event(self, event: AstrMessageEvent):
|
||||
'''
|
||||
|
||||
@@ -1,66 +1,29 @@
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import json
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from core.db import BaseDatabase
|
||||
from collections import defaultdict
|
||||
from core.config import VERSION
|
||||
|
||||
logger = logging.getLogger("astrbot")
|
||||
|
||||
class MetricUploader():
|
||||
def __init__(self, db_helper: BaseDatabase) -> None:
|
||||
self.platform_stats = {}
|
||||
self.llm_stats = defaultdict(int)
|
||||
self.command_stats = defaultdict(int)
|
||||
self.db_helper = db_helper
|
||||
|
||||
async def upload_metrics(self):
|
||||
class Metric():
|
||||
@staticmethod
|
||||
async def upload(**kwargs):
|
||||
'''
|
||||
上传相关非敏感的指标以更好地了解 AstrBot 的使用情况。上传的指标不会包含任何有关消息文本、用户信息等敏感信息。
|
||||
|
||||
这些数据包含:
|
||||
- AstrBot 版本
|
||||
- OS 版本
|
||||
- 平台消息数量
|
||||
- LLM 模型名称、调用次数
|
||||
|
||||
Powered by TickStats.
|
||||
'''
|
||||
await asyncio.sleep(30)
|
||||
while True:
|
||||
res = {
|
||||
"stat_version": "moon",
|
||||
"version": VERSION, # 版本号
|
||||
"platform_stats": self.platform_stats, # 过去 30 分钟各消息平台交互消息数
|
||||
"llm_stats": self.llm_stats,
|
||||
"command_stats": self.command_stats,
|
||||
"sys": sys.platform, # 系统版本
|
||||
"plugin_stats": None,
|
||||
}
|
||||
|
||||
try:
|
||||
self.db_helper.insert_base_metrics(res)
|
||||
except BaseException as e:
|
||||
logger.debug("指标数据保存到数据库失败: " + str(e))
|
||||
await asyncio.sleep(30*60)
|
||||
continue
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post('https://api.soulter.top/upload', data=json.dumps(res), timeout=10) as _:
|
||||
pass
|
||||
except BaseException as e:
|
||||
pass
|
||||
|
||||
self.clear()
|
||||
await asyncio.sleep(30*60)
|
||||
|
||||
def increment_platform_stat(self, platform_name: str):
|
||||
self.platform_stats[platform_name] = self.platform_stats.get(
|
||||
platform_name, 0) + 1
|
||||
|
||||
def clear(self):
|
||||
self.platform_stats.clear()
|
||||
self.llm_stats.clear()
|
||||
self.command_stats.clear()
|
||||
base_url = "https://tickstats.soulter.top/api/metric/90a6c2a1"
|
||||
kwargs["v"] = VERSION
|
||||
kwargs["os"] = sys.platform
|
||||
payload = {
|
||||
"metrics_data": kwargs
|
||||
}
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
logger.debug(f"Uploading metric: {kwargs}")
|
||||
async with session.post(base_url, json=payload, timeout=3) as response:
|
||||
if response.status != 200:
|
||||
logger.error(f"Failed to upload metric: {response.status} {response.reason}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to upload metric: {e}")
|
||||
@@ -34,4 +34,5 @@ class AiocqhttpMessageEvent(AstrMessageEvent):
|
||||
ret = await AiocqhttpMessageEvent._parse_onebot_josn(message)
|
||||
if os.environ.get('TEST_MODE', 'off') == 'on':
|
||||
return
|
||||
await self.bot.send(self.message_obj.raw_message, ret)
|
||||
await self.bot.send(self.message_obj.raw_message, ret)
|
||||
await super().send(message)
|
||||
@@ -36,10 +36,11 @@ class AiocqhttpAdapter(Platform):
|
||||
# 独立会话
|
||||
_, group_id = session.session_id.split("_")
|
||||
await self.bot.send_group_msg(group_id=group_id, message=ret)
|
||||
return
|
||||
await self.bot.send_group_msg(group_id=session.session_id, message=ret)
|
||||
else:
|
||||
await self.bot.send_group_msg(group_id=session.session_id, message=ret)
|
||||
case MessageType.FRIEND_MESSAGE.value:
|
||||
await self.bot.send_private_msg(user_id=session.session_id, message=ret)
|
||||
await super().send_by_session(session, message_chain)
|
||||
|
||||
def convert_message(self, event: Event) -> AstrBotMessage:
|
||||
abm = AstrBotMessage()
|
||||
|
||||
@@ -45,6 +45,7 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
payload['file_image'] = image_path
|
||||
await self.bot.api.post_dms(guild_id=source.guild_id, **payload)
|
||||
|
||||
await super().send(message)
|
||||
|
||||
async def upload_group_and_c2c_image(self, image_base64: str, file_type: int, **kwargs) -> botpy.types.message.Media:
|
||||
payload = {
|
||||
|
||||
@@ -10,6 +10,7 @@ from openai._exceptions import *
|
||||
from openai.types.chat.chat_completion_message_tool_call import Function
|
||||
from astrbot.api import command_parser
|
||||
from .web_searcher import search_from_bing, fetch_website_content
|
||||
from astrbot.core.utils.metrics import Metric
|
||||
|
||||
class Main:
|
||||
def __init__(self, context: Context) -> None:
|
||||
@@ -128,7 +129,7 @@ class Main:
|
||||
session_id=event.session_id,
|
||||
tools=self.context.llm_tools.get_func()
|
||||
)
|
||||
# self.context.metrics_uploader.llm_stats[provider.get_curr_model()] += 1
|
||||
await Metric.upload(llm_tick=1, llm_name=self.provider.get_model(), llm_api_base=self.provider.base_url)
|
||||
|
||||
if isinstance(llm_result, Function):
|
||||
logger.debug(f"function-calling: {llm_result}")
|
||||
@@ -176,7 +177,7 @@ class Main:
|
||||
session_id=event.session_id,
|
||||
image_url=image_url
|
||||
)
|
||||
# self.context.metrics_uploader.llm_stats[provider.get_curr_model()] += 1
|
||||
await Metric.upload(llm_tick=1, llm_name=self.provider.get_model(), llm_api_base=self.provider.base_url)
|
||||
except BadRequestError as e:
|
||||
if tool_use_flag:
|
||||
# seems like the model don't support function-calling
|
||||
|
||||
@@ -84,23 +84,23 @@ class ProviderOpenAIOfficial(Provider):
|
||||
finally:
|
||||
time.sleep(10*60)
|
||||
|
||||
def personality_set(self, default_personality: dict, session_id: str):
|
||||
if not default_personality: return
|
||||
def personality_set(self, personality: dict, session_id: str):
|
||||
if not personality or not personality['prompt']: return
|
||||
if session_id not in self.session_memory:
|
||||
self.session_memory[session_id] = []
|
||||
self.curr_personality = default_personality
|
||||
self.curr_personality = personality
|
||||
self.session_personality = {} # 重置
|
||||
|
||||
new_record = {
|
||||
"user": {
|
||||
"role": "system",
|
||||
"content": default_personality['prompt'],
|
||||
"content": personality['prompt'],
|
||||
},
|
||||
'usage_tokens': 0, # 到该条目的总 token 数
|
||||
'single-tokens': 0 # 该条目的 token 数
|
||||
}
|
||||
|
||||
self.session_memory[session_id].append(new_record)
|
||||
self.session_memory[session_id] = [new_record]
|
||||
|
||||
async def encode_image_bs64(self, image_url: str) -> str:
|
||||
'''
|
||||
@@ -238,6 +238,8 @@ class ProviderOpenAIOfficial(Provider):
|
||||
|
||||
# 获取上下文,openai 格式
|
||||
contexts = await self.retrieve_context(session_id)
|
||||
|
||||
logger.debug(f"OpenAI 请求上下文:{contexts}")
|
||||
|
||||
conf = asdict(self.llm_config.model_config)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user