feat: metrics 采用 Tickstats

This commit is contained in:
Soulter
2024-11-27 21:41:54 +08:00
parent b740cc467d
commit fc28f34ec6
11 changed files with 44 additions and 77 deletions
+2
View File
@@ -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()
+1 -4
View File
@@ -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):
-1
View File
@@ -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
+3 -3
View File
@@ -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)
+3 -3
View File
@@ -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):
'''
+19 -56
View File
@@ -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 = {
+3 -2
View File
@@ -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)