mirror of https://github.com/Mai-with-u/MaiBot.git
数据库微调
parent
54a760b559
commit
1e3dfb9ff1
|
|
@ -1,27 +1,133 @@
|
||||||
import os
|
|
||||||
from peewee import SqliteDatabase
|
|
||||||
from rich.traceback import install
|
from rich.traceback import install
|
||||||
|
from pathlib import Path
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from sqlalchemy import create_engine, event
|
||||||
|
from sqlalchemy.engine import Engine
|
||||||
|
from sqlalchemy.orm import Session, sessionmaker
|
||||||
|
from typing import TYPE_CHECKING, Generator
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from sqlite3 import Connection as SQLite3Connection
|
||||||
|
|
||||||
install(extra_lines=3)
|
install(extra_lines=3)
|
||||||
|
|
||||||
|
|
||||||
# 定义数据库文件路径
|
# 定义数据库文件路径
|
||||||
ROOT_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
|
ROOT_PATH = Path(__file__).parent.parent.parent.parent.absolute().resolve()
|
||||||
_DB_DIR = os.path.join(ROOT_PATH, "data")
|
_DB_DIR = ROOT_PATH / "data"
|
||||||
_DB_FILE = os.path.join(_DB_DIR, "MaiBot.db")
|
_DB_FILE = _DB_DIR / "MaiBot.db"
|
||||||
|
|
||||||
# 确保数据库目录存在
|
# 确保数据库目录存在
|
||||||
os.makedirs(_DB_DIR, exist_ok=True)
|
_DB_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
DATABASE_URL = f"sqlite:///{_DB_FILE}"
|
||||||
|
|
||||||
# 全局 Peewee SQLite 数据库访问点
|
|
||||||
db = SqliteDatabase(
|
@event.listens_for(Engine, "connect")
|
||||||
_DB_FILE,
|
def set_sqlite_pragma(dbapi_connection: "SQLite3Connection", connection_record):
|
||||||
pragmas={
|
"""
|
||||||
"journal_mode": "wal", # WAL模式提高并发性能
|
为每个新的数据库连接设置 SQLite PRAGMA。
|
||||||
"cache_size": -64 * 1000, # 64MB缓存
|
|
||||||
"foreign_keys": 1,
|
这些设置优化了并发性能和数据安全性:
|
||||||
"ignore_check_constraints": 0,
|
- journal_mode=WAL: 启用预写式日志,提高并发性能
|
||||||
"synchronous": 0, # 异步写入提高性能
|
- cache_size: 设置缓存大小为 64MB
|
||||||
"busy_timeout": 1000, # 1秒超时而不是3秒
|
- foreign_keys: 启用外键约束
|
||||||
},
|
- synchronous=NORMAL: 平衡性能和数据安全
|
||||||
|
- busy_timeout: 设置1秒超时,避免锁定冲突
|
||||||
|
"""
|
||||||
|
cursor = dbapi_connection.cursor()
|
||||||
|
cursor.execute("PRAGMA journal_mode=WAL")
|
||||||
|
cursor.execute("PRAGMA cache_size=-64000") # 负值表示KB,64000KB = 64MB
|
||||||
|
cursor.execute("PRAGMA foreign_keys=ON")
|
||||||
|
cursor.execute("PRAGMA synchronous=NORMAL") # NORMAL 模式在WAL下是安全的
|
||||||
|
cursor.execute("PRAGMA busy_timeout=1000") # 1秒超时
|
||||||
|
cursor.close()
|
||||||
|
|
||||||
|
|
||||||
|
# 连接数据库
|
||||||
|
engine = create_engine(
|
||||||
|
DATABASE_URL,
|
||||||
|
echo=False,
|
||||||
|
connect_args={"check_same_thread": False},
|
||||||
|
pool_pre_ping=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 创建会话工厂
|
||||||
|
SessionLocal = sessionmaker(
|
||||||
|
autocommit=False,
|
||||||
|
autoflush=False,
|
||||||
|
bind=engine,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def get_db_session(auto_commit: bool = True) -> Generator[Session, None, None]:
|
||||||
|
"""
|
||||||
|
获取数据库会话的上下文管理器 (推荐使用,自动提交)。
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
----
|
||||||
|
.. code-block:: python
|
||||||
|
# 方式1: 自动提交 (推荐 - 默认行为)
|
||||||
|
with get_db_session() as session:
|
||||||
|
user = User(name="张三", age=25)
|
||||||
|
session.add(user)
|
||||||
|
# 退出时自动 commit,无需手动调用
|
||||||
|
|
||||||
|
# 方式2: 手动控制事务 (高级用法)
|
||||||
|
with get_db_session(auto_commit=False) as session:
|
||||||
|
user1 = User(name="张三", age=25)
|
||||||
|
user2 = User(name="李四", age=30)
|
||||||
|
session.add_all([user1, user2])
|
||||||
|
session.commit() # 手动提交
|
||||||
|
|
||||||
|
Args:
|
||||||
|
auto_commit (bool): 是否在退出上下文时自动提交(默认: True)。
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
Session: SQLAlchemy 数据库会话
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- 会话会在退出上下文时自动关闭
|
||||||
|
- 如果发生异常,会自动回滚事务
|
||||||
|
- auto_commit=True 时,成功执行完会自动提交
|
||||||
|
- auto_commit=False 时,需要手动调用 session.commit()
|
||||||
|
"""
|
||||||
|
session = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield session
|
||||||
|
# 如果启用自动提交且没有异常,则提交事务
|
||||||
|
if auto_commit:
|
||||||
|
session.commit()
|
||||||
|
except Exception:
|
||||||
|
session.rollback()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
|
||||||
|
def get_db_session_manual():
|
||||||
|
"""获取数据库会话的上下文管理器 (手动提交模式)。"""
|
||||||
|
return get_db_session(auto_commit=False)
|
||||||
|
|
||||||
|
|
||||||
|
def get_db() -> Generator[Session, None, None]:
|
||||||
|
"""
|
||||||
|
获取数据库会话的生成器函数。
|
||||||
|
|
||||||
|
适用于依赖注入场景(如 FastAPI)。
|
||||||
|
|
||||||
|
使用示例 (FastAPI):
|
||||||
|
----
|
||||||
|
.. code-block:: python
|
||||||
|
@app.get("/users/{user_id}")
|
||||||
|
def read_user(user_id: int, db: Session = Depends(get_db)):
|
||||||
|
return db.get(User, user_id)
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
Session: SQLAlchemy 数据库会话
|
||||||
|
"""
|
||||||
|
session = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield session
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
|
||||||
|
|
@ -1,246 +0,0 @@
|
||||||
from typing import Optional
|
|
||||||
from pydantic import BaseModel
|
|
||||||
from datetime import datetime
|
|
||||||
from .database_model import ModelUser, ImageType
|
|
||||||
|
|
||||||
|
|
||||||
class MaiMessage(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
message_id: str
|
|
||||||
"""消息id"""
|
|
||||||
time: float
|
|
||||||
"""消息时间,单位为秒"""
|
|
||||||
platform: str
|
|
||||||
"""顶层平台字段"""
|
|
||||||
user_id: str
|
|
||||||
"""发送者用户id"""
|
|
||||||
user_nickname: str
|
|
||||||
"""发送者昵称"""
|
|
||||||
user_cardname: Optional[str] = None
|
|
||||||
"""发送者备注名"""
|
|
||||||
user_platform: Optional[str] = None
|
|
||||||
"""发送者平台"""
|
|
||||||
group_id: Optional[str] = None
|
|
||||||
"""群组id"""
|
|
||||||
group_name: Optional[str] = None
|
|
||||||
"""群组名称"""
|
|
||||||
group_platform: Optional[str] = None
|
|
||||||
"""群组平台"""
|
|
||||||
is_mentioned: bool = False
|
|
||||||
"""被提及"""
|
|
||||||
is_at: bool = False
|
|
||||||
"""被at"""
|
|
||||||
session_id: str
|
|
||||||
"""聊天会话id"""
|
|
||||||
reply_to: Optional[str] = None
|
|
||||||
"""回复的消息id"""
|
|
||||||
is_emoji: bool = False
|
|
||||||
"""是否为表情包消息"""
|
|
||||||
is_picture: bool = False
|
|
||||||
"""是否为图片消息"""
|
|
||||||
is_command: bool = False
|
|
||||||
"""是否为命令"""
|
|
||||||
is_notify: bool = False
|
|
||||||
"""是否为通知消息"""
|
|
||||||
raw_content: str
|
|
||||||
"""base64编码的原始消息内容"""
|
|
||||||
processed_plain_text: str
|
|
||||||
"""平面化处理后的纯文本消息"""
|
|
||||||
display_message: str
|
|
||||||
"""显示的消息内容(被放入Prompt)"""
|
|
||||||
additional_config: Optional[str] = None
|
|
||||||
"""额外配置,JSON格式存储"""
|
|
||||||
|
|
||||||
|
|
||||||
class ModelUsage(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
model_name: str
|
|
||||||
"""模型实际名称(供应商名称)"""
|
|
||||||
model_assign_name: Optional[str] = None
|
|
||||||
"""模型分配名称(用户自定义名称)"""
|
|
||||||
model_api_provider_name: str
|
|
||||||
"""模型API供应商名称"""
|
|
||||||
endpoint: Optional[str] = None
|
|
||||||
"""模型API的具体endpoint"""
|
|
||||||
user_type: ModelUser = ModelUser.SYSTEM
|
|
||||||
"""模型使用者类型"""
|
|
||||||
request_type: str
|
|
||||||
"""内部请求类型,记录哪种模块使用了此模型"""
|
|
||||||
time_cost: float
|
|
||||||
"""本次请求耗时,单位秒"""
|
|
||||||
timestamp: datetime
|
|
||||||
"""请求时间戳"""
|
|
||||||
prompt_tokens: int
|
|
||||||
"""提示词令牌数"""
|
|
||||||
completion_tokens: int
|
|
||||||
"""完成词令牌数"""
|
|
||||||
total_tokens: int
|
|
||||||
"""总令牌数"""
|
|
||||||
cost: float
|
|
||||||
"""本次请求的费用,单位元"""
|
|
||||||
|
|
||||||
|
|
||||||
class Images(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
image_hash: str = ""
|
|
||||||
"""图片哈希,使用sha256哈希值,亦作为图片唯一ID"""
|
|
||||||
description: str
|
|
||||||
"""图片的描述"""
|
|
||||||
full_path: str
|
|
||||||
"""文件的完整路径 (包括文件名)"""
|
|
||||||
image_type: ImageType = ImageType.EMOJI
|
|
||||||
emotion: Optional[str] = None
|
|
||||||
"""表情包的情感标签,逗号分隔"""
|
|
||||||
query_count: int = 0
|
|
||||||
"""被查询次数"""
|
|
||||||
is_registered: bool = False
|
|
||||||
"""是否已经注册"""
|
|
||||||
is_banned: bool = False
|
|
||||||
"""被手动禁用"""
|
|
||||||
record_time: datetime
|
|
||||||
"""记录时间(被创建的时间)"""
|
|
||||||
register_time: Optional[datetime] = None
|
|
||||||
"""注册时间(被注册为可用表情包的时间)"""
|
|
||||||
vlm_processed: bool = False
|
|
||||||
"""是否已经过VLM处理"""
|
|
||||||
|
|
||||||
|
|
||||||
class ActionRecord(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
action_id: str
|
|
||||||
"""动作ID"""
|
|
||||||
timestamp: datetime
|
|
||||||
"""记录时间戳"""
|
|
||||||
session_id: str
|
|
||||||
"""对应的 ChatSession session_id"""
|
|
||||||
action_name: str
|
|
||||||
"""动作名称"""
|
|
||||||
action_reasoning: Optional[str] = None
|
|
||||||
"""动作推理过程"""
|
|
||||||
action_data: Optional[str] = None
|
|
||||||
"""动作数据,JSON格式存储"""
|
|
||||||
action_builtin_prompt: Optional[str] = None
|
|
||||||
"""内置动作提示"""
|
|
||||||
action_display_prompt: Optional[str] = None
|
|
||||||
"""最终输入到Prompt的内容"""
|
|
||||||
|
|
||||||
|
|
||||||
class CommandRecord(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
timestamp: datetime
|
|
||||||
"""记录时间戳"""
|
|
||||||
session_id: str
|
|
||||||
"""对应的 ChatSession session_id"""
|
|
||||||
command_name: str
|
|
||||||
"""命令名称"""
|
|
||||||
command_data: Optional[str] = None
|
|
||||||
"""命令数据,JSON格式存储"""
|
|
||||||
command_result: Optional[str] = None
|
|
||||||
"""命令执行结果"""
|
|
||||||
|
|
||||||
|
|
||||||
class OnlineTime(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
timestamp: datetime
|
|
||||||
"""时间戳"""
|
|
||||||
duration_minutes: int
|
|
||||||
"""时长,单位秒"""
|
|
||||||
start_timestamp: datetime
|
|
||||||
"""上线时间"""
|
|
||||||
end_timestamp: datetime
|
|
||||||
"""下线时间"""
|
|
||||||
|
|
||||||
|
|
||||||
class Expression(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
situation: str
|
|
||||||
"""情景"""
|
|
||||||
style: str
|
|
||||||
"""风格"""
|
|
||||||
context: str
|
|
||||||
"""上下文"""
|
|
||||||
up_content: str
|
|
||||||
content_list: str
|
|
||||||
"""内容列表,JSON格式存储"""
|
|
||||||
count: int = 0
|
|
||||||
"""使用次数"""
|
|
||||||
last_active_time: datetime
|
|
||||||
"""上次使用时间"""
|
|
||||||
create_time: datetime
|
|
||||||
"""创建时间"""
|
|
||||||
session_id: Optional[str] = None
|
|
||||||
"""会话ID,区分是否为全局表达方式"""
|
|
||||||
|
|
||||||
|
|
||||||
class Jargon(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
content: str
|
|
||||||
"""黑话内容"""
|
|
||||||
raw_content: Optional[str] = None
|
|
||||||
"""原始内容,未处理的黑话内容"""
|
|
||||||
meaning: str
|
|
||||||
"""黑话含义"""
|
|
||||||
session_id: Optional[str] = None
|
|
||||||
"""会话ID,区分是否为全局黑话"""
|
|
||||||
count: int = 0
|
|
||||||
"""使用次数"""
|
|
||||||
is_jargon: Optional[bool] = True
|
|
||||||
"""是否为黑话,False表示为白话"""
|
|
||||||
is_complete: bool = False
|
|
||||||
"""是否为已经完成全部推断(count > 100后不再推断)"""
|
|
||||||
inference_with_context: Optional[str] = None
|
|
||||||
"""带上下文的推断结果,JSON格式"""
|
|
||||||
inference_with_content_only: Optional[str] = None
|
|
||||||
"""只基于词条的推断结果,JSON格式"""
|
|
||||||
|
|
||||||
|
|
||||||
class ChatHistory(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
session_id: str
|
|
||||||
"""聊天会话ID"""
|
|
||||||
start_timestamp: datetime
|
|
||||||
"""聊天开始时间"""
|
|
||||||
end_timestamp: datetime
|
|
||||||
"""聊天结束时间"""
|
|
||||||
query_count: int = 0
|
|
||||||
"""被检索次数"""
|
|
||||||
query_forget_count: int = 0
|
|
||||||
"""被遗忘检查的次数"""
|
|
||||||
original_messages: str
|
|
||||||
"""对话原文"""
|
|
||||||
participants: str
|
|
||||||
"""参与者列表,JSON格式存储"""
|
|
||||||
theme: str
|
|
||||||
"""对话主题:这段对话的主要内容,一个简短的标题"""
|
|
||||||
keywords: str
|
|
||||||
"""关键词:这段对话的关键词,JSON格式存储"""
|
|
||||||
summary: str
|
|
||||||
"""概括:对这段话的平文本概括"""
|
|
||||||
|
|
||||||
|
|
||||||
class ThinkingQuestion(BaseModel):
|
|
||||||
id: Optional[int] = None
|
|
||||||
"""自增主键"""
|
|
||||||
question: str
|
|
||||||
"""问题内容"""
|
|
||||||
context: Optional[str] = None
|
|
||||||
"""上下文"""
|
|
||||||
found_answer: bool = False
|
|
||||||
"""是否找到答案"""
|
|
||||||
answer: Optional[str] = None
|
|
||||||
"""问题答案"""
|
|
||||||
thinking_steps: Optional[str] = None
|
|
||||||
"""思考步骤,JSON格式存储"""
|
|
||||||
created_timestamp: datetime
|
|
||||||
"""创建时间"""
|
|
||||||
updated_timestamp: datetime
|
|
||||||
"""最后更新时间"""
|
|
||||||
|
|
@ -77,7 +77,7 @@ class ModelUsage(SQLModel, table=True):
|
||||||
cost: float # 本次请求的费用,单位元
|
cost: float # 本次请求的费用,单位元
|
||||||
|
|
||||||
|
|
||||||
class Images(SQLModel, table=True):
|
class Image(SQLModel, table=True):
|
||||||
"""用于同时存储表情包和图片的数据库模型。"""
|
"""用于同时存储表情包和图片的数据库模型。"""
|
||||||
|
|
||||||
__tablename__ = "images" # type: ignore
|
__tablename__ = "images" # type: ignore
|
||||||
|
|
@ -98,6 +98,7 @@ class Images(SQLModel, table=True):
|
||||||
|
|
||||||
record_time: datetime = Field(default_factory=datetime.now, index=True) # 记录时间(被创建的时间)
|
record_time: datetime = Field(default_factory=datetime.now, index=True) # 记录时间(被创建的时间)
|
||||||
register_time: Optional[datetime] = Field(default=None, nullable=True) # 注册时间(被注册为可用表情包的时间)
|
register_time: Optional[datetime] = Field(default=None, nullable=True) # 注册时间(被注册为可用表情包的时间)
|
||||||
|
last_used_time: Optional[datetime] = Field(default=None, nullable=True) # 上次使用时间
|
||||||
|
|
||||||
vlm_processed: bool = Field(default=False) # 是否已经过VLM处理
|
vlm_processed: bool = Field(default=False) # 是否已经过VLM处理
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue